Code

Added -4 and -6 command line options into check_http, check_ldap and
[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] [-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);        
150         
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;
175 \f
177 /* process command-line arguments */
178 int
179 process_arguments (int argc, char **argv)
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 ();
347 int
348 get_threshold (char *arg, float *trta, int *tpl)
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);
360 int
361 validate_arguments ()
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;
405 \f
407 int
408 run_ping (char *command_line, char *server_address)
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                         }
434                         
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;
530 void
531 print_usage (void)
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);
539 void
540 print_help (void)
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 ();