latest changes from RCP console
[public/netxms.git] / src / agent / subagents / ping / ping.cpp
CommitLineData
5039dede
AK
1/*
2** NetXMS PING subagent
0f8ec5bc 3** Copyright (C) 2004-2012 Victor Kirhenshtein
5039dede
AK
4**
5** This program is free software; you can redistribute it and/or modify
6** it under the terms of the GNU General Public License as published by
7** the Free Software Foundation; either version 2 of the License, or
8** (at your option) any later version.
9**
10** This program is distributed in the hope that it will be useful,
11** but WITHOUT ANY WARRANTY; without even the implied warranty of
12** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13** GNU General Public License for more details.
14**
15** You should have received a copy of the GNU General Public License
16** along with this program; if not, write to the Free Software
17** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18**
19** File: ping.cpp
20**
21**/
22
23#include "ping.h"
24
25
26//
27// Static data
28//
29
30static CONDITION m_hCondShutdown = INVALID_CONDITION_HANDLE;
31#ifdef _NETWARE
32static CONDITION m_hCondTerminate = INVALID_CONDITION_HANDLE;
33#endif
34static BOOL m_bShutdown = FALSE;
35static DWORD m_dwNumTargets = 0;
36static PING_TARGET *m_pTargetList = NULL;
37static DWORD m_dwTimeout = 3000; // Default timeout is 3 seconds
38static DWORD m_dwDefPacketSize = 46;
39static DWORD m_dwPollsPerMinute = 4;
40
41
42//
43// Poller thread
44//
45
e91e2e4c 46static THREAD_RESULT THREAD_CALL PollerThread(void *arg)
5039dede
AK
47{
48 QWORD qwStartTime;
0f8ec5bc 49 DWORD i, dwSum, dwLost, dwCount, dwInterval, dwElapsedTime, dwStdDev;
5039dede 50 BOOL bUnreachable;
e91e2e4c 51 PING_TARGET *target = (PING_TARGET *)arg;
5039dede
AK
52 while(!m_bShutdown)
53 {
54 bUnreachable = FALSE;
55 qwStartTime = GetCurrentTimeMs();
e91e2e4c 56 if (IcmpPing(target->dwIpAddr, 1, m_dwTimeout, &target->dwLastRTT, target->dwPacketSize) != ICMP_SUCCESS)
5039dede 57 {
e91e2e4c 58 target->dwLastRTT = 10000;
5039dede
AK
59 bUnreachable = TRUE;
60 }
61
e91e2e4c
VK
62 target->pdwHistory[target->iBufPos++] = target->dwLastRTT;
63 if (target->iBufPos == (int)m_dwPollsPerMinute)
64 target->iBufPos = 0;
5039dede 65
0f8ec5bc 66 for(i = 0, dwSum = 0, dwLost = 0, dwCount = 0, dwStdDev = 0; i < m_dwPollsPerMinute; i++)
5039dede 67 {
e91e2e4c 68 if (target->pdwHistory[i] < 10000)
5039dede 69 {
e91e2e4c 70 dwSum += target->pdwHistory[i];
0f8ec5bc
VK
71 if (target->pdwHistory[i] > 0)
72 {
73 dwStdDev += (target->dwAvgRTT - target->pdwHistory[i]) * (target->dwAvgRTT - target->pdwHistory[i]);
74 }
5039dede
AK
75 dwCount++;
76 }
77 else
78 {
79 dwLost++;
80 }
81 }
e91e2e4c
VK
82 target->dwAvgRTT = bUnreachable ? 10000 : (dwSum / dwCount);
83 target->dwPacketLoss = dwLost * 100 / m_dwPollsPerMinute;
0f8ec5bc
VK
84
85 if (dwCount > 0)
86 {
87 target->dwStdDevRTT = (DWORD)sqrt((double)dwStdDev / (double)dwCount);
88 }
89 else
90 {
91 target->dwStdDevRTT = 0;
92 }
5039dede
AK
93
94 dwElapsedTime = (DWORD)(GetCurrentTimeMs() - qwStartTime);
5039dede 95 dwInterval = 60000 / m_dwPollsPerMinute;
0f8ec5bc 96
5039dede
AK
97 if (ConditionWait(m_hCondShutdown, (dwInterval > dwElapsedTime + 1000) ? dwInterval - dwElapsedTime : 1000))
98 break;
99 }
100 return THREAD_OK;
101}
102
103
104//
105// Hanlder for immediate ping request
106//
107
108static LONG H_IcmpPing(const TCHAR *pszParam, const TCHAR *pArg, TCHAR *pValue)
109{
110 TCHAR szHostName[256], szTimeOut[32], szPacketSize[32];
111 DWORD dwAddr, dwTimeOut = m_dwTimeout, dwRTT, dwPacketSize = m_dwDefPacketSize;
112
6173bea8 113 if (!AgentGetParameterArg(pszParam, 1, szHostName, 256))
5039dede
AK
114 return SYSINFO_RC_UNSUPPORTED;
115 StrStrip(szHostName);
6173bea8 116 if (!AgentGetParameterArg(pszParam, 2, szTimeOut, 256))
5039dede
AK
117 return SYSINFO_RC_UNSUPPORTED;
118 StrStrip(szTimeOut);
6173bea8 119 if (!AgentGetParameterArg(pszParam, 3, szPacketSize, 256))
5039dede
AK
120 return SYSINFO_RC_UNSUPPORTED;
121 StrStrip(szPacketSize);
122
123 dwAddr = _t_inet_addr(szHostName);
124 if (szTimeOut[0] != 0)
125 {
126 dwTimeOut = _tcstoul(szTimeOut, NULL, 0);
127 if (dwTimeOut < 500)
128 dwTimeOut = 500;
129 if (dwTimeOut > 5000)
130 dwTimeOut = 5000;
131 }
132 if (szPacketSize[0] != 0)
133 {
134 dwPacketSize = _tcstoul(szPacketSize, NULL, 0);
135 }
136
137 if (IcmpPing(dwAddr, 1, dwTimeOut, &dwRTT, dwPacketSize) != ICMP_SUCCESS)
138 dwRTT = 10000;
139 ret_uint(pValue, dwRTT);
140 return SYSINFO_RC_SUCCESS;
141}
142
143
144//
145// Handler for poller information
146//
147
148static LONG H_PollResult(const TCHAR *pszParam, const TCHAR *pArg, TCHAR *pValue)
149{
150 TCHAR szTarget[MAX_DB_STRING];
151 DWORD i, dwIpAddr;
152 BOOL bUseName = FALSE;
153
6173bea8 154 if (!AgentGetParameterArg(pszParam, 1, szTarget, MAX_DB_STRING))
5039dede
AK
155 return SYSINFO_RC_UNSUPPORTED;
156 StrStrip(szTarget);
157
158 dwIpAddr = _t_inet_addr(szTarget);
159 if ((dwIpAddr == INADDR_ANY) || (dwIpAddr == INADDR_NONE))
160 bUseName = TRUE;
161
162 for(i = 0; i < m_dwNumTargets; i++)
163 {
164 if (bUseName)
165 {
166 if (!_tcsicmp(m_pTargetList[i].szName, szTarget))
167 break;
168 }
169 else
170 {
171 if (m_pTargetList[i].dwIpAddr == dwIpAddr)
172 break;
173 }
174 }
175
176 if (i == m_dwNumTargets)
177 return SYSINFO_RC_UNSUPPORTED; // No such target
178
179 switch(*pArg)
180 {
181 case _T('A'):
182 ret_uint(pValue, m_pTargetList[i].dwAvgRTT);
183 break;
184 case _T('L'):
185 ret_uint(pValue, m_pTargetList[i].dwLastRTT);
186 break;
187 case _T('P'):
188 ret_uint(pValue, m_pTargetList[i].dwPacketLoss);
189 break;
0f8ec5bc
VK
190 case _T('D'):
191 ret_uint(pValue, m_pTargetList[i].dwStdDevRTT);
192 break;
5039dede
AK
193 default:
194 return SYSINFO_RC_UNSUPPORTED;
195 }
196
197 return SYSINFO_RC_SUCCESS;
198}
199
200
201//
202// Handler for configured target list
203//
204
6173bea8 205static LONG H_TargetList(const TCHAR *pszParam, const TCHAR *pArg, StringList *value)
5039dede
AK
206{
207 DWORD i;
208 TCHAR szBuffer[MAX_DB_STRING + 64], szIpAddr[16];
209
210 for(i = 0; i < m_dwNumTargets; i++)
211 {
f3387429 212 _sntprintf(szBuffer, MAX_DB_STRING + 64, _T("%s %u %u %u %u %s"), IpToStr(ntohl(m_pTargetList[i].dwIpAddr), szIpAddr),
5039dede
AK
213 m_pTargetList[i].dwLastRTT, m_pTargetList[i].dwAvgRTT, m_pTargetList[i].dwPacketLoss,
214 m_pTargetList[i].dwPacketSize, m_pTargetList[i].szName);
6173bea8 215 value->add(szBuffer);
5039dede
AK
216 }
217
218 return SYSINFO_RC_SUCCESS;
219}
220
221
222//
223// Called by master agent at unload
224//
225
226static void SubagentShutdown(void)
227{
228 DWORD i;
229
230 m_bShutdown = TRUE;
231 if (m_hCondShutdown != INVALID_CONDITION_HANDLE)
232 ConditionSet(m_hCondShutdown);
233
234 for(i = 0; i < m_dwNumTargets; i++)
235 ThreadJoin(m_pTargetList[i].hThread);
236 safe_free(m_pTargetList);
237
238#ifdef _NETWARE
239 // Notify main thread that NLM can exit
240 ConditionSet(m_hCondTerminate);
241#endif
242}
243
244
245//
246// Add target from configuration file parameter
247// Parameter value should be <ip_address>:<name>:<packet_size>
248// Name and size parts are optional and can be missing
249//
250
251static BOOL AddTargetFromConfig(TCHAR *pszCfg)
252{
253 TCHAR *ptr, *pszLine, *pszName = NULL;
254 DWORD dwIpAddr, dwPacketSize = m_dwDefPacketSize;
255 BOOL bResult = FALSE;
256
257 pszLine = _tcsdup(pszCfg);
258 ptr = _tcschr(pszLine, _T(':'));
259 if (ptr != NULL)
260 {
261 *ptr = 0;
262 ptr++;
263 StrStrip(ptr);
264 pszName = ptr;
265
266 // Packet size
267 ptr = _tcschr(pszName, _T(':'));
268 if (ptr != NULL)
269 {
270 *ptr = 0;
271 ptr++;
272 StrStrip(ptr);
273 StrStrip(pszName);
274 dwPacketSize = _tcstoul(ptr, NULL, 0);
275 }
276 }
277 StrStrip(pszLine);
278
279 dwIpAddr = _t_inet_addr(pszLine);
280 if ((dwIpAddr != INADDR_ANY) && (dwIpAddr != INADDR_NONE))
281 {
282 m_pTargetList = (PING_TARGET *)realloc(m_pTargetList, sizeof(PING_TARGET) * (m_dwNumTargets + 1));
283 memset(&m_pTargetList[m_dwNumTargets], 0, sizeof(PING_TARGET));
284 m_pTargetList[m_dwNumTargets].dwIpAddr = dwIpAddr;
285 if (pszName != NULL)
286 nx_strncpy(m_pTargetList[m_dwNumTargets].szName, pszName, MAX_DB_STRING);
287 else
288 IpToStr(ntohl(dwIpAddr), m_pTargetList[m_dwNumTargets].szName);
289 m_pTargetList[m_dwNumTargets].dwPacketSize = dwPacketSize;
290 m_dwNumTargets++;
291 bResult = TRUE;
292 }
293
294 free(pszLine);
295 return bResult;
296}
297
298
299//
300// Configuration file template
301//
302
303static TCHAR *m_pszTargetList = NULL;
42a96a81 304static NX_CFG_TEMPLATE m_cfgTemplate[] =
5039dede
AK
305{
306 { _T("DefaultPacketSize"), CT_LONG, 0, 0, 0, 0, &m_dwDefPacketSize },
307 { _T("PacketRate"), CT_LONG, 0, 0, 0, 0, &m_dwPollsPerMinute },
308 { _T("Target"), CT_STRING_LIST, _T('\n'), 0, 0, 0, &m_pszTargetList },
309 { _T("Timeout"), CT_LONG, 0, 0, 0, 0, &m_dwTimeout },
310 { _T(""), CT_END_OF_LIST, 0, 0, 0, 0, NULL }
311};
312
313
314//
315// Subagent initialization
316//
317
42a96a81 318static BOOL SubagentInit(Config *config)
5039dede 319{
42a96a81
VK
320 DWORD i;
321 bool success;
5039dede 322
42a96a81 323 // Parse configuration
97a92859 324 success = config->parseTemplate(_T("Ping"), m_cfgTemplate);
42a96a81 325 if (success)
5039dede
AK
326 {
327 TCHAR *pItem, *pEnd;
328
329 if (m_dwPollsPerMinute == 0)
330 m_dwPollsPerMinute = 1;
331 if (m_dwPollsPerMinute > MAX_POLLS_PER_MINUTE)
332 m_dwPollsPerMinute = MAX_POLLS_PER_MINUTE;
333
334 // Parse target list
335 if (m_pszTargetList != NULL)
336 {
337 for(pItem = m_pszTargetList; *pItem != 0; pItem = pEnd + 1)
338 {
339 pEnd = _tcschr(pItem, _T('\n'));
340 if (pEnd != NULL)
341 *pEnd = 0;
342 StrStrip(pItem);
343 if (!AddTargetFromConfig(pItem))
6173bea8 344 AgentWriteLog(EVENTLOG_WARNING_TYPE,
5039dede
AK
345 _T("Unable to add ICMP ping target from configuration file. ")
346 _T("Original configuration record: %s"), pItem);
347 }
348 free(m_pszTargetList);
349 }
350
351 // Create shutdown condition and start poller threads
352 m_hCondShutdown = ConditionCreate(TRUE);
353 for(i = 0; i < m_dwNumTargets; i++)
354 m_pTargetList[i].hThread = ThreadCreateEx(PollerThread, 0, &m_pTargetList[i]);
355 }
356 else
357 {
358 safe_free(m_pszTargetList);
359 }
360
42a96a81 361 return success;
5039dede
AK
362}
363
364
365//
366// Subagent information
367//
368
369static NETXMS_SUBAGENT_PARAM m_parameters[] =
370{
371 { _T("Icmp.AvgPingTime(*)"), H_PollResult, _T("A"), DCI_DT_UINT, _T("Average response time of ICMP ping to {instance} for last minute") },
372 { _T("Icmp.LastPingTime(*)"), H_PollResult, _T("L"), DCI_DT_UINT, _T("Response time of last ICMP ping to {instance}") },
373 { _T("Icmp.PacketLoss(*)"), H_PollResult, _T("P"), DCI_DT_UINT, _T("Packet loss for ICMP ping to {instance}") },
0f8ec5bc 374 { _T("Icmp.PingStdDev(*)"), H_PollResult, _T("D"), DCI_DT_UINT, _T("Standard deviation for ICMP ping to {instance}") },
5039dede
AK
375 { _T("Icmp.Ping(*)"), H_IcmpPing, NULL, DCI_DT_UINT, _T("ICMP ping response time for {instance}") }
376};
f1a20e8d 377static NETXMS_SUBAGENT_LIST m_enums[] =
5039dede
AK
378{
379 { _T("Icmp.TargetList"), H_TargetList, NULL }
380};
381
382static NETXMS_SUBAGENT_INFO m_info =
383{
384 NETXMS_SUBAGENT_INFO_MAGIC,
f3387429 385 _T("PING"), NETXMS_VERSION_STRING,
5039dede
AK
386 SubagentInit, SubagentShutdown, NULL,
387 sizeof(m_parameters) / sizeof(NETXMS_SUBAGENT_PARAM),
388 m_parameters,
f1a20e8d 389 sizeof(m_enums) / sizeof(NETXMS_SUBAGENT_LIST),
5039dede 390 m_enums,
f1a20e8d
VK
391 0, NULL, // tables
392 0, NULL, // actions
393 0, NULL // push parameters
5039dede
AK
394};
395
396
397//
398// Entry point for NetXMS agent
399//
400
401DECLARE_SUBAGENT_ENTRY_POINT(PING)
402{
403 *ppInfo = &m_info;
404 return TRUE;
405}
406
407
408//
409// DLL entry point
410//
411
412#ifdef _WIN32
413
414BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
415{
416 if (dwReason == DLL_PROCESS_ATTACH)
417 DisableThreadLibraryCalls(hInstance);
418 return TRUE;
419}
420
421#endif
422
423
424//
425// NetWare entry point
426// We use main() instead of _init() and _fini() to implement
427// automatic unload of the subagent after unload handler is called
428//
429
430#ifdef _NETWARE
431
432int main(int argc, char *argv[])
433{
434 m_hCondTerminate = ConditionCreate(TRUE);
435 ConditionWait(m_hCondTerminate, INFINITE);
436 ConditionDestroy(m_hCondTerminate);
437 sleep(1);
438 return 0;
439}
440
441#endif