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 #ifdef HAVE_GETOPT_H
166 int option_index = 0;
167 static struct option long_options[] = {
168 STD_LONG_OPTS,
169 {"packets", required_argument, 0, 'p'},
170 {"nohtml", no_argument, 0, 'n'},
171 {"link", no_argument, 0, 'L'},
172 {0, 0, 0, 0}
173 };
174 #endif
176 #define OPTCHARS "VvhnLt:c:w:H:p:"
178 if (argc < 2)
179 return ERROR;
181 for (c = 1; c < argc; c++) {
182 if (strcmp ("-to", argv[c]) == 0)
183 strcpy (argv[c], "-t");
184 if (strcmp ("-nohtml", argv[c]) == 0)
185 strcpy (argv[c], "-n");
186 }
188 while (1) {
189 #ifdef HAVE_GETOPT_H
190 c = getopt_long (argc, argv, OPTCHARS, long_options, &option_index);
191 #else
192 c = getopt (argc, argv, OPTCHARS);
193 #endif
194 if (c == -1 || c == EOF)
195 break;
197 switch (c) {
198 case '?': /* usage */
199 usage3 ("Unknown argument", optopt);
200 case 'h': /* help */
201 print_help ();
202 exit (STATE_OK);
203 case 'V': /* version */
204 print_revision (progname, REVISION);
205 exit (STATE_OK);
206 case 't': /* timeout period */
207 timeout_interval = atoi (optarg);
208 break;
209 case 'v': /* verbose mode */
210 verbose = TRUE;
211 break;
212 case 'H': /* hostname */
213 ptr=optarg;
214 while (1) {
215 n_addresses++;
216 if (n_addresses > max_addr) {
217 max_addr *= 2;
218 addresses = realloc (addresses, max_addr);
219 if (addresses == NULL)
220 terminate (STATE_UNKNOWN, "Could not realloc() addresses\n");
221 }
222 addresses[n_addresses-1] = ptr;
223 if (ptr = index (ptr, ',')) {
224 strcpy (ptr, "");
225 ptr += sizeof(char);
226 } else {
227 break;
228 }
229 }
230 break;
231 case 'p': /* number of packets to send */
232 if (is_intnonneg (optarg))
233 max_packets = atoi (optarg);
234 else
235 usage2 ("<max_packets> (%s) must be a non-negative number\n", optarg);
236 break;
237 case 'n': /* no HTML */
238 display_html = FALSE;
239 break;
240 case 'L': /* show HTML */
241 display_html = TRUE;
242 break;
243 case 'c':
244 get_threshold (optarg, &crta, &cpl);
245 break;
246 case 'w':
247 get_threshold (optarg, &wrta, &wpl);
248 break;
249 }
250 }
252 c = optind;
253 if (c == argc)
254 return validate_arguments ();
256 if (addresses[0] == NULL) {
257 if (is_host (argv[c]) == FALSE) {
258 printf ("Invalid host name/address: %s\n\n", argv[c]);
259 return ERROR;
260 } else {
261 addresses[0] = argv[c++];
262 if (c == argc)
263 return validate_arguments ();
264 }
265 }
267 if (wpl == UNKNOWN_PACKET_LOSS) {
268 if (is_intpercent (argv[c]) == FALSE) {
269 printf ("<wpl> (%s) must be an integer percentage\n", argv[c]);
270 return ERROR;
271 } else {
272 wpl = atoi (argv[c++]);
273 if (c == argc)
274 return validate_arguments ();
275 }
276 }
278 if (cpl == UNKNOWN_PACKET_LOSS) {
279 if (is_intpercent (argv[c]) == FALSE) {
280 printf ("<cpl> (%s) must be an integer percentage\n", argv[c]);
281 return ERROR;
282 } else {
283 cpl = atoi (argv[c++]);
284 if (c == argc)
285 return validate_arguments ();
286 }
287 }
289 if (wrta == UNKNOWN_TRIP_TIME) {
290 if (is_negative (argv[c])) {
291 printf ("<wrta> (%s) must be a non-negative number\n", argv[c]);
292 return ERROR;
293 } else {
294 wrta = atof (argv[c++]);
295 if (c == argc)
296 return validate_arguments ();
297 }
298 }
300 if (crta == UNKNOWN_TRIP_TIME) {
301 if (is_negative (argv[c])) {
302 printf ("<crta> (%s) must be a non-negative number\n", argv[c]);
303 return ERROR;
304 } else {
305 crta = atof (argv[c++]);
306 if (c == argc)
307 return validate_arguments ();
308 }
309 }
311 if (max_packets == -1) {
312 if (is_intnonneg (argv[c])) {
313 max_packets = atoi (argv[c++]);
314 } else {
315 printf ("<max_packets> (%s) must be a non-negative number\n", argv[c]);
316 return ERROR;
317 }
318 }
320 return validate_arguments ();
321 }
323 int
324 get_threshold (char *arg, float *trta, int *tpl)
325 {
326 if (is_intnonneg (arg) && sscanf (arg, "%f", trta) == 1)
327 return OK;
328 else if (strpbrk (arg, ",:") && strstr (arg, "%") && sscanf (arg, "%f%*[:,]%d%%", trta, tpl) == 2)
329 return OK;
330 else if (strstr (arg, "%") && sscanf (arg, "%d%%", tpl) == 1)
331 return OK;
332 else
333 usage2 ("%s: Warning threshold must be integer or percentage!\n\n", arg);
334 }
336 int
337 validate_arguments ()
338 {
339 float max_seconds;
340 int i;
342 if (wrta == UNKNOWN_TRIP_TIME) {
343 printf ("<wrta> was not set\n");
344 return ERROR;
345 }
346 else if (crta == UNKNOWN_TRIP_TIME) {
347 printf ("<crta> was not set\n");
348 return ERROR;
349 }
350 else if (wpl == UNKNOWN_PACKET_LOSS) {
351 printf ("<wpl> was not set\n");
352 return ERROR;
353 }
354 else if (cpl == UNKNOWN_PACKET_LOSS) {
355 printf ("<cpl> was not set\n");
356 return ERROR;
357 }
358 else if (wrta > crta) {
359 printf ("<wrta> (%f) cannot be larger than <crta> (%f)\n", wrta, crta);
360 return ERROR;
361 }
362 else if (wpl > cpl) {
363 printf ("<wpl> (%d) cannot be larger than <cpl> (%d)\n", wpl, cpl);
364 return ERROR;
365 }
367 if (max_packets == -1)
368 max_packets = DEFAULT_MAX_PACKETS;
370 max_seconds = crta / 1000.0 * max_packets + max_packets;
371 if (max_seconds > timeout_interval)
372 timeout_interval = (int)max_seconds;
374 for (i=0; i<n_addresses; i++) {
375 if (is_host(addresses[i]) == FALSE)
376 usage2 ("Invalid host name/address", addresses[i]);
377 }
379 return OK;
380 }
381 \f
383 int
384 run_ping (char *command_line, char *server_address)
385 {
386 char input_buffer[MAX_INPUT_BUFFER];
387 int result = STATE_UNKNOWN;
389 warn_text = malloc (1);
390 if (warn_text == NULL)
391 terminate (STATE_UNKNOWN, "unable to malloc warn_text");
392 warn_text[0] = 0;
394 if ((child_process = spopen (command_line)) == NULL) {
395 printf ("Cannot open pipe: ");
396 terminate (STATE_UNKNOWN, command_line);
397 }
398 child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
399 if (child_stderr == NULL)
400 printf ("Cannot open stderr for %s\n", command_line);
402 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
404 if (strstr (input_buffer, "(DUP!)")) {
405 /* cannot use the max function since STATE_UNKNOWN is max
406 result = max (result, STATE_WARNING); */
407 if( !(result == STATE_CRITICAL) ){
408 result = STATE_WARNING;
409 }
411 warn_text = realloc (warn_text, strlen (WARN_DUPLICATES) + 1);
412 if (warn_text == NULL)
413 terminate (STATE_UNKNOWN, "unable to realloc warn_text");
414 strcpy (warn_text, WARN_DUPLICATES);
415 }
417 /* get the percent loss statistics */
418 if (sscanf
419 (input_buffer, "%*d packets transmitted, %*d packets received, +%*d errors, %d%% packet loss",
420 &pl) == 1
421 || sscanf
422 (input_buffer, "%*d packets transmitted, %*d packets received, %d%% packet loss",
423 &pl) == 1
424 || sscanf
425 (input_buffer, "%*d packets transmitted, %*d packets received, %d%% loss, time", &pl) == 1
426 || sscanf
427 (input_buffer, "%*d packets transmitted, %*d received, %d%% loss, time", &pl) == 1
428 /* Suse 8.0 as reported by Richard * Brodie */
429 )
430 continue;
432 /* get the round trip average */
433 else
434 if (sscanf (input_buffer, "round-trip min/avg/max = %*f/%f/%*f", &rta)
435 == 1
436 || sscanf (input_buffer,
437 "round-trip min/avg/max/mdev = %*f/%f/%*f/%*f",
438 &rta) == 1
439 || sscanf (input_buffer,
440 "round-trip min/avg/max/sdev = %*f/%f/%*f/%*f",
441 &rta) == 1
442 || sscanf (input_buffer,
443 "round-trip min/avg/max/stddev = %*f/%f/%*f/%*f",
444 &rta) == 1
445 || sscanf (input_buffer,
446 "round-trip min/avg/max/std-dev = %*f/%f/%*f/%*f",
447 &rta) == 1
448 || sscanf (input_buffer, "round-trip (ms) min/avg/max = %*f/%f/%*f",
449 &rta) == 1
450 || sscanf (input_buffer, "rtt min/avg/max/mdev = %*f/%f/%*f/%*f ms",
451 &rta) == 1
452 )
453 continue;
454 }
456 /* this is needed because there is no rta if all packets are lost */
457 if (pl == 100)
458 rta = crta;
461 /* check stderr */
462 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
463 if (strstr
464 (input_buffer,
465 "Warning: no SO_TIMESTAMP support, falling back to SIOCGSTAMP"))
466 continue;
468 if (strstr (input_buffer, "Network is unreachable"))
469 terminate (STATE_CRITICAL, "PING CRITICAL - Network unreachable (%s)",
470 server_address);
471 else if (strstr (input_buffer, "Destination Host Unreachable"))
472 terminate (STATE_CRITICAL, "PING CRITICAL - Host Unreachable (%s)",
473 server_address);
474 else if (strstr (input_buffer, "unknown host" ) )
475 terminate (STATE_CRITICAL, "PING CRITICAL - Host not found (%s)",
476 server_address);
478 warn_text =
479 realloc (warn_text, strlen (warn_text) + strlen (input_buffer) + 2);
480 if (warn_text == NULL)
481 terminate (STATE_UNKNOWN, "unable to realloc warn_text");
482 if (strlen (warn_text) == 0)
483 strcpy (warn_text, input_buffer);
484 else
485 sprintf (warn_text, "%s %s", warn_text, input_buffer);
487 if (strstr (input_buffer, "DUPLICATES FOUND")) {
488 if( !(result == STATE_CRITICAL) ){
489 result = STATE_WARNING;
490 }
491 }
492 else
493 result = STATE_CRITICAL ;
494 }
495 (void) fclose (child_stderr);
498 /* close the pipe - WARNING if status is set */
499 if (spclose (child_process))
500 result = max_state (result, STATE_WARNING);
502 return result;
503 }
506 void
507 print_usage (void)
508 {
509 printf ("Usage:\n" " %s %s\n"
510 #ifdef HAVE_GETOPT_H
511 " %s (-h | --help) for detailed help\n"
512 " %s (-V | --version) for version information\n",
513 #else
514 " %s -h for detailed help\n"
515 " %s -V for version information\n",
516 #endif
517 progname, OPTIONS, progname, progname);
518 }
520 void
521 print_help (void)
522 {
523 print_revision (progname, REVISION);
524 printf
525 ("Copyright (c) %s %s <%s>\n\n%s\n",
526 COPYRIGHT, AUTHOR, EMAIL, SUMMARY);
527 print_usage ();
528 printf
529 ("\nOptions:\n" LONGOPTIONS "\n" DESCRIPTION "\n",
530 DEFAULT_MAX_PACKETS, DEFAULT_SOCKET_TIMEOUT);
531 support ();
532 }