Code

fixes for using POSIX return codes
[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 #define 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 call_getopt (int, char **);
60 int get_threshold (char *, float *, int *);
61 int validate_arguments (void);
62 int run_ping (char *);
63 void print_usage (void);
64 void print_help (void);
66 int display_html = FALSE;
67 int wpl = UNKNOWN_PACKET_LOSS;
68 int cpl = UNKNOWN_PACKET_LOSS;
69 float wrta = UNKNOWN_TRIP_TIME;
70 float crta = UNKNOWN_TRIP_TIME;
71 char *server_address = NULL;
72 int max_packets = -1;
73 int verbose = FALSE;
75 float rta = UNKNOWN_TRIP_TIME;
76 int pl = UNKNOWN_PACKET_LOSS;
78 char *warn_text = NULL;
80 int
81 main (int argc, char **argv)
82 {
83         char *command_line = NULL;
84         int result = STATE_UNKNOWN;
86         if (process_arguments (argc, argv) == ERROR)
87                 usage ("Could not parse arguments");
89         /* does the host address of number of packets argument come first? */
90 #ifdef PING_PACKETS_FIRST
91         command_line =
92                 ssprintf (command_line, PING_COMMAND, max_packets, server_address);
93 #else
94         command_line =
95                 ssprintf (command_line, PING_COMMAND, server_address, max_packets);
96 #endif
98         /* Set signal handling and alarm */
99         if (signal (SIGALRM, popen_timeout_alarm_handler) == SIG_ERR) {
100                 printf ("Cannot catch SIGALRM");
101                 return STATE_UNKNOWN;
102         }
104         /* handle timeouts gracefully */
105         alarm (timeout_interval);
107         if (verbose)
108                 printf ("%s ==> ", command_line);
110         /* run the command */
111         run_ping (command_line);
113         if (pl == UNKNOWN_PACKET_LOSS || rta == UNKNOWN_TRIP_TIME) {
114                 printf ("%s\n", command_line);
115                 terminate (STATE_UNKNOWN,
116                                                          "Error: Could not interpret output from ping command\n");
117         }
119         if (pl >= cpl || rta >= crta || rta < 0)
120                 result = STATE_CRITICAL;
121         else if (pl >= wpl || rta >= wrta)
122                 result = STATE_WARNING;
123         else if (pl < wpl && rta < wrta && pl >= 0 && rta >= 0)
124                 /* cannot use the max function because STATE_UNKNOWN is now 3 gt STATE_OK                       
125                 result = max (result, STATE_OK);  */
126                 if( !( (result == STATE_WARNING) || (result == STATE_CRITICAL) )  ) {
127                         result = STATE_OK;      
128                 }
129         
130         if (display_html == TRUE)
131                 printf ("<A HREF='%s/traceroute.cgi?%s'>", CGIURL, server_address);
132         if (pl == 100)
133                 printf ("PING %s - %sPacket loss = %d%%", state_text (result), warn_text,
134                                                 pl);
135         else
136                 printf ("PING %s - %sPacket loss = %d%%, RTA = %2.2f ms",
137                                                 state_text (result), warn_text, pl, rta);
138         if (display_html == TRUE)
139                 printf ("</A>");
140         printf ("\n");
142         if (verbose)
143                 printf ("%f:%d%% %f:%d%%\n", wrta, wpl, crta, cpl);
145         return result;
147 \f
149 /* process command-line arguments */
150 int
151 process_arguments (int argc, char **argv)
153         int c;
155         if (argc < 2)
156                 return ERROR;
158         for (c = 1; c < argc; c++) {
159                 if (strcmp ("-to", argv[c]) == 0)
160                         strcpy (argv[c], "-t");
161                 if (strcmp ("-nohtml", argv[c]) == 0)
162                         strcpy (argv[c], "-n");
163         }
165         c = 0;
166         while ((c += call_getopt (argc - c, &argv[c])) < argc) {
168                 if (is_option (argv[c]))
169                         continue;
171                 if (server_address == NULL) {
172                         if (is_host (argv[c]) == FALSE) {
173                                 printf ("Invalid host name/address: %s\n\n", argv[c]);
174                                 return ERROR;
175                         }
176                         server_address = argv[c];
177                 }
178                 else if (wpl == UNKNOWN_PACKET_LOSS) {
179                         if (is_intpercent (argv[c]) == FALSE) {
180                                 printf ("<wpl> (%s) must be an integer percentage\n", argv[c]);
181                                 return ERROR;
182                         }
183                         wpl = atoi (argv[c]);
184                 }
185                 else if (cpl == UNKNOWN_PACKET_LOSS) {
186                         if (is_intpercent (argv[c]) == FALSE) {
187                                 printf ("<cpl> (%s) must be an integer percentage\n", argv[c]);
188                                 return ERROR;
189                         }
190                         cpl = atoi (argv[c]);
191                 }
192                 else if (wrta == UNKNOWN_TRIP_TIME) {
193                         if (is_negative (argv[c])) {
194                                 printf ("<wrta> (%s) must be a non-negative number\n", argv[c]);
195                                 return ERROR;
196                         }
197                         wrta = atof (argv[c]);
198                 }
199                 else if (crta == UNKNOWN_TRIP_TIME) {
200                         if (is_negative (argv[c])) {
201                                 printf ("<crta> (%s) must be a non-negative number\n", argv[c]);
202                                 return ERROR;
203                         }
204                         crta = atof (argv[c]);
205                 }
206                 else if (max_packets == -1) {
207                         if (is_intnonneg (argv[c])) {
208                                 max_packets = atoi (argv[c]);
209                         }
210                         else {
211                                 printf ("<max_packets> (%s) must be a non-negative number\n",
212                                                                 argv[c]);
213                                 return ERROR;
214                         }
215                 }
217         }
219         return validate_arguments ();
222 int
223 call_getopt (int argc, char **argv)
225         int c, i = 0;
227 #ifdef HAVE_GETOPT_H
228         int option_index = 0;
229         static struct option long_options[] = {
230                 {"help", no_argument, 0, 'h'},
231                 {"version", no_argument, 0, 'V'},
232                 {"verbose", no_argument, 0, 'v'},
233                 {"nohtml", no_argument, 0, 'n'},
234                 {"link", no_argument, 0, 'L'},
235                 {"timeout", required_argument, 0, 't'},
236                 {"critical", required_argument, 0, 'c'},
237                 {"warning", required_argument, 0, 'w'},
238                 {"hostname", required_argument, 0, 'H'},
239                 {"packets", required_argument, 0, 'p'},
240                 {0, 0, 0, 0}
241         };
242 #endif
244         while (1) {
245 #ifdef HAVE_GETOPT_H
246                 c =
247                         getopt_long (argc, argv, "+hVvt:c:w:H:p:nL", long_options,
248                                                                          &option_index);
249 #else
250                 c = getopt (argc, argv, "+hVvt:c:w:H:p:nL");
251 #endif
253                 i++;
255                 if (c == -1 || c == EOF || c == 1)
256                         break;
258                 switch (c) {
259                 case 't':
260                 case 'c':
261                 case 'w':
262                 case 'H':
263                 case 'p':
264                         i++;
265                 }
267                 switch (c) {
268                 case '?':                                                                       /* print short usage statement if args not parsable */
269                         usage2 ("Unknown argument", optarg);
270                 case 'h':                                                                       /* help */
271                         print_help ();
272                         exit (STATE_OK);
273                 case 'V':                                                                       /* version */
274                         print_revision (PROGNAME, REVISION);
275                         exit (STATE_OK);
276                 case 't':                                                                       /* timeout period */
277                         timeout_interval = atoi (optarg);
278                         break;
279                 case 'v':                                                                       /* verbose mode */
280                         verbose = TRUE;
281                         break;
282                 case 'H':                                                                       /* hostname */
283                         if (is_host (optarg) == FALSE)
284                                 usage2 ("Invalid host name/address", optarg);
285                         server_address = optarg;
286                         break;
287                 case 'p':                                                                       /* number of packets to send */
288                         if (is_intnonneg (optarg))
289                                 max_packets = atoi (optarg);
290                         else
291                                 usage2 ("<max_packets> (%s) must be a non-negative number\n", optarg);
292                         break;
293                 case 'n':                                                                       /* no HTML */
294                         display_html = FALSE;
295                         break;
296                 case 'L':                                                                       /* show HTML */
297                         display_html = TRUE;
298                         break;
299                 case 'c':
300                         get_threshold (optarg, &crta, &cpl);
301                         break;
302                 case 'w':
303                         get_threshold (optarg, &wrta, &wpl);
304                         break;
305                 }
306         }
308         return i;
311 int
312 get_threshold (char *arg, float *trta, int *tpl)
314         if (is_intnonneg (arg) && sscanf (arg, "%f", trta) == 1)
315                 return OK;
316         else if (strpbrk (arg, ",:") && strstr (arg, "%") && sscanf (arg, "%f%*[:,]%d%%", trta, tpl) == 2)
317                 return OK;
318         else if (strstr (arg, "%") && sscanf (arg, "%d%%", tpl) == 1) 
319                 return OK;
320         else
321                 usage2 ("%s: Warning threshold must be integer or percentage!\n\n", arg);
325 int
326 validate_arguments ()
328         float max_seconds;
330         if (wrta == UNKNOWN_TRIP_TIME) {
331                 printf ("<wrta> was not set\n");
332                 return ERROR;
333         }
334         else if (crta == UNKNOWN_TRIP_TIME) {
335                 printf ("<crta> was not set\n");
336                 return ERROR;
337         }
338         else if (wpl == UNKNOWN_PACKET_LOSS) {
339                 printf ("<wpl> was not set\n");
340                 return ERROR;
341         }
342         else if (cpl == UNKNOWN_PACKET_LOSS) {
343                 printf ("<cpl> was not set\n");
344                 return ERROR;
345         }
346         else if (wrta > crta) {
347                 printf ("<wrta> (%f) cannot be larger than <crta> (%f)\n", wrta, crta);
348                 return ERROR;
349         }
350         else if (wpl > cpl) {
351                 printf ("<wpl> (%d) cannot be larger than <cpl> (%d)\n", wpl, cpl);
352                 return ERROR;
353         }
355         if (max_packets == -1)
356                 max_packets = DEFAULT_MAX_PACKETS;
358         max_seconds = crta / 1000.0 * max_packets + max_packets;
359         if (max_seconds > timeout_interval)
360                 timeout_interval = (int)max_seconds;
362         return OK;
364 \f
366 int
367 run_ping (char *command_line)
369         char input_buffer[MAX_INPUT_BUFFER];
370         int result = STATE_UNKNOWN;
372         warn_text = malloc (1);
373         if (warn_text == NULL)
374                 terminate (STATE_UNKNOWN, "unable to malloc warn_text");
375         warn_text[0] = 0;
377         if ((child_process = spopen (command_line)) == NULL) {
378                 printf ("Cannot open pipe: ");
379                 terminate (STATE_UNKNOWN, command_line);
380         }
381         child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
382         if (child_stderr == NULL)
383                 printf ("Cannot open stderr for %s\n", command_line);
385         while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
387                 if (strstr (input_buffer, "(DUP!)")) {
388                         /* cannot use the max function since STATE_UNKNOWN is max
389                         result = max (result, STATE_WARNING); */
390                         if( !(result == STATE_CRITICAL) ){
391                                 result = STATE_WARNING;
392                         }
393                         
394                         warn_text = realloc (warn_text, strlen (WARN_DUPLICATES) + 1);
395                         if (warn_text == NULL)
396                                 terminate (STATE_UNKNOWN, "unable to realloc warn_text");
397                         strcpy (warn_text, WARN_DUPLICATES);
398                 }
400                 /* get the percent loss statistics */
401                 if (sscanf
402                                         (input_buffer, "%*d packets transmitted, %*d packets received, +%*d errors, %d%% packet loss",
403                                                  &pl) == 1
404                                 || sscanf 
405                                         (input_buffer, "%*d packets transmitted, %*d packets received, %d%% packet loss",
406                                                 &pl) == 1
407                                 || sscanf 
408                                         (input_buffer, "%*d packets transmitted, %*d packets received, %d%% loss, time", &pl) == 1
409                                 || sscanf
410                                         (input_buffer, "%*d packets transmitted, %*d received, %d%% loss, time", &pl) == 1
411                                         /* Suse 8.0 as reported by Richard * Brodie */
412                                 )
413                         continue;
415                 /* get the round trip average */
416                 else
417                         if (sscanf (input_buffer, "round-trip min/avg/max = %*f/%f/%*f", &rta)
418                                         == 1
419                                         || sscanf (input_buffer,
420                                                                                  "round-trip min/avg/max/mdev = %*f/%f/%*f/%*f",
421                                                                                  &rta) == 1
422                                         || sscanf (input_buffer,
423                                                                                  "round-trip min/avg/max/sdev = %*f/%f/%*f/%*f",
424                                                                                  &rta) == 1
425                                         || sscanf (input_buffer,
426                                                                                  "round-trip min/avg/max/stddev = %*f/%f/%*f/%*f",
427                                                                                  &rta) == 1
428                                         || sscanf (input_buffer,
429                                                                                  "round-trip min/avg/max/std-dev = %*f/%f/%*f/%*f",
430                                                                                  &rta) == 1
431                                         || sscanf (input_buffer, "round-trip (ms) min/avg/max = %*f/%f/%*f",
432                                                                                  &rta) == 1
433                                         || sscanf (input_buffer, "rtt min/avg/max/mdev = %*f/%f/%*f/%*f ms",
434                                                                                  &rta) == 1
435                                                                                 )
436                         continue;
437         }
439         /* this is needed because there is no rta if all packets are lost */
440         if (pl == 100)
441                 rta = crta;
444         /* check stderr */
445         while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
446                 if (strstr
447                                 (input_buffer,
448                                  "Warning: no SO_TIMESTAMP support, falling back to SIOCGSTAMP"))
449                                 continue;
451                 if (strstr (input_buffer, "Network is unreachable"))
452                         terminate (STATE_CRITICAL, "PING CRITICAL - Network unreachable (%s)",
453                                                                  server_address);
454                 else if (strstr (input_buffer, "Destination Host Unreachable"))
455                         terminate (STATE_CRITICAL, "PING CRITICAL - Host Unreachable (%s)",
456                                                                  server_address);
457                 else if (strstr (input_buffer, "unknown host" ) )
458                         terminate (STATE_CRITICAL, "PING CRITICAL - Host not found (%s)",
459                                                                 server_address);
461                 warn_text =
462                         realloc (warn_text, strlen (warn_text) + strlen (input_buffer) + 2);
463                 if (warn_text == NULL)
464                         terminate (STATE_UNKNOWN, "unable to realloc warn_text");
465                 if (strlen (warn_text) == 0)
466                         strcpy (warn_text, input_buffer);
467                 else
468                         sprintf (warn_text, "%s %s", warn_text, input_buffer);
470                 if (strstr (input_buffer, "DUPLICATES FOUND"))
471                         /* cannot use the max function since STATE_UNKNOWN is max
472                         result = max (result, STATE_WARNING); */
473                         if( !(result == STATE_CRITICAL) ){
474                                 result = STATE_WARNING;
475                         }
476                 else
477                         /* cannot use the max function since STATE_UNKNOWN is max
478                         result = max (result, STATE_CRITICAL); */
479                         result = STATE_CRITICAL ;
480         }
481         (void) fclose (child_stderr);
484         /* close the pipe - WARNING if status is set */
485         if (spclose (child_process))
486                 result = max (result, STATE_WARNING);
488         return result;
492 void
493 print_usage (void)
495         printf ("Usage:\n" " %s %s\n"
496 #ifdef HAVE_GETOPT_H
497                                         " %s (-h | --help) for detailed help\n"
498                                         " %s (-V | --version) for version information\n",
499 #else
500                                         " %s -h for detailed help\n"
501                                         " %s -V for version information\n",
502 #endif
503                                         PROGNAME, OPTIONS, PROGNAME, PROGNAME);
506 void
507 print_help (void)
509         print_revision (PROGNAME, REVISION);
510         printf
511                 ("Copyright (c) %s %s <%s>\n\n%s\n",
512                  COPYRIGHT, AUTHOR, EMAIL, SUMMARY);
513         print_usage ();
514         printf
515                 ("\nOptions:\n" LONGOPTIONS "\n" DESCRIPTION "\n", 
516                  DEFAULT_MAX_PACKETS, DEFAULT_SOCKET_TIMEOUT);
517         support ();