1 /*****************************************************************************
2 *
3 * CHECK_PING.C
4 *
5 * Program: Ping plugin for Nagios
6 * License: GPL
7 * Copyright (c) 1999 Ethan Galstad (nagios@nagios.org)
8 *
9 * $Id$
10 *
11 *****************************************************************************/
13 const char *progname = "check_ping";
14 #define REVISION "$Revision$"
15 #define COPYRIGHT "1999-2001"
16 #define AUTHOR "Ethan Galstad/Karl DeBisschop"
17 #define EMAIL "kdebisschop@users.sourceforge.net"
18 #define SUMMARY "Use ping to check connection statistics for a remote host.\n"
20 #define OPTIONS "\
21 -H <host_address> -w <wrta>,<wpl>%% -c <crta>,<cpl>%%\n\
22 [-p packets] [-t timeout] [-L]\n"
24 #define LONGOPTIONS "\
25 -H, --hostname=HOST\n\
26 host to ping\n\
27 -w, --warning=THRESHOLD\n\
28 warning threshold pair\n\
29 -c, --critical=THRESHOLD\n\
30 critical threshold pair\n\
31 -p, --packets=INTEGER\n\
32 number of ICMP ECHO packets to send (Default: %d)\n\
33 -t, --timeout=INTEGER\n\
34 optional specified timeout in second (Default: %d)\n\
35 -L, --link\n\
36 show HTML in the plugin output (obsoleted by urlize)\n\
37 THRESHOLD is <rta>,<pl>%% where <rta> is the round trip average travel\n\
38 time (ms) which triggers a WARNING or CRITICAL state, and <pl> is the\n\
39 percentage of packet loss to trigger an alarm state.\n"
41 #define DESCRIPTION "\
42 This plugin uses the ping command to probe the specified host for packet loss\n\
43 (percentage) and round trip average (milliseconds). It can produce HTML output\n\
44 linking to a traceroute CGI contributed by Ian Cass. The CGI can be found in\n\
45 the contrib area of the downloads section at http://www.nagios.org\n\n"
47 #include "config.h"
48 #include "common.h"
49 #include "popen.h"
50 #include "utils.h"
52 #define UNKNOWN_PACKET_LOSS 200 /* 200% */
53 #define UNKNOWN_TRIP_TIME -1.0 /* -1 seconds */
54 #define DEFAULT_MAX_PACKETS 5 /* default no. of ICMP ECHO packets */
56 #define WARN_DUPLICATES "DUPLICATES FOUND! "
58 int process_arguments (int, char **);
59 int get_threshold (char *, float *, int *);
60 int validate_arguments (void);
61 int run_ping (char *, char *);
62 void print_usage (void);
63 void print_help (void);
65 int display_html = FALSE;
66 int wpl = UNKNOWN_PACKET_LOSS;
67 int cpl = UNKNOWN_PACKET_LOSS;
68 float wrta = UNKNOWN_TRIP_TIME;
69 float crta = UNKNOWN_TRIP_TIME;
70 char **addresses = NULL;
71 int n_addresses;
72 int max_addr = 1;
73 int max_packets = -1;
74 int verbose = FALSE;
76 float rta = UNKNOWN_TRIP_TIME;
77 int pl = UNKNOWN_PACKET_LOSS;
79 char *warn_text = NULL;
81 int
82 main (int argc, char **argv)
83 {
84 char *command_line = NULL;
85 int result = STATE_UNKNOWN;
86 int this_result = STATE_UNKNOWN;
87 int i;
89 addresses = malloc (max_addr);
91 if (process_arguments (argc, argv) == ERROR)
92 usage ("Could not parse arguments");
93 exit;
95 /* Set signal handling and alarm */
96 if (signal (SIGALRM, popen_timeout_alarm_handler) == SIG_ERR) {
97 printf ("Cannot catch SIGALRM");
98 return STATE_UNKNOWN;
99 }
101 /* handle timeouts gracefully */
102 alarm (timeout_interval);
104 for (i = 0 ; i < n_addresses ; i++) {
106 /* does the host address of number of packets argument come first? */
107 #ifdef PING_PACKETS_FIRST
108 asprintf (&command_line, PING_COMMAND, max_packets, addresses[i]);
109 #else
110 asprintf (&command_line, PING_COMMAND, addresses[i], max_packets);
111 #endif
113 if (verbose)
114 printf ("%s ==> ", command_line);
116 /* run the command */
117 this_result = run_ping (command_line, addresses[i]);
119 if (pl == UNKNOWN_PACKET_LOSS || rta == UNKNOWN_TRIP_TIME) {
120 printf ("%s\n", command_line);
121 terminate (STATE_UNKNOWN,
122 "Error: Could not interpret output from ping command\n");
123 }
125 if (pl >= cpl || rta >= crta || rta < 0)
126 this_result = STATE_CRITICAL;
127 else if (pl >= wpl || rta >= wrta)
128 this_result = STATE_WARNING;
129 else if (pl >= 0 && rta >= 0)
130 this_result = max_state (STATE_OK, this_result);
132 if (n_addresses > 1 && this_result != STATE_UNKNOWN)
133 terminate (STATE_OK, "%s is alive\n", addresses[i]);
135 if (display_html == TRUE)
136 printf ("<A HREF='%s/traceroute.cgi?%s'>", CGIURL, addresses[i]);
137 if (pl == 100)
138 printf ("PING %s - %sPacket loss = %d%%", state_text (this_result), warn_text,
139 pl);
140 else
141 printf ("PING %s - %sPacket loss = %d%%, RTA = %2.2f ms",
142 state_text (this_result), warn_text, pl, rta);
143 if (display_html == TRUE)
144 printf ("</A>");
145 printf ("\n");
147 if (verbose)
148 printf ("%f:%d%% %f:%d%%\n", wrta, wpl, crta, cpl);
150 result = max_state (result, this_result);
152 }
154 return result;
155 }
156 \f
158 /* process command-line arguments */
159 int
160 process_arguments (int argc, char **argv)
161 {
162 int c = 1;
163 char *ptr;
165 int option_index = 0;
166 static struct option long_options[] = {
167 STD_LONG_OPTS,
168 {"packets", required_argument, 0, 'p'},
169 {"nohtml", no_argument, 0, 'n'},
170 {"link", no_argument, 0, 'L'},
171 {0, 0, 0, 0}
172 };
174 if (argc < 2)
175 return ERROR;
177 for (c = 1; c < argc; c++) {
178 if (strcmp ("-to", argv[c]) == 0)
179 strcpy (argv[c], "-t");
180 if (strcmp ("-nohtml", argv[c]) == 0)
181 strcpy (argv[c], "-n");
182 }
184 while (1) {
185 c = getopt_long (argc, argv, "VvhnLt:c:w:H:p:", long_options, &option_index);
187 if (c == -1 || c == EOF)
188 break;
190 switch (c) {
191 case '?': /* usage */
192 usage3 ("Unknown argument", optopt);
193 case 'h': /* help */
194 print_help ();
195 exit (STATE_OK);
196 case 'V': /* version */
197 print_revision (progname, REVISION);
198 exit (STATE_OK);
199 case 't': /* timeout period */
200 timeout_interval = atoi (optarg);
201 break;
202 case 'v': /* verbose mode */
203 verbose = TRUE;
204 break;
205 case 'H': /* hostname */
206 ptr=optarg;
207 while (1) {
208 n_addresses++;
209 if (n_addresses > max_addr) {
210 max_addr *= 2;
211 addresses = realloc (addresses, max_addr);
212 if (addresses == NULL)
213 terminate (STATE_UNKNOWN, "Could not realloc() addresses\n");
214 }
215 addresses[n_addresses-1] = ptr;
216 if (ptr = index (ptr, ',')) {
217 strcpy (ptr, "");
218 ptr += sizeof(char);
219 } else {
220 break;
221 }
222 }
223 break;
224 case 'p': /* number of packets to send */
225 if (is_intnonneg (optarg))
226 max_packets = atoi (optarg);
227 else
228 usage2 ("<max_packets> (%s) must be a non-negative number\n", optarg);
229 break;
230 case 'n': /* no HTML */
231 display_html = FALSE;
232 break;
233 case 'L': /* show HTML */
234 display_html = TRUE;
235 break;
236 case 'c':
237 get_threshold (optarg, &crta, &cpl);
238 break;
239 case 'w':
240 get_threshold (optarg, &wrta, &wpl);
241 break;
242 }
243 }
245 c = optind;
246 if (c == argc)
247 return validate_arguments ();
249 if (addresses[0] == NULL) {
250 if (is_host (argv[c]) == FALSE) {
251 printf ("Invalid host name/address: %s\n\n", argv[c]);
252 return ERROR;
253 } else {
254 addresses[0] = argv[c++];
255 if (c == argc)
256 return validate_arguments ();
257 }
258 }
260 if (wpl == UNKNOWN_PACKET_LOSS) {
261 if (is_intpercent (argv[c]) == FALSE) {
262 printf ("<wpl> (%s) must be an integer percentage\n", argv[c]);
263 return ERROR;
264 } else {
265 wpl = atoi (argv[c++]);
266 if (c == argc)
267 return validate_arguments ();
268 }
269 }
271 if (cpl == UNKNOWN_PACKET_LOSS) {
272 if (is_intpercent (argv[c]) == FALSE) {
273 printf ("<cpl> (%s) must be an integer percentage\n", argv[c]);
274 return ERROR;
275 } else {
276 cpl = atoi (argv[c++]);
277 if (c == argc)
278 return validate_arguments ();
279 }
280 }
282 if (wrta == UNKNOWN_TRIP_TIME) {
283 if (is_negative (argv[c])) {
284 printf ("<wrta> (%s) must be a non-negative number\n", argv[c]);
285 return ERROR;
286 } else {
287 wrta = atof (argv[c++]);
288 if (c == argc)
289 return validate_arguments ();
290 }
291 }
293 if (crta == UNKNOWN_TRIP_TIME) {
294 if (is_negative (argv[c])) {
295 printf ("<crta> (%s) must be a non-negative number\n", argv[c]);
296 return ERROR;
297 } else {
298 crta = atof (argv[c++]);
299 if (c == argc)
300 return validate_arguments ();
301 }
302 }
304 if (max_packets == -1) {
305 if (is_intnonneg (argv[c])) {
306 max_packets = atoi (argv[c++]);
307 } else {
308 printf ("<max_packets> (%s) must be a non-negative number\n", argv[c]);
309 return ERROR;
310 }
311 }
313 return validate_arguments ();
314 }
316 int
317 get_threshold (char *arg, float *trta, int *tpl)
318 {
319 if (is_intnonneg (arg) && sscanf (arg, "%f", trta) == 1)
320 return OK;
321 else if (strpbrk (arg, ",:") && strstr (arg, "%") && sscanf (arg, "%f%*[:,]%d%%", trta, tpl) == 2)
322 return OK;
323 else if (strstr (arg, "%") && sscanf (arg, "%d%%", tpl) == 1)
324 return OK;
325 else
326 usage2 ("%s: Warning threshold must be integer or percentage!\n\n", arg);
327 }
329 int
330 validate_arguments ()
331 {
332 float max_seconds;
333 int i;
335 if (wrta == UNKNOWN_TRIP_TIME) {
336 printf ("<wrta> was not set\n");
337 return ERROR;
338 }
339 else if (crta == UNKNOWN_TRIP_TIME) {
340 printf ("<crta> was not set\n");
341 return ERROR;
342 }
343 else if (wpl == UNKNOWN_PACKET_LOSS) {
344 printf ("<wpl> was not set\n");
345 return ERROR;
346 }
347 else if (cpl == UNKNOWN_PACKET_LOSS) {
348 printf ("<cpl> was not set\n");
349 return ERROR;
350 }
351 else if (wrta > crta) {
352 printf ("<wrta> (%f) cannot be larger than <crta> (%f)\n", wrta, crta);
353 return ERROR;
354 }
355 else if (wpl > cpl) {
356 printf ("<wpl> (%d) cannot be larger than <cpl> (%d)\n", wpl, cpl);
357 return ERROR;
358 }
360 if (max_packets == -1)
361 max_packets = DEFAULT_MAX_PACKETS;
363 max_seconds = crta / 1000.0 * max_packets + max_packets;
364 if (max_seconds > timeout_interval)
365 timeout_interval = (int)max_seconds;
367 for (i=0; i<n_addresses; i++) {
368 if (is_host(addresses[i]) == FALSE)
369 usage2 ("Invalid host name/address", addresses[i]);
370 }
372 return OK;
373 }
374 \f
376 int
377 run_ping (char *command_line, char *server_address)
378 {
379 char input_buffer[MAX_INPUT_BUFFER];
380 int result = STATE_UNKNOWN;
382 warn_text = malloc (1);
383 if (warn_text == NULL)
384 terminate (STATE_UNKNOWN, "unable to malloc warn_text");
385 warn_text[0] = 0;
387 if ((child_process = spopen (command_line)) == NULL) {
388 printf ("Cannot open pipe: ");
389 terminate (STATE_UNKNOWN, command_line);
390 }
391 child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
392 if (child_stderr == NULL)
393 printf ("Cannot open stderr for %s\n", command_line);
395 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
397 if (strstr (input_buffer, "(DUP!)")) {
398 /* cannot use the max function since STATE_UNKNOWN is max
399 result = max (result, STATE_WARNING); */
400 if( !(result == STATE_CRITICAL) ){
401 result = STATE_WARNING;
402 }
404 warn_text = realloc (warn_text, strlen (WARN_DUPLICATES) + 1);
405 if (warn_text == NULL)
406 terminate (STATE_UNKNOWN, "unable to realloc warn_text");
407 strcpy (warn_text, WARN_DUPLICATES);
408 }
410 /* get the percent loss statistics */
411 if (sscanf
412 (input_buffer, "%*d packets transmitted, %*d packets received, +%*d errors, %d%% packet loss",
413 &pl) == 1
414 || sscanf
415 (input_buffer, "%*d packets transmitted, %*d packets received, %d%% packet loss",
416 &pl) == 1
417 || sscanf
418 (input_buffer, "%*d packets transmitted, %*d packets received, %d%% loss, time", &pl) == 1
419 || sscanf
420 (input_buffer, "%*d packets transmitted, %*d received, %d%% loss, time", &pl) == 1
421 /* Suse 8.0 as reported by Richard * Brodie */
422 )
423 continue;
425 /* get the round trip average */
426 else
427 if (sscanf (input_buffer, "round-trip min/avg/max = %*f/%f/%*f", &rta)
428 == 1
429 || sscanf (input_buffer,
430 "round-trip min/avg/max/mdev = %*f/%f/%*f/%*f",
431 &rta) == 1
432 || sscanf (input_buffer,
433 "round-trip min/avg/max/sdev = %*f/%f/%*f/%*f",
434 &rta) == 1
435 || sscanf (input_buffer,
436 "round-trip min/avg/max/stddev = %*f/%f/%*f/%*f",
437 &rta) == 1
438 || sscanf (input_buffer,
439 "round-trip min/avg/max/std-dev = %*f/%f/%*f/%*f",
440 &rta) == 1
441 || sscanf (input_buffer, "round-trip (ms) min/avg/max = %*f/%f/%*f",
442 &rta) == 1
443 || sscanf (input_buffer, "rtt min/avg/max/mdev = %*f/%f/%*f/%*f ms",
444 &rta) == 1
445 )
446 continue;
447 }
449 /* this is needed because there is no rta if all packets are lost */
450 if (pl == 100)
451 rta = crta;
454 /* check stderr */
455 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
456 if (strstr
457 (input_buffer,
458 "Warning: no SO_TIMESTAMP support, falling back to SIOCGSTAMP"))
459 continue;
461 if (strstr (input_buffer, "Network is unreachable"))
462 terminate (STATE_CRITICAL, "PING CRITICAL - Network unreachable (%s)",
463 server_address);
464 else if (strstr (input_buffer, "Destination Host Unreachable"))
465 terminate (STATE_CRITICAL, "PING CRITICAL - Host Unreachable (%s)",
466 server_address);
467 else if (strstr (input_buffer, "unknown host" ) )
468 terminate (STATE_CRITICAL, "PING CRITICAL - Host not found (%s)",
469 server_address);
471 warn_text =
472 realloc (warn_text, strlen (warn_text) + strlen (input_buffer) + 2);
473 if (warn_text == NULL)
474 terminate (STATE_UNKNOWN, "unable to realloc warn_text");
475 if (strlen (warn_text) == 0)
476 strcpy (warn_text, input_buffer);
477 else
478 sprintf (warn_text, "%s %s", warn_text, input_buffer);
480 if (strstr (input_buffer, "DUPLICATES FOUND")) {
481 if( !(result == STATE_CRITICAL) ){
482 result = STATE_WARNING;
483 }
484 }
485 else
486 result = STATE_CRITICAL ;
487 }
488 (void) fclose (child_stderr);
491 /* close the pipe - WARNING if status is set */
492 if (spclose (child_process))
493 result = max_state (result, STATE_WARNING);
495 return result;
496 }
499 void
500 print_usage (void)
501 {
502 printf ("Usage:\n" " %s %s\n"
503 " %s (-h | --help) for detailed help\n"
504 " %s (-V | --version) for version information\n",
505 progname, OPTIONS, progname, progname);
506 }
508 void
509 print_help (void)
510 {
511 print_revision (progname, REVISION);
512 printf
513 ("Copyright (c) %s %s <%s>\n\n%s\n",
514 COPYRIGHT, AUTHOR, EMAIL, SUMMARY);
515 print_usage ();
516 printf
517 ("\nOptions:\n" LONGOPTIONS "\n" DESCRIPTION "\n",
518 DEFAULT_MAX_PACKETS, DEFAULT_SOCKET_TIMEOUT);
519 support ();
520 }