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