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