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