]> git.stg.codes - stg.git/blob - projects/stargazer/plugins/other/ping/ping.cpp
More subscriptions, less notifiers.
[stg.git] / projects / stargazer / plugins / other / ping / ping.cpp
1 #include "ping.h"
2
3 #include "stg/user.h"
4 #include "stg/locker.h"
5 #include "stg/user_property.h"
6
7 #include <cstdio>
8 #include <cassert>
9 #include <csignal>
10 #include <ctime>
11 #include <algorithm>
12
13 namespace
14 {
15 //-----------------------------------------------------------------------------
16 //-----------------------------------------------------------------------------
17 //-----------------------------------------------------------------------------
18 template <typename varType>
19 class HAS_USER: public std::binary_function<varType, UserPtr, bool>
20 {
21 public:
22     explicit HAS_USER(const UserPtr & u) : user(u) {}
23     bool operator()(varType notifier) const
24         {
25         return notifier.GetUser() == user;
26         }
27 private:
28     const UserPtr & user;
29 };
30 }
31
32 extern "C" STG::Plugin* GetPlugin()
33 {
34     static PING plugin;
35     return &plugin;
36 }
37 //-----------------------------------------------------------------------------
38 //-----------------------------------------------------------------------------
39 //-----------------------------------------------------------------------------
40 int PING_SETTINGS::ParseSettings(const STG::ModuleSettings & s)
41 {
42 STG::ParamValue pv;
43 std::vector<STG::ParamValue>::const_iterator pvi;
44
45 pv.param = "PingDelay";
46 pvi = std::find(s.moduleParams.begin(), s.moduleParams.end(), pv);
47 if (pvi == s.moduleParams.end() || pvi->value.empty())
48     {
49     errorStr = "Parameter \'PingDelay\' not found.";
50     printfd(__FILE__, "Parameter 'PingDelay' not found\n");
51     return -1;
52     }
53 if (ParseIntInRange(pvi->value[0], 5, 3600, &pingDelay) != 0)
54     {
55     errorStr = "Cannot parse parameter \'PingDelay\': " + errorStr;
56     printfd(__FILE__, "Canot parse parameter 'PingDelay'\n");
57     return -1;
58     }
59
60 return 0;
61 }
62 //-----------------------------------------------------------------------------
63 PING::PING()
64     : users(nullptr),
65       isRunning(false),
66       logger(STG::PluginLogger::get("ping"))
67 {
68 }
69 //-----------------------------------------------------------------------------
70 int PING::ParseSettings()
71 {
72 auto ret = pingSettings.ParseSettings(settings);
73 if (ret != 0)
74     errorStr = pingSettings.GetStrError();
75 return ret;
76 }
77 //-----------------------------------------------------------------------------
78 int PING::Start()
79 {
80 GetUsers();
81
82 m_onAddUserConn = users->onAdd([this](auto user){ AddUser(user); });
83 m_onDelUserConn = users->onDel([this](auto user){ DelUser(user); });
84
85 pinger.SetDelayTime(pingSettings.GetPingDelay());
86 pinger.Start();
87
88 m_thread = std::jthread([this](auto token){ Run(std::move(token)); });
89
90 return 0;
91 }
92 //-----------------------------------------------------------------------------
93 int PING::Stop()
94 {
95 std::lock_guard lock(m_mutex);
96
97 if (!m_thread.joinable())
98     return 0;
99
100 pinger.Stop();
101 m_thread.request_stop();
102 //5 seconds to thread stops itself
103 struct timespec ts = {0, 200000000};
104 for (int i = 0; i < 25; i++)
105     {
106     if (!isRunning)
107         break;
108
109     nanosleep(&ts, nullptr);
110     }
111
112 m_onAddUserConn.disconnect();
113 m_onDelUserConn.disconnect();
114
115 std::list<UserPtr>::iterator users_iter;
116 users_iter = usersList.begin();
117 while (users_iter != usersList.end())
118     {
119     UnSetUserNotifiers(*users_iter);
120     ++users_iter;
121     }
122
123 if (isRunning)
124     m_thread.detach();
125 else
126     m_thread.join();
127
128 return 0;
129 }
130 //-----------------------------------------------------------------------------
131 bool PING::IsRunning()
132 {
133 return isRunning;
134 }
135 //-----------------------------------------------------------------------------
136 void PING::Run(std::stop_token token)
137 {
138 sigset_t signalSet;
139 sigfillset(&signalSet);
140 pthread_sigmask(SIG_BLOCK, &signalSet, nullptr);
141
142 isRunning = true;
143
144 long delay = (10000000 * pingSettings.GetPingDelay()) / 3 + 50000000;
145
146 while (!token.stop_requested())
147     {
148     auto iter = usersList.begin();
149         {
150         std::lock_guard lock(m_mutex);
151         while (iter != usersList.end())
152             {
153             if ((*iter)->GetProperties().ips.ConstData().onlyOneIP())
154                 {
155                 uint32_t ip = (*iter)->GetProperties().ips.ConstData()[0].ip;
156                 time_t t;
157                 if (pinger.GetIPTime(ip, &t) == 0)
158                     {
159                     if (t != 0)
160                         (*iter)->UpdatePingTime(t);
161                     }
162                 }
163             else
164                 {
165                 uint32_t ip = (*iter)->GetCurrIP();
166                 if (ip != 0)
167                     {
168                     time_t t;
169                     if (pinger.GetIPTime(ip, &t) == 0)
170                         {
171                         if (t != 0)
172                             (*iter)->UpdatePingTime(t);
173                         }
174                     }
175                 }
176             ++iter;
177             }
178         }
179     struct timespec ts = {delay / 1000000000, delay % 1000000000};
180     for (int i = 0; i < 100; i++)
181         {
182         if (!token.stop_requested())
183             {
184             nanosleep(&ts, nullptr);
185             }
186         }
187     }
188
189 isRunning = false;
190 }
191 //-----------------------------------------------------------------------------
192 void PING::SetUserNotifiers(UserPtr u)
193 {
194 CHG_CURRIP_NOTIFIER_PING ChgCurrIPNotifier(*this, u);
195 CHG_IPS_NOTIFIER_PING ChgIPNotifier(*this, u);
196
197 ChgCurrIPNotifierList.push_front(ChgCurrIPNotifier);
198 ChgIPNotifierList.push_front(ChgIPNotifier);
199
200 u->AddCurrIPAfterNotifier(&(*ChgCurrIPNotifierList.begin()));
201 u->GetProperties().ips.AddAfterNotifier(&(*ChgIPNotifierList.begin()));
202 }
203 //-----------------------------------------------------------------------------
204 void PING::UnSetUserNotifiers(UserPtr u)
205 {
206 // ---          CurrIP              ---
207 HAS_USER<CHG_CURRIP_NOTIFIER_PING> IsContainsUserCurrIP(u);
208 HAS_USER<CHG_IPS_NOTIFIER_PING> IsContainsUserIP(u);
209
210 std::list<CHG_CURRIP_NOTIFIER_PING>::iterator currIPter;
211 std::list<CHG_IPS_NOTIFIER_PING>::iterator IPIter;
212
213 currIPter = find_if(ChgCurrIPNotifierList.begin(),
214                     ChgCurrIPNotifierList.end(),
215                     IsContainsUserCurrIP);
216
217 if (currIPter != ChgCurrIPNotifierList.end())
218     {
219     currIPter->GetUser()->DelCurrIPAfterNotifier(&(*currIPter));
220     ChgCurrIPNotifierList.erase(currIPter);
221     }
222 // ---         CurrIP end          ---
223
224 // ---          IP              ---
225 IPIter = find_if(ChgIPNotifierList.begin(),
226                  ChgIPNotifierList.end(),
227                  IsContainsUserIP);
228
229 if (IPIter != ChgIPNotifierList.end())
230     {
231     IPIter->GetUser()->GetProperties().ips.DelAfterNotifier(&(*IPIter));
232     ChgIPNotifierList.erase(IPIter);
233     }
234 // ---          IP end          ---
235 }
236 //-----------------------------------------------------------------------------
237 void PING::GetUsers()
238 {
239 std::lock_guard lock(m_mutex);
240
241 UserPtr u;
242 int h = users->OpenSearch();
243 assert(h && "USERS::OpenSearch is always correct");
244
245 while (users->SearchNext(h, &u) == 0)
246     {
247     usersList.push_back(u);
248     SetUserNotifiers(u);
249     if (u->GetProperties().ips.ConstData().onlyOneIP())
250         {
251         pinger.AddIP(u->GetProperties().ips.ConstData()[0].ip);
252         }
253     else
254         {
255         uint32_t ip = u->GetCurrIP();
256         if (ip != 0)
257             pinger.AddIP(ip);
258         }
259     }
260
261 users->CloseSearch(h);
262 }
263 //-----------------------------------------------------------------------------
264 void PING::AddUser(UserPtr u)
265 {
266 std::lock_guard lock(m_mutex);
267
268 SetUserNotifiers(u);
269 usersList.push_back(u);
270 }
271 //-----------------------------------------------------------------------------
272 void PING::DelUser(UserPtr u)
273 {
274 std::lock_guard lock(m_mutex);
275
276 UnSetUserNotifiers(u);
277
278 std::list<UserPtr>::iterator users_iter;
279 users_iter = usersList.begin();
280
281 while (users_iter != usersList.end())
282     {
283     if (u == *users_iter)
284         {
285         usersList.erase(users_iter);
286         break;
287         }
288     ++users_iter;
289     }
290 }
291 //-----------------------------------------------------------------------------
292 void CHG_CURRIP_NOTIFIER_PING::notify(const uint32_t & oldIP, const uint32_t & newIP)
293 {
294 ping.pinger.DelIP(oldIP);
295 if (newIP != 0)
296     ping.pinger.AddIP(newIP);
297 }
298 //-----------------------------------------------------------------------------
299 void CHG_IPS_NOTIFIER_PING::notify(const STG::UserIPs & oldIPS, const STG::UserIPs & newIPS)
300 {
301 if (oldIPS.onlyOneIP())
302     ping.pinger.DelIP(oldIPS[0].ip);
303
304 if (newIPS.onlyOneIP())
305     ping.pinger.AddIP(newIPS[0].ip);
306 }