Code

add logic to check multiple servers
[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 i;
88         addresses = malloc (max_addr);
90         if (process_arguments (argc, argv) == ERROR)
91                 usage ("Could not parse arguments");
92         exit;
94         /* Set signal handling and alarm */
95         if (signal (SIGALRM, popen_timeout_alarm_handler) == SIG_ERR) {
96                 printf ("Cannot catch SIGALRM");
97                 return STATE_UNKNOWN;
98         }
100         /* handle timeouts gracefully */
101         alarm (timeout_interval);
103         for (i = 0 ; i < n_addresses ; i++) {
105                 /* does the host address of number of packets argument come first? */
106 #ifdef PING_PACKETS_FIRST
107                 asprintf (&command_line, PING_COMMAND, max_packets, addresses[i]);
108 #else
109                 asprintf (&command_line, PING_COMMAND, addresses[i], max_packets);
110 #endif
112                 if (verbose)
113                         printf ("%s ==> ", command_line);
115                 /* run the command */
116                 run_ping (command_line, addresses[i]);
118                 if (pl == UNKNOWN_PACKET_LOSS || rta == UNKNOWN_TRIP_TIME) {
119                         printf ("%s\n", command_line);
120                         terminate (STATE_UNKNOWN,
121                                                                  "Error: Could not interpret output from ping command\n");
122                 }
124                 if (pl >= cpl || rta >= crta || rta < 0)
125                         result = STATE_CRITICAL;
126                 else if (pl >= wpl || rta >= wrta)
127                         result = STATE_WARNING;
128                 else if (pl < wpl && rta < wrta && pl >= 0 && rta >= 0)
129                         /* cannot use the max function because STATE_UNKNOWN is now 3 gt STATE_OK                       
130                                  result = max (result, STATE_OK);  */
131                         if( !( (result == STATE_WARNING) || (result == STATE_CRITICAL) )  ) {
132                                 result = STATE_OK;      
133                         }
134         
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 (result), warn_text,
139                                                         pl);
140                 else
141                         printf ("PING %s - %sPacket loss = %d%%, RTA = %2.2f ms",
142                                                         state_text (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);
149         }
151         return result;
153 \f
155 /* process command-line arguments */
156 int
157 process_arguments (int argc, char **argv)
159         int c = 1;
161 #ifdef HAVE_GETOPT_H
162         int option_index = 0;
163         static struct option long_options[] = {
164                 STD_LONG_OPTS,
165                 {"packets", required_argument, 0, 'p'},
166                 {"nohtml", no_argument, 0, 'n'},
167                 {"link", no_argument, 0, 'L'},
168                 {0, 0, 0, 0}
169         };
170 #endif
172 #define OPTCHARS "Vvht:c:w:H:p:nL"
174         if (argc < 2)
175                 return ERROR;
177         for (c = 1; c < argc; c++) {
178                 if (strcmp ("-to", argv[c]) == 0)
179                         strcpy (argv[c], "-t");
180                 if (strcmp ("-nohtml", argv[c]) == 0)
181                         strcpy (argv[c], "-n");
182         }
184         while (1) {
185 #ifdef HAVE_GETOPT_H
186                 c = getopt_long (argc, argv, OPTCHARS, long_options, &option_index);
187 #else
188                 c = getopt (argc, argv, OPTCHARS);
189 #endif
190                 if (c == -1 || c == EOF)
191                         break;
193                 switch (c) {
194                 case '?':       /* usage */
195                         usage3 ("Unknown argument", optopt);
196                 case 'h':       /* help */
197                         print_help ();
198                         exit (STATE_OK);
199                 case 'V':       /* version */
200                         print_revision (progname, REVISION);
201                         exit (STATE_OK);
202                 case 't':       /* timeout period */
203                         timeout_interval = atoi (optarg);
204                         break;
205                 case 'v':       /* verbose mode */
206                         verbose = TRUE;
207                         break;
208                 case 'H':       /* hostname */
209                         if (is_host (optarg) == FALSE)
210                                 usage2 ("Invalid host name/address", optarg);
211                         n_addresses++;
212                         if (n_addresses > max_addr) {
213                                 max_addr *= 2;
214                                 addresses = realloc (addresses, max_addr);
215                                 if (addresses == NULL)
216                                         terminate (STATE_UNKNOWN, "Could not realloc() addresses\n");
217                         }
218                         addresses[n_addresses-1] = optarg;
219                         break;
220                 case 'p':       /* number of packets to send */
221                         if (is_intnonneg (optarg))
222                                 max_packets = atoi (optarg);
223                         else
224                                 usage2 ("<max_packets> (%s) must be a non-negative number\n", optarg);
225                         break;
226                 case 'n':       /* no HTML */
227                         display_html = FALSE;
228                         break;
229                 case 'L':       /* show HTML */
230                         display_html = TRUE;
231                         break;
232                 case 'c':
233                         get_threshold (optarg, &crta, &cpl);
234                         break;
235                 case 'w':
236                         get_threshold (optarg, &wrta, &wpl);
237                         break;
238                 }
239         }
241         c = optind;
242         if (c == argc)
243                 return validate_arguments ();
245         if (addresses[0] == NULL) {
246                 if (is_host (argv[c]) == FALSE) {
247                         printf ("Invalid host name/address: %s\n\n", argv[c]);
248                         return ERROR;
249                 } else {
250                         addresses[0] = argv[c++];
251                         if (c == argc)
252                                 return validate_arguments ();
253                 }
254         }
256         if (wpl == UNKNOWN_PACKET_LOSS) {
257                 if (is_intpercent (argv[c]) == FALSE) {
258                         printf ("<wpl> (%s) must be an integer percentage\n", argv[c]);
259                         return ERROR;
260                 } else {
261                         wpl = atoi (argv[c++]);
262                         if (c == argc)
263                                 return validate_arguments ();
264                 }
265         }
267         if (cpl == UNKNOWN_PACKET_LOSS) {
268                 if (is_intpercent (argv[c]) == FALSE) {
269                         printf ("<cpl> (%s) must be an integer percentage\n", argv[c]);
270                         return ERROR;
271                 } else {
272                         cpl = atoi (argv[c++]);
273                         if (c == argc)
274                                 return validate_arguments ();
275                 }
276         }
278         if (wrta == UNKNOWN_TRIP_TIME) {
279                 if (is_negative (argv[c])) {
280                         printf ("<wrta> (%s) must be a non-negative number\n", argv[c]);
281                         return ERROR;
282                 } else {
283                         wrta = atof (argv[c++]);
284                         if (c == argc)
285                                 return validate_arguments ();
286                 }
287         }
289         if (crta == UNKNOWN_TRIP_TIME) {
290                 if (is_negative (argv[c])) {
291                         printf ("<crta> (%s) must be a non-negative number\n", argv[c]);
292                         return ERROR;
293                 } else {
294                         crta = atof (argv[c++]);
295                         if (c == argc)
296                                 return validate_arguments ();
297                 }
298         }
300         if (max_packets == -1) {
301                 if (is_intnonneg (argv[c])) {
302                         max_packets = atoi (argv[c++]);
303                 }       else {
304                         printf ("<max_packets> (%s) must be a non-negative number\n", argv[c]);
305                         return ERROR;
306                 }
307         }
309         return validate_arguments ();
312 int
313 get_threshold (char *arg, float *trta, int *tpl)
315         if (is_intnonneg (arg) && sscanf (arg, "%f", trta) == 1)
316                 return OK;
317         else if (strpbrk (arg, ",:") && strstr (arg, "%") && sscanf (arg, "%f%*[:,]%d%%", trta, tpl) == 2)
318                 return OK;
319         else if (strstr (arg, "%") && sscanf (arg, "%d%%", tpl) == 1) 
320                 return OK;
321         else
322                 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, char *server_address)
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                         if( !(result == STATE_CRITICAL) ){
472                                 result = STATE_WARNING;
473                         }
474                 }
475                 else
476                         result = STATE_CRITICAL ;
477         }
478         (void) fclose (child_stderr);
481         /* close the pipe - WARNING if status is set */
482         if (spclose (child_process))
483                 result = max_state (result, STATE_WARNING);
485         return result;
489 void
490 print_usage (void)
492         printf ("Usage:\n" " %s %s\n"
493 #ifdef HAVE_GETOPT_H
494                                         " %s (-h | --help) for detailed help\n"
495                                         " %s (-V | --version) for version information\n",
496 #else
497                                         " %s -h for detailed help\n"
498                                         " %s -V for version information\n",
499 #endif
500                                         progname, OPTIONS, progname, progname);
503 void
504 print_help (void)
506         print_revision (progname, REVISION);
507         printf
508                 ("Copyright (c) %s %s <%s>\n\n%s\n",
509                  COPYRIGHT, AUTHOR, EMAIL, SUMMARY);
510         print_usage ();
511         printf
512                 ("\nOptions:\n" LONGOPTIONS "\n" DESCRIPTION "\n", 
513                  DEFAULT_MAX_PACKETS, DEFAULT_SOCKET_TIMEOUT);
514         support ();