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 address_family = AF_INET6;
231 break;
232 case 'H': /* hostname */
233 ptr=optarg;
234 while (1) {
235 n_addresses++;
236 if (n_addresses > max_addr) {
237 max_addr *= 2;
238 addresses = realloc (addresses, max_addr);
239 if (addresses == NULL)
240 terminate (STATE_UNKNOWN, "Could not realloc() addresses\n");
241 }
242 addresses[n_addresses-1] = ptr;
243 if (ptr = index (ptr, ',')) {
244 strcpy (ptr, "");
245 ptr += sizeof(char);
246 } else {
247 break;
248 }
249 }
250 break;
251 case 'p': /* number of packets to send */
252 if (is_intnonneg (optarg))
253 max_packets = atoi (optarg);
254 else
255 usage2 ("<max_packets> (%s) must be a non-negative number\n", optarg);
256 break;
257 case 'n': /* no HTML */
258 display_html = FALSE;
259 break;
260 case 'L': /* show HTML */
261 display_html = TRUE;
262 break;
263 case 'c':
264 get_threshold (optarg, &crta, &cpl);
265 break;
266 case 'w':
267 get_threshold (optarg, &wrta, &wpl);
268 break;
269 }
270 }
272 c = optind;
273 if (c == argc)
274 return validate_arguments ();
276 if (addresses[0] == NULL) {
277 if (is_host (argv[c]) == FALSE) {
278 printf ("Invalid host name/address: %s\n\n", argv[c]);
279 return ERROR;
280 } else {
281 addresses[0] = argv[c++];
282 if (c == argc)
283 return validate_arguments ();
284 }
285 }
287 if (wpl == UNKNOWN_PACKET_LOSS) {
288 if (is_intpercent (argv[c]) == FALSE) {
289 printf ("<wpl> (%s) must be an integer percentage\n", argv[c]);
290 return ERROR;
291 } else {
292 wpl = atoi (argv[c++]);
293 if (c == argc)
294 return validate_arguments ();
295 }
296 }
298 if (cpl == UNKNOWN_PACKET_LOSS) {
299 if (is_intpercent (argv[c]) == FALSE) {
300 printf ("<cpl> (%s) must be an integer percentage\n", argv[c]);
301 return ERROR;
302 } else {
303 cpl = atoi (argv[c++]);
304 if (c == argc)
305 return validate_arguments ();
306 }
307 }
309 if (wrta == UNKNOWN_TRIP_TIME) {
310 if (is_negative (argv[c])) {
311 printf ("<wrta> (%s) must be a non-negative number\n", argv[c]);
312 return ERROR;
313 } else {
314 wrta = atof (argv[c++]);
315 if (c == argc)
316 return validate_arguments ();
317 }
318 }
320 if (crta == UNKNOWN_TRIP_TIME) {
321 if (is_negative (argv[c])) {
322 printf ("<crta> (%s) must be a non-negative number\n", argv[c]);
323 return ERROR;
324 } else {
325 crta = atof (argv[c++]);
326 if (c == argc)
327 return validate_arguments ();
328 }
329 }
331 if (max_packets == -1) {
332 if (is_intnonneg (argv[c])) {
333 max_packets = atoi (argv[c++]);
334 } else {
335 printf ("<max_packets> (%s) must be a non-negative number\n", argv[c]);
336 return ERROR;
337 }
338 }
340 return validate_arguments ();
341 }
343 int
344 get_threshold (char *arg, float *trta, int *tpl)
345 {
346 if (is_intnonneg (arg) && sscanf (arg, "%f", trta) == 1)
347 return OK;
348 else if (strpbrk (arg, ",:") && strstr (arg, "%") && sscanf (arg, "%f%*[:,]%d%%", trta, tpl) == 2)
349 return OK;
350 else if (strstr (arg, "%") && sscanf (arg, "%d%%", tpl) == 1)
351 return OK;
352 else
353 usage2 ("%s: Warning threshold must be integer or percentage!\n\n", arg);
354 }
356 int
357 validate_arguments ()
358 {
359 float max_seconds;
360 int i;
362 if (wrta == UNKNOWN_TRIP_TIME) {
363 printf ("<wrta> was not set\n");
364 return ERROR;
365 }
366 else if (crta == UNKNOWN_TRIP_TIME) {
367 printf ("<crta> was not set\n");
368 return ERROR;
369 }
370 else if (wpl == UNKNOWN_PACKET_LOSS) {
371 printf ("<wpl> was not set\n");
372 return ERROR;
373 }
374 else if (cpl == UNKNOWN_PACKET_LOSS) {
375 printf ("<cpl> was not set\n");
376 return ERROR;
377 }
378 else if (wrta > crta) {
379 printf ("<wrta> (%f) cannot be larger than <crta> (%f)\n", wrta, crta);
380 return ERROR;
381 }
382 else if (wpl > cpl) {
383 printf ("<wpl> (%d) cannot be larger than <cpl> (%d)\n", wpl, cpl);
384 return ERROR;
385 }
387 if (max_packets == -1)
388 max_packets = DEFAULT_MAX_PACKETS;
390 max_seconds = crta / 1000.0 * max_packets + max_packets;
391 if (max_seconds > timeout_interval)
392 timeout_interval = (int)max_seconds;
394 for (i=0; i<n_addresses; i++) {
395 if (is_host(addresses[i]) == FALSE)
396 usage2 ("Invalid host name/address", addresses[i]);
397 }
399 return OK;
400 }
401 \f
403 int
404 run_ping (char *command_line, char *server_address)
405 {
406 char input_buffer[MAX_INPUT_BUFFER];
407 int result = STATE_UNKNOWN;
409 warn_text = malloc (1);
410 if (warn_text == NULL)
411 terminate (STATE_UNKNOWN, "unable to malloc warn_text");
412 warn_text[0] = 0;
414 if ((child_process = spopen (command_line)) == NULL) {
415 printf ("Cannot open pipe: ");
416 terminate (STATE_UNKNOWN, command_line);
417 }
418 child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
419 if (child_stderr == NULL)
420 printf ("Cannot open stderr for %s\n", command_line);
422 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
424 if (strstr (input_buffer, "(DUP!)")) {
425 /* cannot use the max function since STATE_UNKNOWN is max
426 result = max (result, STATE_WARNING); */
427 if( !(result == STATE_CRITICAL) ){
428 result = STATE_WARNING;
429 }
431 warn_text = realloc (warn_text, strlen (WARN_DUPLICATES) + 1);
432 if (warn_text == NULL)
433 terminate (STATE_UNKNOWN, "unable to realloc warn_text");
434 strcpy (warn_text, WARN_DUPLICATES);
435 }
437 /* get the percent loss statistics */
438 if (sscanf
439 (input_buffer, "%*d packets transmitted, %*d packets received, +%*d errors, %d%% packet loss",
440 &pl) == 1
441 || sscanf
442 (input_buffer, "%*d packets transmitted, %*d packets received, %d%% packet loss",
443 &pl) == 1
444 || sscanf
445 (input_buffer, "%*d packets transmitted, %*d packets received, %d%% loss, time", &pl) == 1
446 || sscanf
447 (input_buffer, "%*d packets transmitted, %*d received, %d%% loss, time", &pl) == 1
448 /* Suse 8.0 as reported by Richard * Brodie */
449 )
450 continue;
452 /* get the round trip average */
453 else
454 if (sscanf (input_buffer, "round-trip min/avg/max = %*f/%f/%*f", &rta)
455 == 1
456 || sscanf (input_buffer,
457 "round-trip min/avg/max/mdev = %*f/%f/%*f/%*f",
458 &rta) == 1
459 || sscanf (input_buffer,
460 "round-trip min/avg/max/sdev = %*f/%f/%*f/%*f",
461 &rta) == 1
462 || sscanf (input_buffer,
463 "round-trip min/avg/max/stddev = %*f/%f/%*f/%*f",
464 &rta) == 1
465 || sscanf (input_buffer,
466 "round-trip min/avg/max/std-dev = %*f/%f/%*f/%*f",
467 &rta) == 1
468 || sscanf (input_buffer, "round-trip (ms) min/avg/max = %*f/%f/%*f",
469 &rta) == 1
470 || sscanf (input_buffer, "rtt min/avg/max/mdev = %*f/%f/%*f/%*f ms",
471 &rta) == 1
472 )
473 continue;
474 }
476 /* this is needed because there is no rta if all packets are lost */
477 if (pl == 100)
478 rta = crta;
481 /* check stderr */
482 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
483 if (strstr
484 (input_buffer,
485 "Warning: no SO_TIMESTAMP support, falling back to SIOCGSTAMP"))
486 continue;
488 if (strstr (input_buffer, "Network is unreachable"))
489 terminate (STATE_CRITICAL, "PING CRITICAL - Network unreachable (%s)",
490 server_address);
491 else if (strstr (input_buffer, "Destination Host Unreachable"))
492 terminate (STATE_CRITICAL, "PING CRITICAL - Host Unreachable (%s)",
493 server_address);
494 else if (strstr (input_buffer, "unknown host" ) )
495 terminate (STATE_CRITICAL, "PING CRITICAL - Host not found (%s)",
496 server_address);
498 warn_text =
499 realloc (warn_text, strlen (warn_text) + strlen (input_buffer) + 2);
500 if (warn_text == NULL)
501 terminate (STATE_UNKNOWN, "unable to realloc warn_text");
502 if (strlen (warn_text) == 0)
503 strcpy (warn_text, input_buffer);
504 else
505 sprintf (warn_text, "%s %s", warn_text, input_buffer);
507 if (strstr (input_buffer, "DUPLICATES FOUND")) {
508 if( !(result == STATE_CRITICAL) ){
509 result = STATE_WARNING;
510 }
511 }
512 else
513 result = STATE_CRITICAL ;
514 }
515 (void) fclose (child_stderr);
518 /* close the pipe - WARNING if status is set */
519 if (spclose (child_process))
520 result = max_state (result, STATE_WARNING);
522 return result;
523 }
526 void
527 print_usage (void)
528 {
529 printf ("Usage:\n" " %s %s\n"
530 " %s (-h | --help) for detailed help\n"
531 " %s (-V | --version) for version information\n",
532 progname, OPTIONS, progname, progname);
533 }
535 void
536 print_help (void)
537 {
538 print_revision (progname, REVISION);
539 printf
540 ("Copyright (c) %s %s <%s>\n\n%s\n",
541 COPYRIGHT, AUTHOR, EMAIL, SUMMARY);
542 print_usage ();
543 printf
544 ("\nOptions:\n" LONGOPTIONS "\n" DESCRIPTION "\n",
545 DEFAULT_MAX_PACKETS, DEFAULT_SOCKET_TIMEOUT);
546 support ();
547 }