Code

3602122c28abd4a6288e266bc4f5f79b8c21380b
[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 get_threshold (char *, float *, int *);
60 int validate_arguments (void);
61 int run_ping (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 *server_address = NULL;
71 int max_packets = -1;
72 int verbose = FALSE;
74 float rta = UNKNOWN_TRIP_TIME;
75 int pl = UNKNOWN_PACKET_LOSS;
77 char *warn_text = NULL;
79 int
80 main (int argc, char **argv)
81 {
82         char *command_line = NULL;
83         int result = STATE_UNKNOWN;
85         if (process_arguments (argc, argv) == ERROR)
86                 usage ("Could not parse arguments");
87         exit;
89         /* does the host address of number of packets argument come first? */
90 #ifdef PING_PACKETS_FIRST
91         asprintf (&command_line, PING_COMMAND, max_packets, server_address);
92 #else
93         asprintf (&command_line, PING_COMMAND, server_address, max_packets);
94 #endif
96         /* Set signal handling and alarm */
97         if (signal (SIGALRM, popen_timeout_alarm_handler) == SIG_ERR) {
98                 printf ("Cannot catch SIGALRM");
99                 return STATE_UNKNOWN;
100         }
102         /* handle timeouts gracefully */
103         alarm (timeout_interval);
105         if (verbose)
106                 printf ("%s ==> ", command_line);
108         /* run the command */
109         run_ping (command_line);
111         if (pl == UNKNOWN_PACKET_LOSS || rta == UNKNOWN_TRIP_TIME) {
112                 printf ("%s\n", command_line);
113                 terminate (STATE_UNKNOWN,
114                                                          "Error: Could not interpret output from ping command\n");
115         }
117         if (pl >= cpl || rta >= crta || rta < 0)
118                 result = STATE_CRITICAL;
119         else if (pl >= wpl || rta >= wrta)
120                 result = STATE_WARNING;
121         else if (pl < wpl && rta < wrta && pl >= 0 && rta >= 0)
122                 /* cannot use the max function because STATE_UNKNOWN is now 3 gt STATE_OK                       
123                 result = max (result, STATE_OK);  */
124                 if( !( (result == STATE_WARNING) || (result == STATE_CRITICAL) )  ) {
125                         result = STATE_OK;      
126                 }
127         
128         if (display_html == TRUE)
129                 printf ("<A HREF='%s/traceroute.cgi?%s'>", CGIURL, server_address);
130         if (pl == 100)
131                 printf ("PING %s - %sPacket loss = %d%%", state_text (result), warn_text,
132                                                 pl);
133         else
134                 printf ("PING %s - %sPacket loss = %d%%, RTA = %2.2f ms",
135                                                 state_text (result), warn_text, pl, rta);
136         if (display_html == TRUE)
137                 printf ("</A>");
138         printf ("\n");
140         if (verbose)
141                 printf ("%f:%d%% %f:%d%%\n", wrta, wpl, crta, cpl);
143         return result;
145 \f
147 /* process command-line arguments */
148 int
149 process_arguments (int argc, char **argv)
151         int c = 1;
153 #ifdef HAVE_GETOPT_H
154         int option_index = 0;
155         static struct option long_options[] = {
156                 STD_LONG_OPTS,
157                 {"packets", required_argument, 0, 'p'},
158                 {"nohtml", no_argument, 0, 'n'},
159                 {"link", no_argument, 0, 'L'},
160                 {0, 0, 0, 0}
161         };
162 #endif
164 #define OPTCHARS "Vvht:c:w:H:p:nL"
166         if (argc < 2)
167                 return ERROR;
169         for (c = 1; c < argc; c++) {
170                 if (strcmp ("-to", argv[c]) == 0)
171                         strcpy (argv[c], "-t");
172                 if (strcmp ("-nohtml", argv[c]) == 0)
173                         strcpy (argv[c], "-n");
174         }
176         while (1) {
177 #ifdef HAVE_GETOPT_H
178                 c = getopt_long (argc, argv, OPTCHARS, long_options, &option_index);
179 #else
180                 c = getopt (argc, argv, OPTCHARS);
181 #endif
182                 if (c == -1 || c == EOF)
183                         break;
185                 switch (c) {
186                 case '?':       /* usage */
187                         usage3 ("Unknown argument", optopt);
188                 case 'h':       /* help */
189                         print_help ();
190                         exit (STATE_OK);
191                 case 'V':       /* version */
192                         print_revision (PROGNAME, REVISION);
193                         exit (STATE_OK);
194                 case 't':       /* timeout period */
195                         timeout_interval = atoi (optarg);
196                         break;
197                 case 'v':       /* verbose mode */
198                         verbose = TRUE;
199                         break;
200                 case 'H':       /* hostname */
201                         if (is_host (optarg) == FALSE)
202                                 usage2 ("Invalid host name/address", optarg);
203                         server_address = optarg;
204                         break;
205                 case 'p':       /* number of packets to send */
206                         if (is_intnonneg (optarg))
207                                 max_packets = atoi (optarg);
208                         else
209                                 usage2 ("<max_packets> (%s) must be a non-negative number\n", optarg);
210                         break;
211                 case 'n':       /* no HTML */
212                         display_html = FALSE;
213                         break;
214                 case 'L':       /* show HTML */
215                         display_html = TRUE;
216                         break;
217                 case 'c':
218                         get_threshold (optarg, &crta, &cpl);
219                         break;
220                 case 'w':
221                         get_threshold (optarg, &wrta, &wpl);
222                         break;
223                 }
224         }
226         c = optind;
227         if (c == argc)
228                 return validate_arguments ();
230         if (server_address == NULL) {
231                 if (is_host (argv[c]) == FALSE) {
232                         printf ("Invalid host name/address: %s\n\n", argv[c]);
233                         return ERROR;
234                 } else {
235                         server_address = argv[c++];
236                         if (c == argc)
237                                 return validate_arguments ();
238                 }
239         }
241         if (wpl == UNKNOWN_PACKET_LOSS) {
242                 if (is_intpercent (argv[c]) == FALSE) {
243                         printf ("<wpl> (%s) must be an integer percentage\n", argv[c]);
244                         return ERROR;
245                 } else {
246                         wpl = atoi (argv[c++]);
247                         if (c == argc)
248                                 return validate_arguments ();
249                 }
250         }
252         if (cpl == UNKNOWN_PACKET_LOSS) {
253                 if (is_intpercent (argv[c]) == FALSE) {
254                         printf ("<cpl> (%s) must be an integer percentage\n", argv[c]);
255                         return ERROR;
256                 } else {
257                         cpl = atoi (argv[c++]);
258                         if (c == argc)
259                                 return validate_arguments ();
260                 }
261         }
263         if (wrta == UNKNOWN_TRIP_TIME) {
264                 if (is_negative (argv[c])) {
265                         printf ("<wrta> (%s) must be a non-negative number\n", argv[c]);
266                         return ERROR;
267                 } else {
268                         wrta = atof (argv[c++]);
269                         if (c == argc)
270                                 return validate_arguments ();
271                 }
272         }
274         if (crta == UNKNOWN_TRIP_TIME) {
275                 if (is_negative (argv[c])) {
276                         printf ("<crta> (%s) must be a non-negative number\n", argv[c]);
277                         return ERROR;
278                 } else {
279                         crta = atof (argv[c++]);
280                         if (c == argc)
281                                 return validate_arguments ();
282                 }
283         }
285         if (max_packets == -1) {
286                 if (is_intnonneg (argv[c])) {
287                         max_packets = atoi (argv[c++]);
288                 }       else {
289                         printf ("<max_packets> (%s) must be a non-negative number\n", argv[c]);
290                         return ERROR;
291                 }
292         }
294         return validate_arguments ();
297 int
298 get_threshold (char *arg, float *trta, int *tpl)
300         if (is_intnonneg (arg) && sscanf (arg, "%f", trta) == 1)
301                 return OK;
302         else if (strpbrk (arg, ",:") && strstr (arg, "%") && sscanf (arg, "%f%*[:,]%d%%", trta, tpl) == 2)
303                 return OK;
304         else if (strstr (arg, "%") && sscanf (arg, "%d%%", tpl) == 1) 
305                 return OK;
306         else
307                 usage2 ("%s: Warning threshold must be integer or percentage!\n\n", arg);
310 int
311 validate_arguments ()
313         float max_seconds;
315         if (wrta == UNKNOWN_TRIP_TIME) {
316                 printf ("<wrta> was not set\n");
317                 return ERROR;
318         }
319         else if (crta == UNKNOWN_TRIP_TIME) {
320                 printf ("<crta> was not set\n");
321                 return ERROR;
322         }
323         else if (wpl == UNKNOWN_PACKET_LOSS) {
324                 printf ("<wpl> was not set\n");
325                 return ERROR;
326         }
327         else if (cpl == UNKNOWN_PACKET_LOSS) {
328                 printf ("<cpl> was not set\n");
329                 return ERROR;
330         }
331         else if (wrta > crta) {
332                 printf ("<wrta> (%f) cannot be larger than <crta> (%f)\n", wrta, crta);
333                 return ERROR;
334         }
335         else if (wpl > cpl) {
336                 printf ("<wpl> (%d) cannot be larger than <cpl> (%d)\n", wpl, cpl);
337                 return ERROR;
338         }
340         if (max_packets == -1)
341                 max_packets = DEFAULT_MAX_PACKETS;
343         max_seconds = crta / 1000.0 * max_packets + max_packets;
344         if (max_seconds > timeout_interval)
345                 timeout_interval = (int)max_seconds;
347         return OK;
349 \f
351 int
352 run_ping (char *command_line)
354         char input_buffer[MAX_INPUT_BUFFER];
355         int result = STATE_UNKNOWN;
357         warn_text = malloc (1);
358         if (warn_text == NULL)
359                 terminate (STATE_UNKNOWN, "unable to malloc warn_text");
360         warn_text[0] = 0;
362         if ((child_process = spopen (command_line)) == NULL) {
363                 printf ("Cannot open pipe: ");
364                 terminate (STATE_UNKNOWN, command_line);
365         }
366         child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
367         if (child_stderr == NULL)
368                 printf ("Cannot open stderr for %s\n", command_line);
370         while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
372                 if (strstr (input_buffer, "(DUP!)")) {
373                         /* cannot use the max function since STATE_UNKNOWN is max
374                         result = max (result, STATE_WARNING); */
375                         if( !(result == STATE_CRITICAL) ){
376                                 result = STATE_WARNING;
377                         }
378                         
379                         warn_text = realloc (warn_text, strlen (WARN_DUPLICATES) + 1);
380                         if (warn_text == NULL)
381                                 terminate (STATE_UNKNOWN, "unable to realloc warn_text");
382                         strcpy (warn_text, WARN_DUPLICATES);
383                 }
385                 /* get the percent loss statistics */
386                 if (sscanf
387                                         (input_buffer, "%*d packets transmitted, %*d packets received, +%*d errors, %d%% packet loss",
388                                                  &pl) == 1
389                                 || sscanf 
390                                         (input_buffer, "%*d packets transmitted, %*d packets received, %d%% packet loss",
391                                                 &pl) == 1
392                                 || sscanf 
393                                         (input_buffer, "%*d packets transmitted, %*d packets received, %d%% loss, time", &pl) == 1
394                                 || sscanf
395                                         (input_buffer, "%*d packets transmitted, %*d received, %d%% loss, time", &pl) == 1
396                                         /* Suse 8.0 as reported by Richard * Brodie */
397                                 )
398                         continue;
400                 /* get the round trip average */
401                 else
402                         if (sscanf (input_buffer, "round-trip min/avg/max = %*f/%f/%*f", &rta)
403                                         == 1
404                                         || sscanf (input_buffer,
405                                                                                  "round-trip min/avg/max/mdev = %*f/%f/%*f/%*f",
406                                                                                  &rta) == 1
407                                         || sscanf (input_buffer,
408                                                                                  "round-trip min/avg/max/sdev = %*f/%f/%*f/%*f",
409                                                                                  &rta) == 1
410                                         || sscanf (input_buffer,
411                                                                                  "round-trip min/avg/max/stddev = %*f/%f/%*f/%*f",
412                                                                                  &rta) == 1
413                                         || sscanf (input_buffer,
414                                                                                  "round-trip min/avg/max/std-dev = %*f/%f/%*f/%*f",
415                                                                                  &rta) == 1
416                                         || sscanf (input_buffer, "round-trip (ms) min/avg/max = %*f/%f/%*f",
417                                                                                  &rta) == 1
418                                         || sscanf (input_buffer, "rtt min/avg/max/mdev = %*f/%f/%*f/%*f ms",
419                                                                                  &rta) == 1
420                                                                                 )
421                         continue;
422         }
424         /* this is needed because there is no rta if all packets are lost */
425         if (pl == 100)
426                 rta = crta;
429         /* check stderr */
430         while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
431                 if (strstr
432                                 (input_buffer,
433                                  "Warning: no SO_TIMESTAMP support, falling back to SIOCGSTAMP"))
434                                 continue;
436                 if (strstr (input_buffer, "Network is unreachable"))
437                         terminate (STATE_CRITICAL, "PING CRITICAL - Network unreachable (%s)",
438                                                                  server_address);
439                 else if (strstr (input_buffer, "Destination Host Unreachable"))
440                         terminate (STATE_CRITICAL, "PING CRITICAL - Host Unreachable (%s)",
441                                                                  server_address);
442                 else if (strstr (input_buffer, "unknown host" ) )
443                         terminate (STATE_CRITICAL, "PING CRITICAL - Host not found (%s)",
444                                                                 server_address);
446                 warn_text =
447                         realloc (warn_text, strlen (warn_text) + strlen (input_buffer) + 2);
448                 if (warn_text == NULL)
449                         terminate (STATE_UNKNOWN, "unable to realloc warn_text");
450                 if (strlen (warn_text) == 0)
451                         strcpy (warn_text, input_buffer);
452                 else
453                         sprintf (warn_text, "%s %s", warn_text, input_buffer);
455                 if (strstr (input_buffer, "DUPLICATES FOUND")) {
456                         if( !(result == STATE_CRITICAL) ){
457                                 result = STATE_WARNING;
458                         }
459                 }
460                 else
461                         result = STATE_CRITICAL ;
462         }
463         (void) fclose (child_stderr);
466         /* close the pipe - WARNING if status is set */
467         if (spclose (child_process))
468                 result = max_state (result, STATE_WARNING);
470         return result;
474 void
475 print_usage (void)
477         printf ("Usage:\n" " %s %s\n"
478 #ifdef HAVE_GETOPT_H
479                                         " %s (-h | --help) for detailed help\n"
480                                         " %s (-V | --version) for version information\n",
481 #else
482                                         " %s -h for detailed help\n"
483                                         " %s -V for version information\n",
484 #endif
485                                         PROGNAME, OPTIONS, PROGNAME, PROGNAME);
488 void
489 print_help (void)
491         print_revision (PROGNAME, REVISION);
492         printf
493                 ("Copyright (c) %s %s <%s>\n\n%s\n",
494                  COPYRIGHT, AUTHOR, EMAIL, SUMMARY);
495         print_usage ();
496         printf
497                 ("\nOptions:\n" LONGOPTIONS "\n" DESCRIPTION "\n", 
498                  DEFAULT_MAX_PACKETS, DEFAULT_SOCKET_TIMEOUT);
499         support ();