Code

accept comma-delimted list of hosts for checking if a multihomed host is alive
[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 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);        
131         
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;
156 \f
158 /* process command-line arguments */
159 int
160 process_arguments (int argc, char **argv)
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 ();
323 int
324 get_threshold (char *arg, float *trta, int *tpl)
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);
336 int
337 validate_arguments ()
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;
381 \f
383 int
384 run_ping (char *command_line, char *server_address)
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                         }
410                         
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;
506 void
507 print_usage (void)
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);
520 void
521 print_help (void)
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 ();