Code

check_ping is now coded with -4 & -6 options to call PING or PING6 command
[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                         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 ();
343 int
344 get_threshold (char *arg, float *trta, int *tpl)
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);
356 int
357 validate_arguments ()
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;
401 \f
403 int
404 run_ping (char *command_line, char *server_address)
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                         }
430                         
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;
526 void
527 print_usage (void)
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);
535 void
536 print_help (void)
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 ();