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