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