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] [-4|-6]\n"
24 #define LONGOPTIONS "\
25 -H, --hostname=HOST\n\
26 host to ping\n\
27 -4, --use-ipv4\n\
28 Use IPv4 ICMP PING\n\
29 -6, --use-ipv6\n\
30 Use IPv6 ICMP PING\n\
31 -w, --warning=THRESHOLD\n\
32 warning threshold pair\n\
33 -c, --critical=THRESHOLD\n\
34 critical threshold pair\n\
35 -p, --packets=INTEGER\n\
36 number of ICMP ECHO packets to send (Default: %d)\n\
37 -t, --timeout=INTEGER\n\
38 optional specified timeout in second (Default: %d)\n\
39 -L, --link\n\
40 show HTML in the plugin output (obsoleted by urlize)\n\
41 THRESHOLD is <rta>,<pl>%% where <rta> is the round trip average travel\n\
42 time (ms) which triggers a WARNING or CRITICAL state, and <pl> is the\n\
43 percentage of packet loss to trigger an alarm state.\n"
45 #define DESCRIPTION "\
46 This plugin uses the ping command to probe the specified host for packet loss\n\
47 (percentage) and round trip average (milliseconds). It can produce HTML output\n\
48 linking to a traceroute CGI contributed by Ian Cass. The CGI can be found in\n\
49 the contrib area of the downloads section at http://www.nagios.org\n\n"
51 #include "config.h"
52 #include "common.h"
53 #include "netutils.h"
54 #include "popen.h"
55 #include "utils.h"
57 #define UNKNOWN_PACKET_LOSS 200 /* 200% */
58 #define UNKNOWN_TRIP_TIME -1.0 /* -1 seconds */
59 #define DEFAULT_MAX_PACKETS 5 /* default no. of ICMP ECHO packets */
61 #define WARN_DUPLICATES "DUPLICATES FOUND! "
63 int process_arguments (int, char **);
64 int get_threshold (char *, float *, int *);
65 int validate_arguments (void);
66 int run_ping (char *, char *);
67 void print_usage (void);
68 void print_help (void);
70 int display_html = FALSE;
71 int wpl = UNKNOWN_PACKET_LOSS;
72 int cpl = UNKNOWN_PACKET_LOSS;
73 float wrta = UNKNOWN_TRIP_TIME;
74 float crta = UNKNOWN_TRIP_TIME;
75 char **addresses = NULL;
76 int n_addresses;
77 int max_addr = 1;
78 int max_packets = -1;
79 int verbose = FALSE;
81 float rta = UNKNOWN_TRIP_TIME;
82 int pl = UNKNOWN_PACKET_LOSS;
84 char *warn_text = NULL;
86 int
87 main (int argc, char **argv)
88 {
89 char *command_line = NULL;
90 int result = STATE_UNKNOWN;
91 int this_result = STATE_UNKNOWN;
92 int i;
94 addresses = malloc (max_addr);
96 if (process_arguments (argc, argv) == ERROR)
97 usage ("Could not parse arguments");
98 exit;
100 /* Set signal handling and alarm */
101 if (signal (SIGALRM, popen_timeout_alarm_handler) == SIG_ERR) {
102 printf ("Cannot catch SIGALRM");
103 return STATE_UNKNOWN;
104 }
106 /* handle timeouts gracefully */
107 alarm (timeout_interval);
109 for (i = 0 ; i < n_addresses ; i++) {
111 /* does the host address of number of packets argument come first? */
112 #ifdef PING6_COMMAND
113 # ifdef PING_PACKETS_FIRST
114 if (is_inet6_addr(addresses[i]) && address_family != AF_INET)
115 asprintf (&command_line, PING6_COMMAND, max_packets, addresses[i]);
116 else
117 asprintf (&command_line, PING_COMMAND, max_packets, addresses[i]);
118 # else
119 if (is_inet6_addr(addresses[i]) && address_family != AF_INET)
120 asprintf (&command_line, PING6_COMMAND, addresses[i], max_packets);
121 else
122 asprintf (&command_line, PING_COMMAND, addresses[i], max_packets);
123 # endif
124 #else /* USE_IPV6 */
125 # ifdef PING_PACKETS_FIRST
126 asprintf (&command_line, PING_COMMAND, max_packets, addresses[i]);
127 # else
128 asprintf (&command_line, PING_COMMAND, addresses[i], max_packets);
129 # endif
130 #endif /* USE_IPV6 */
132 if (verbose)
133 printf ("%s ==> ", command_line);
135 /* run the command */
136 this_result = run_ping (command_line, addresses[i]);
138 if (pl == UNKNOWN_PACKET_LOSS || rta == UNKNOWN_TRIP_TIME) {
139 printf ("%s\n", command_line);
140 terminate (STATE_UNKNOWN,
141 "Error: Could not interpret output from ping command\n");
142 }
144 if (pl >= cpl || rta >= crta || rta < 0)
145 this_result = STATE_CRITICAL;
146 else if (pl >= wpl || rta >= wrta)
147 this_result = STATE_WARNING;
148 else if (pl >= 0 && rta >= 0)
149 this_result = max_state (STATE_OK, this_result);
151 if (n_addresses > 1 && this_result != STATE_UNKNOWN)
152 terminate (STATE_OK, "%s is alive\n", addresses[i]);
154 if (display_html == TRUE)
155 printf ("<A HREF='%s/traceroute.cgi?%s'>", CGIURL, addresses[i]);
156 if (pl == 100)
157 printf ("PING %s - %sPacket loss = %d%%", state_text (this_result), warn_text,
158 pl);
159 else
160 printf ("PING %s - %sPacket loss = %d%%, RTA = %2.2f ms",
161 state_text (this_result), warn_text, pl, rta);
162 if (display_html == TRUE)
163 printf ("</A>");
164 printf ("\n");
166 if (verbose)
167 printf ("%f:%d%% %f:%d%%\n", wrta, wpl, crta, cpl);
169 result = max_state (result, this_result);
171 }
173 return result;
174 }
175 \f
177 /* process command-line arguments */
178 int
179 process_arguments (int argc, char **argv)
180 {
181 int c = 1;
182 char *ptr;
184 int option_index = 0;
185 static struct option long_options[] = {
186 STD_LONG_OPTS,
187 {"packets", required_argument, 0, 'p'},
188 {"nohtml", no_argument, 0, 'n'},
189 {"link", no_argument, 0, 'L'},
190 {"use-ipv4", no_argument, 0, '4'},
191 {"use-ipv6", no_argument, 0, '6'},
192 {0, 0, 0, 0}
193 };
195 if (argc < 2)
196 return ERROR;
198 for (c = 1; c < argc; c++) {
199 if (strcmp ("-to", argv[c]) == 0)
200 strcpy (argv[c], "-t");
201 if (strcmp ("-nohtml", argv[c]) == 0)
202 strcpy (argv[c], "-n");
203 }
205 while (1) {
206 c = getopt_long (argc, argv, "VvhnL46t:c:w:H:p:", long_options, &option_index);
208 if (c == -1 || c == EOF)
209 break;
211 switch (c) {
212 case '?': /* usage */
213 usage3 ("Unknown argument", optopt);
214 case 'h': /* help */
215 print_help ();
216 exit (STATE_OK);
217 case 'V': /* version */
218 print_revision (progname, REVISION);
219 exit (STATE_OK);
220 case 't': /* timeout period */
221 timeout_interval = atoi (optarg);
222 break;
223 case 'v': /* verbose mode */
224 verbose = TRUE;
225 break;
226 case '4': /* IPv4 only */
227 address_family = AF_INET;
228 break;
229 case '6': /* IPv6 only */
230 #ifdef USE_IPV6
231 address_family = AF_INET6;
232 #else
233 usage ("IPv6 support not available\n");
234 #endif
235 break;
236 case 'H': /* hostname */
237 ptr=optarg;
238 while (1) {
239 n_addresses++;
240 if (n_addresses > max_addr) {
241 max_addr *= 2;
242 addresses = realloc (addresses, max_addr);
243 if (addresses == NULL)
244 terminate (STATE_UNKNOWN, "Could not realloc() addresses\n");
245 }
246 addresses[n_addresses-1] = ptr;
247 if (ptr = index (ptr, ',')) {
248 strcpy (ptr, "");
249 ptr += sizeof(char);
250 } else {
251 break;
252 }
253 }
254 break;
255 case 'p': /* number of packets to send */
256 if (is_intnonneg (optarg))
257 max_packets = atoi (optarg);
258 else
259 usage2 ("<max_packets> (%s) must be a non-negative number\n", optarg);
260 break;
261 case 'n': /* no HTML */
262 display_html = FALSE;
263 break;
264 case 'L': /* show HTML */
265 display_html = TRUE;
266 break;
267 case 'c':
268 get_threshold (optarg, &crta, &cpl);
269 break;
270 case 'w':
271 get_threshold (optarg, &wrta, &wpl);
272 break;
273 }
274 }
276 c = optind;
277 if (c == argc)
278 return validate_arguments ();
280 if (addresses[0] == NULL) {
281 if (is_host (argv[c]) == FALSE) {
282 printf ("Invalid host name/address: %s\n\n", argv[c]);
283 return ERROR;
284 } else {
285 addresses[0] = argv[c++];
286 if (c == argc)
287 return validate_arguments ();
288 }
289 }
291 if (wpl == UNKNOWN_PACKET_LOSS) {
292 if (is_intpercent (argv[c]) == FALSE) {
293 printf ("<wpl> (%s) must be an integer percentage\n", argv[c]);
294 return ERROR;
295 } else {
296 wpl = atoi (argv[c++]);
297 if (c == argc)
298 return validate_arguments ();
299 }
300 }
302 if (cpl == UNKNOWN_PACKET_LOSS) {
303 if (is_intpercent (argv[c]) == FALSE) {
304 printf ("<cpl> (%s) must be an integer percentage\n", argv[c]);
305 return ERROR;
306 } else {
307 cpl = atoi (argv[c++]);
308 if (c == argc)
309 return validate_arguments ();
310 }
311 }
313 if (wrta == UNKNOWN_TRIP_TIME) {
314 if (is_negative (argv[c])) {
315 printf ("<wrta> (%s) must be a non-negative number\n", argv[c]);
316 return ERROR;
317 } else {
318 wrta = atof (argv[c++]);
319 if (c == argc)
320 return validate_arguments ();
321 }
322 }
324 if (crta == UNKNOWN_TRIP_TIME) {
325 if (is_negative (argv[c])) {
326 printf ("<crta> (%s) must be a non-negative number\n", argv[c]);
327 return ERROR;
328 } else {
329 crta = atof (argv[c++]);
330 if (c == argc)
331 return validate_arguments ();
332 }
333 }
335 if (max_packets == -1) {
336 if (is_intnonneg (argv[c])) {
337 max_packets = atoi (argv[c++]);
338 } else {
339 printf ("<max_packets> (%s) must be a non-negative number\n", argv[c]);
340 return ERROR;
341 }
342 }
344 return validate_arguments ();
345 }
347 int
348 get_threshold (char *arg, float *trta, int *tpl)
349 {
350 if (is_intnonneg (arg) && sscanf (arg, "%f", trta) == 1)
351 return OK;
352 else if (strpbrk (arg, ",:") && strstr (arg, "%") && sscanf (arg, "%f%*[:,]%d%%", trta, tpl) == 2)
353 return OK;
354 else if (strstr (arg, "%") && sscanf (arg, "%d%%", tpl) == 1)
355 return OK;
356 else
357 usage2 ("%s: Warning threshold must be integer or percentage!\n\n", arg);
358 }
360 int
361 validate_arguments ()
362 {
363 float max_seconds;
364 int i;
366 if (wrta == UNKNOWN_TRIP_TIME) {
367 printf ("<wrta> was not set\n");
368 return ERROR;
369 }
370 else if (crta == UNKNOWN_TRIP_TIME) {
371 printf ("<crta> was not set\n");
372 return ERROR;
373 }
374 else if (wpl == UNKNOWN_PACKET_LOSS) {
375 printf ("<wpl> was not set\n");
376 return ERROR;
377 }
378 else if (cpl == UNKNOWN_PACKET_LOSS) {
379 printf ("<cpl> was not set\n");
380 return ERROR;
381 }
382 else if (wrta > crta) {
383 printf ("<wrta> (%f) cannot be larger than <crta> (%f)\n", wrta, crta);
384 return ERROR;
385 }
386 else if (wpl > cpl) {
387 printf ("<wpl> (%d) cannot be larger than <cpl> (%d)\n", wpl, cpl);
388 return ERROR;
389 }
391 if (max_packets == -1)
392 max_packets = DEFAULT_MAX_PACKETS;
394 max_seconds = crta / 1000.0 * max_packets + max_packets;
395 if (max_seconds > timeout_interval)
396 timeout_interval = (int)max_seconds;
398 for (i=0; i<n_addresses; i++) {
399 if (is_host(addresses[i]) == FALSE)
400 usage2 ("Invalid host name/address", addresses[i]);
401 }
403 return OK;
404 }
405 \f
407 int
408 run_ping (char *command_line, char *server_address)
409 {
410 char input_buffer[MAX_INPUT_BUFFER];
411 int result = STATE_UNKNOWN;
413 warn_text = malloc (1);
414 if (warn_text == NULL)
415 terminate (STATE_UNKNOWN, "unable to malloc warn_text");
416 warn_text[0] = 0;
418 if ((child_process = spopen (command_line)) == NULL) {
419 printf ("Cannot open pipe: ");
420 terminate (STATE_UNKNOWN, command_line);
421 }
422 child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
423 if (child_stderr == NULL)
424 printf ("Cannot open stderr for %s\n", command_line);
426 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
428 if (strstr (input_buffer, "(DUP!)")) {
429 /* cannot use the max function since STATE_UNKNOWN is max
430 result = max (result, STATE_WARNING); */
431 if( !(result == STATE_CRITICAL) ){
432 result = STATE_WARNING;
433 }
435 warn_text = realloc (warn_text, strlen (WARN_DUPLICATES) + 1);
436 if (warn_text == NULL)
437 terminate (STATE_UNKNOWN, "unable to realloc warn_text");
438 strcpy (warn_text, WARN_DUPLICATES);
439 }
441 /* get the percent loss statistics */
442 if (sscanf
443 (input_buffer, "%*d packets transmitted, %*d packets received, +%*d errors, %d%% packet loss",
444 &pl) == 1
445 || sscanf
446 (input_buffer, "%*d packets transmitted, %*d packets received, %d%% packet loss",
447 &pl) == 1
448 || sscanf
449 (input_buffer, "%*d packets transmitted, %*d packets received, %d%% loss, time", &pl) == 1
450 || sscanf
451 (input_buffer, "%*d packets transmitted, %*d received, %d%% loss, time", &pl) == 1
452 /* Suse 8.0 as reported by Richard * Brodie */
453 )
454 continue;
456 /* get the round trip average */
457 else
458 if (sscanf (input_buffer, "round-trip min/avg/max = %*f/%f/%*f", &rta)
459 == 1
460 || sscanf (input_buffer,
461 "round-trip min/avg/max/mdev = %*f/%f/%*f/%*f",
462 &rta) == 1
463 || sscanf (input_buffer,
464 "round-trip min/avg/max/sdev = %*f/%f/%*f/%*f",
465 &rta) == 1
466 || sscanf (input_buffer,
467 "round-trip min/avg/max/stddev = %*f/%f/%*f/%*f",
468 &rta) == 1
469 || sscanf (input_buffer,
470 "round-trip min/avg/max/std-dev = %*f/%f/%*f/%*f",
471 &rta) == 1
472 || sscanf (input_buffer, "round-trip (ms) min/avg/max = %*f/%f/%*f",
473 &rta) == 1
474 || sscanf (input_buffer, "rtt min/avg/max/mdev = %*f/%f/%*f/%*f ms",
475 &rta) == 1
476 )
477 continue;
478 }
480 /* this is needed because there is no rta if all packets are lost */
481 if (pl == 100)
482 rta = crta;
485 /* check stderr */
486 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
487 if (strstr
488 (input_buffer,
489 "Warning: no SO_TIMESTAMP support, falling back to SIOCGSTAMP"))
490 continue;
492 if (strstr (input_buffer, "Network is unreachable"))
493 terminate (STATE_CRITICAL, "PING CRITICAL - Network unreachable (%s)",
494 server_address);
495 else if (strstr (input_buffer, "Destination Host Unreachable"))
496 terminate (STATE_CRITICAL, "PING CRITICAL - Host Unreachable (%s)",
497 server_address);
498 else if (strstr (input_buffer, "unknown host" ) )
499 terminate (STATE_CRITICAL, "PING CRITICAL - Host not found (%s)",
500 server_address);
502 warn_text =
503 realloc (warn_text, strlen (warn_text) + strlen (input_buffer) + 2);
504 if (warn_text == NULL)
505 terminate (STATE_UNKNOWN, "unable to realloc warn_text");
506 if (strlen (warn_text) == 0)
507 strcpy (warn_text, input_buffer);
508 else
509 sprintf (warn_text, "%s %s", warn_text, input_buffer);
511 if (strstr (input_buffer, "DUPLICATES FOUND")) {
512 if( !(result == STATE_CRITICAL) ){
513 result = STATE_WARNING;
514 }
515 }
516 else
517 result = STATE_CRITICAL ;
518 }
519 (void) fclose (child_stderr);
522 /* close the pipe - WARNING if status is set */
523 if (spclose (child_process))
524 result = max_state (result, STATE_WARNING);
526 return result;
527 }
530 void
531 print_usage (void)
532 {
533 printf ("Usage:\n" " %s %s\n"
534 " %s (-h | --help) for detailed help\n"
535 " %s (-V | --version) for version information\n",
536 progname, OPTIONS, progname, progname);
537 }
539 void
540 print_help (void)
541 {
542 print_revision (progname, REVISION);
543 printf
544 ("Copyright (c) %s %s <%s>\n\n%s\n",
545 COPYRIGHT, AUTHOR, EMAIL, SUMMARY);
546 print_usage ();
547 printf
548 ("\nOptions:\n" LONGOPTIONS "\n" DESCRIPTION "\n",
549 DEFAULT_MAX_PACKETS, DEFAULT_SOCKET_TIMEOUT);
550 support ();
551 }