Code

Modified check_ping to handle IPv6 as well as IPv4 ICMP checks using the
[nagiosplug.git] / plugins / check_ping.c
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 PING6_COMMAND
108 # ifdef PING_PACKETS_FIRST
109         if (is_inet6_addr(addresses[i]))
110                 asprintf (&command_line, PING6_COMMAND, max_packets, addresses[i]);
111         else
112                 asprintf (&command_line, PING_COMMAND, max_packets, addresses[i]);
113 # else
114         if (is_inet6_addr(addresses[i]))
115                 asprintf (&command_line, PING6_COMMAND, addresses[i], max_packets);
116         else
117                 asprintf (&command_line, PING_COMMAND, addresses[i], max_packets);
118 # endif
119 #else /* USE_IPV6 */
120 # ifdef PING_PACKETS_FIRST
121                 asprintf (&command_line, PING_COMMAND, max_packets, addresses[i]);
122 # else
123                 asprintf (&command_line, PING_COMMAND, addresses[i], max_packets);
124 # endif
125 #endif /* USE_IPV6 */
127                 if (verbose)
128                         printf ("%s ==> ", command_line);
130                 /* run the command */
131                 this_result = run_ping (command_line, addresses[i]);
133                 if (pl == UNKNOWN_PACKET_LOSS || rta == UNKNOWN_TRIP_TIME) {
134                         printf ("%s\n", command_line);
135                         terminate (STATE_UNKNOWN,
136                                                                  "Error: Could not interpret output from ping command\n");
137                 }
139                 if (pl >= cpl || rta >= crta || rta < 0)
140                         this_result = STATE_CRITICAL;
141                 else if (pl >= wpl || rta >= wrta)
142                         this_result = STATE_WARNING;
143                 else if (pl >= 0 && rta >= 0)
144                         this_result = max_state (STATE_OK, this_result);        
145         
146                 if (n_addresses > 1 && this_result != STATE_UNKNOWN)
147                         terminate (STATE_OK, "%s is alive\n", addresses[i]);
149                 if (display_html == TRUE)
150                         printf ("<A HREF='%s/traceroute.cgi?%s'>", CGIURL, addresses[i]);
151                 if (pl == 100)
152                         printf ("PING %s - %sPacket loss = %d%%", state_text (this_result), warn_text,
153                                                         pl);
154                 else
155                         printf ("PING %s - %sPacket loss = %d%%, RTA = %2.2f ms",
156                                                         state_text (this_result), warn_text, pl, rta);
157                 if (display_html == TRUE)
158                         printf ("</A>");
159                 printf ("\n");
161                 if (verbose)
162                         printf ("%f:%d%% %f:%d%%\n", wrta, wpl, crta, cpl);
164                 result = max_state (result, this_result);
166         }
168         return result;
170 \f
172 /* process command-line arguments */
173 int
174 process_arguments (int argc, char **argv)
176         int c = 1;
177         char *ptr;
179         int option_index = 0;
180         static struct option long_options[] = {
181                 STD_LONG_OPTS,
182                 {"packets", required_argument, 0, 'p'},
183                 {"nohtml", no_argument, 0, 'n'},
184                 {"link", no_argument, 0, 'L'},
185                 {0, 0, 0, 0}
186         };
188         if (argc < 2)
189                 return ERROR;
191         for (c = 1; c < argc; c++) {
192                 if (strcmp ("-to", argv[c]) == 0)
193                         strcpy (argv[c], "-t");
194                 if (strcmp ("-nohtml", argv[c]) == 0)
195                         strcpy (argv[c], "-n");
196         }
198         while (1) {
199                 c = getopt_long (argc, argv, "VvhnLt:c:w:H:p:", long_options, &option_index);
201                 if (c == -1 || c == EOF)
202                         break;
204                 switch (c) {
205                 case '?':       /* usage */
206                         usage3 ("Unknown argument", optopt);
207                 case 'h':       /* help */
208                         print_help ();
209                         exit (STATE_OK);
210                 case 'V':       /* version */
211                         print_revision (progname, REVISION);
212                         exit (STATE_OK);
213                 case 't':       /* timeout period */
214                         timeout_interval = atoi (optarg);
215                         break;
216                 case 'v':       /* verbose mode */
217                         verbose = TRUE;
218                         break;
219                 case 'H':       /* hostname */
220                         ptr=optarg;
221                         while (1) {
222                                 n_addresses++;
223                                 if (n_addresses > max_addr) {
224                                         max_addr *= 2;
225                                         addresses = realloc (addresses, max_addr);
226                                         if (addresses == NULL)
227                                                 terminate (STATE_UNKNOWN, "Could not realloc() addresses\n");
228                                 }
229                                 addresses[n_addresses-1] = ptr;
230                                 if (ptr = index (ptr, ',')) {
231                                         strcpy (ptr, "");
232                                         ptr += sizeof(char);
233                                 } else {
234                                         break;
235                                 }
236                         }
237                         break;
238                 case 'p':       /* number of packets to send */
239                         if (is_intnonneg (optarg))
240                                 max_packets = atoi (optarg);
241                         else
242                                 usage2 ("<max_packets> (%s) must be a non-negative number\n", optarg);
243                         break;
244                 case 'n':       /* no HTML */
245                         display_html = FALSE;
246                         break;
247                 case 'L':       /* show HTML */
248                         display_html = TRUE;
249                         break;
250                 case 'c':
251                         get_threshold (optarg, &crta, &cpl);
252                         break;
253                 case 'w':
254                         get_threshold (optarg, &wrta, &wpl);
255                         break;
256                 }
257         }
259         c = optind;
260         if (c == argc)
261                 return validate_arguments ();
263         if (addresses[0] == NULL) {
264                 if (is_host (argv[c]) == FALSE) {
265                         printf ("Invalid host name/address: %s\n\n", argv[c]);
266                         return ERROR;
267                 } else {
268                         addresses[0] = argv[c++];
269                         if (c == argc)
270                                 return validate_arguments ();
271                 }
272         }
274         if (wpl == UNKNOWN_PACKET_LOSS) {
275                 if (is_intpercent (argv[c]) == FALSE) {
276                         printf ("<wpl> (%s) must be an integer percentage\n", argv[c]);
277                         return ERROR;
278                 } else {
279                         wpl = atoi (argv[c++]);
280                         if (c == argc)
281                                 return validate_arguments ();
282                 }
283         }
285         if (cpl == UNKNOWN_PACKET_LOSS) {
286                 if (is_intpercent (argv[c]) == FALSE) {
287                         printf ("<cpl> (%s) must be an integer percentage\n", argv[c]);
288                         return ERROR;
289                 } else {
290                         cpl = atoi (argv[c++]);
291                         if (c == argc)
292                                 return validate_arguments ();
293                 }
294         }
296         if (wrta == UNKNOWN_TRIP_TIME) {
297                 if (is_negative (argv[c])) {
298                         printf ("<wrta> (%s) must be a non-negative number\n", argv[c]);
299                         return ERROR;
300                 } else {
301                         wrta = atof (argv[c++]);
302                         if (c == argc)
303                                 return validate_arguments ();
304                 }
305         }
307         if (crta == UNKNOWN_TRIP_TIME) {
308                 if (is_negative (argv[c])) {
309                         printf ("<crta> (%s) must be a non-negative number\n", argv[c]);
310                         return ERROR;
311                 } else {
312                         crta = atof (argv[c++]);
313                         if (c == argc)
314                                 return validate_arguments ();
315                 }
316         }
318         if (max_packets == -1) {
319                 if (is_intnonneg (argv[c])) {
320                         max_packets = atoi (argv[c++]);
321                 }       else {
322                         printf ("<max_packets> (%s) must be a non-negative number\n", argv[c]);
323                         return ERROR;
324                 }
325         }
327         return validate_arguments ();
330 int
331 get_threshold (char *arg, float *trta, int *tpl)
333         if (is_intnonneg (arg) && sscanf (arg, "%f", trta) == 1)
334                 return OK;
335         else if (strpbrk (arg, ",:") && strstr (arg, "%") && sscanf (arg, "%f%*[:,]%d%%", trta, tpl) == 2)
336                 return OK;
337         else if (strstr (arg, "%") && sscanf (arg, "%d%%", tpl) == 1) 
338                 return OK;
339         else
340                 usage2 ("%s: Warning threshold must be integer or percentage!\n\n", arg);
343 int
344 validate_arguments ()
346         float max_seconds;
347         int i;
349         if (wrta == UNKNOWN_TRIP_TIME) {
350                 printf ("<wrta> was not set\n");
351                 return ERROR;
352         }
353         else if (crta == UNKNOWN_TRIP_TIME) {
354                 printf ("<crta> was not set\n");
355                 return ERROR;
356         }
357         else if (wpl == UNKNOWN_PACKET_LOSS) {
358                 printf ("<wpl> was not set\n");
359                 return ERROR;
360         }
361         else if (cpl == UNKNOWN_PACKET_LOSS) {
362                 printf ("<cpl> was not set\n");
363                 return ERROR;
364         }
365         else if (wrta > crta) {
366                 printf ("<wrta> (%f) cannot be larger than <crta> (%f)\n", wrta, crta);
367                 return ERROR;
368         }
369         else if (wpl > cpl) {
370                 printf ("<wpl> (%d) cannot be larger than <cpl> (%d)\n", wpl, cpl);
371                 return ERROR;
372         }
374         if (max_packets == -1)
375                 max_packets = DEFAULT_MAX_PACKETS;
377         max_seconds = crta / 1000.0 * max_packets + max_packets;
378         if (max_seconds > timeout_interval)
379                 timeout_interval = (int)max_seconds;
381         for (i=0; i<n_addresses; i++) {
382                 if (is_host(addresses[i]) == FALSE)
383                         usage2 ("Invalid host name/address", addresses[i]);
384         }
386         return OK;
388 \f
390 int
391 run_ping (char *command_line, char *server_address)
393         char input_buffer[MAX_INPUT_BUFFER];
394         int result = STATE_UNKNOWN;
396         warn_text = malloc (1);
397         if (warn_text == NULL)
398                 terminate (STATE_UNKNOWN, "unable to malloc warn_text");
399         warn_text[0] = 0;
401         if ((child_process = spopen (command_line)) == NULL) {
402                 printf ("Cannot open pipe: ");
403                 terminate (STATE_UNKNOWN, command_line);
404         }
405         child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
406         if (child_stderr == NULL)
407                 printf ("Cannot open stderr for %s\n", command_line);
409         while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
411                 if (strstr (input_buffer, "(DUP!)")) {
412                         /* cannot use the max function since STATE_UNKNOWN is max
413                         result = max (result, STATE_WARNING); */
414                         if( !(result == STATE_CRITICAL) ){
415                                 result = STATE_WARNING;
416                         }
417                         
418                         warn_text = realloc (warn_text, strlen (WARN_DUPLICATES) + 1);
419                         if (warn_text == NULL)
420                                 terminate (STATE_UNKNOWN, "unable to realloc warn_text");
421                         strcpy (warn_text, WARN_DUPLICATES);
422                 }
424                 /* get the percent loss statistics */
425                 if (sscanf
426                                         (input_buffer, "%*d packets transmitted, %*d packets received, +%*d errors, %d%% packet loss",
427                                                  &pl) == 1
428                                 || sscanf 
429                                         (input_buffer, "%*d packets transmitted, %*d packets received, %d%% packet loss",
430                                                 &pl) == 1
431                                 || sscanf 
432                                         (input_buffer, "%*d packets transmitted, %*d packets received, %d%% loss, time", &pl) == 1
433                                 || sscanf
434                                         (input_buffer, "%*d packets transmitted, %*d received, %d%% loss, time", &pl) == 1
435                                         /* Suse 8.0 as reported by Richard * Brodie */
436                                 )
437                         continue;
439                 /* get the round trip average */
440                 else
441                         if (sscanf (input_buffer, "round-trip min/avg/max = %*f/%f/%*f", &rta)
442                                         == 1
443                                         || sscanf (input_buffer,
444                                                                                  "round-trip min/avg/max/mdev = %*f/%f/%*f/%*f",
445                                                                                  &rta) == 1
446                                         || sscanf (input_buffer,
447                                                                                  "round-trip min/avg/max/sdev = %*f/%f/%*f/%*f",
448                                                                                  &rta) == 1
449                                         || sscanf (input_buffer,
450                                                                                  "round-trip min/avg/max/stddev = %*f/%f/%*f/%*f",
451                                                                                  &rta) == 1
452                                         || sscanf (input_buffer,
453                                                                                  "round-trip min/avg/max/std-dev = %*f/%f/%*f/%*f",
454                                                                                  &rta) == 1
455                                         || sscanf (input_buffer, "round-trip (ms) min/avg/max = %*f/%f/%*f",
456                                                                                  &rta) == 1
457                                         || sscanf (input_buffer, "rtt min/avg/max/mdev = %*f/%f/%*f/%*f ms",
458                                                                                  &rta) == 1
459                                                                                 )
460                         continue;
461         }
463         /* this is needed because there is no rta if all packets are lost */
464         if (pl == 100)
465                 rta = crta;
468         /* check stderr */
469         while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
470                 if (strstr
471                                 (input_buffer,
472                                  "Warning: no SO_TIMESTAMP support, falling back to SIOCGSTAMP"))
473                                 continue;
475                 if (strstr (input_buffer, "Network is unreachable"))
476                         terminate (STATE_CRITICAL, "PING CRITICAL - Network unreachable (%s)",
477                                                                  server_address);
478                 else if (strstr (input_buffer, "Destination Host Unreachable"))
479                         terminate (STATE_CRITICAL, "PING CRITICAL - Host Unreachable (%s)",
480                                                                  server_address);
481                 else if (strstr (input_buffer, "unknown host" ) )
482                         terminate (STATE_CRITICAL, "PING CRITICAL - Host not found (%s)",
483                                                                 server_address);
485                 warn_text =
486                         realloc (warn_text, strlen (warn_text) + strlen (input_buffer) + 2);
487                 if (warn_text == NULL)
488                         terminate (STATE_UNKNOWN, "unable to realloc warn_text");
489                 if (strlen (warn_text) == 0)
490                         strcpy (warn_text, input_buffer);
491                 else
492                         sprintf (warn_text, "%s %s", warn_text, input_buffer);
494                 if (strstr (input_buffer, "DUPLICATES FOUND")) {
495                         if( !(result == STATE_CRITICAL) ){
496                                 result = STATE_WARNING;
497                         }
498                 }
499                 else
500                         result = STATE_CRITICAL ;
501         }
502         (void) fclose (child_stderr);
505         /* close the pipe - WARNING if status is set */
506         if (spclose (child_process))
507                 result = max_state (result, STATE_WARNING);
509         return result;
513 void
514 print_usage (void)
516         printf ("Usage:\n" " %s %s\n"
517                                         " %s (-h | --help) for detailed help\n"
518                                         " %s (-V | --version) for version information\n",
519                                         progname, OPTIONS, progname, progname);
522 void
523 print_help (void)
525         print_revision (progname, REVISION);
526         printf
527                 ("Copyright (c) %s %s <%s>\n\n%s\n",
528                  COPYRIGHT, AUTHOR, EMAIL, SUMMARY);
529         print_usage ();
530         printf
531                 ("\nOptions:\n" LONGOPTIONS "\n" DESCRIPTION "\n", 
532                  DEFAULT_MAX_PACKETS, DEFAULT_SOCKET_TIMEOUT);
533         support ();