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