Code

Initial revision
[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_pgsql"
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                 result = max (result, STATE_OK);
126         if (display_html == TRUE)
127                 printf ("<A HREF='%s/traceroute.cgi?%s'>", CGIURL, server_address);
128         if (pl == 100)
129                 printf ("PING %s - %sPacket loss = %d%%", state_text (result), warn_text,
130                                                 pl);
131         else
132                 printf ("PING %s - %sPacket loss = %d%%, RTA = %2.2f ms",
133                                                 state_text (result), warn_text, pl, rta);
134         if (display_html == TRUE)
135                 printf ("</A>");
136         printf ("\n");
138         if (verbose)
139                 printf ("%f:%d%% %f:%d%%\n", wrta, wpl, crta, cpl);
141         return result;
143 \f
145 /* process command-line arguments */
146 int
147 process_arguments (int argc, char **argv)
149         int c;
151         if (argc < 2)
152                 return ERROR;
154         for (c = 1; c < argc; c++) {
155                 if (strcmp ("-to", argv[c]) == 0)
156                         strcpy (argv[c], "-t");
157                 if (strcmp ("-nohtml", argv[c]) == 0)
158                         strcpy (argv[c], "-n");
159         }
161         c = 0;
162         while ((c += call_getopt (argc - c, &argv[c])) < argc) {
164                 if (is_option (argv[c]))
165                         continue;
167                 if (server_address == NULL) {
168                         if (is_host (argv[c]) == FALSE) {
169                                 printf ("Invalid host name/address: %s\n\n", argv[c]);
170                                 return ERROR;
171                         }
172                         server_address = argv[c];
173                 }
174                 else if (wpl == UNKNOWN_PACKET_LOSS) {
175                         if (is_intpercent (argv[c]) == FALSE) {
176                                 printf ("<wpl> (%s) must be an integer percentage\n", argv[c]);
177                                 return ERROR;
178                         }
179                         wpl = atoi (argv[c]);
180                 }
181                 else if (cpl == UNKNOWN_PACKET_LOSS) {
182                         if (is_intpercent (argv[c]) == FALSE) {
183                                 printf ("<cpl> (%s) must be an integer percentage\n", argv[c]);
184                                 return ERROR;
185                         }
186                         cpl = atoi (argv[c]);
187                 }
188                 else if (wrta == UNKNOWN_TRIP_TIME) {
189                         if (is_negative (argv[c])) {
190                                 printf ("<wrta> (%s) must be a non-negative number\n", argv[c]);
191                                 return ERROR;
192                         }
193                         wrta = atof (argv[c]);
194                 }
195                 else if (crta == UNKNOWN_TRIP_TIME) {
196                         if (is_negative (argv[c])) {
197                                 printf ("<crta> (%s) must be a non-negative number\n", argv[c]);
198                                 return ERROR;
199                         }
200                         crta = atof (argv[c]);
201                 }
202                 else if (max_packets == -1) {
203                         if (is_intnonneg (argv[c])) {
204                                 max_packets = atoi (argv[c]);
205                         }
206                         else {
207                                 printf ("<max_packets> (%s) must be a non-negative number\n",
208                                                                 argv[c]);
209                                 return ERROR;
210                         }
211                 }
213         }
215         return validate_arguments ();
218 int
219 call_getopt (int argc, char **argv)
221         int c, i = 0;
223 #ifdef HAVE_GETOPT_H
224         int option_index = 0;
225         static struct option long_options[] = {
226                 {"help", no_argument, 0, 'h'},
227                 {"version", no_argument, 0, 'V'},
228                 {"verbose", no_argument, 0, 'v'},
229                 {"nohtml", no_argument, 0, 'n'},
230                 {"link", no_argument, 0, 'L'},
231                 {"timeout", required_argument, 0, 't'},
232                 {"critical", required_argument, 0, 'c'},
233                 {"warning", required_argument, 0, 'w'},
234                 {"hostname", required_argument, 0, 'H'},
235                 {"packets", required_argument, 0, 'p'},
236                 {0, 0, 0, 0}
237         };
238 #endif
240         while (1) {
241 #ifdef HAVE_GETOPT_H
242                 c =
243                         getopt_long (argc, argv, "+hVvt:c:w:H:p:nL", long_options,
244                                                                          &option_index);
245 #else
246                 c = getopt (argc, argv, "+hVvt:c:w:H:p:nL");
247 #endif
249                 i++;
251                 if (c == -1 || c == EOF || c == 1)
252                         break;
254                 switch (c) {
255                 case 't':
256                 case 'c':
257                 case 'w':
258                 case 'H':
259                 case 'p':
260                         i++;
261                 }
263                 switch (c) {
264                 case '?':                                                                       /* print short usage statement if args not parsable */
265                         usage2 ("Unknown argument", optarg);
266                 case 'h':                                                                       /* help */
267                         print_help ();
268                         exit (STATE_OK);
269                 case 'V':                                                                       /* version */
270                         print_revision (PROGNAME, REVISION);
271                         exit (STATE_OK);
272                 case 't':                                                                       /* timeout period */
273                         timeout_interval = atoi (optarg);
274                         break;
275                 case 'v':                                                                       /* verbose mode */
276                         verbose = TRUE;
277                         break;
278                 case 'H':                                                                       /* hostname */
279                         if (is_host (optarg) == FALSE)
280                                 usage2 ("Invalid host name/address", optarg);
281                         server_address = optarg;
282                         break;
283                 case 'p':                                                                       /* number of packets to send */
284                         if (is_intnonneg (optarg))
285                                 max_packets = atoi (optarg);
286                         else
287                                 usage2 ("<max_packets> (%s) must be a non-negative number\n", optarg);
288                         break;
289                 case 'n':                                                                       /* no HTML */
290                         display_html = FALSE;
291                         break;
292                 case 'L':                                                                       /* show HTML */
293                         display_html = TRUE;
294                         break;
295                 case 'c':
296                         get_threshold (optarg, &crta, &cpl);
297                         break;
298                 case 'w':
299                         get_threshold (optarg, &wrta, &wpl);
300                         break;
301                 }
302         }
304         return i;
307 int
308 get_threshold (char *arg, float *trta, int *tpl)
310         if (is_intnonneg (arg) && sscanf (arg, "%f", trta) == 1)
311                 return OK;
312         else if (strpbrk (arg, ",:") && strstr (arg, "%") && sscanf (arg, "%f%*[:,]%d%%", trta, tpl) == 2)
313                 return OK;
314         else if (strstr (arg, "%") && sscanf (arg, "%d%%", tpl) == 1) 
315                 return OK;
316         else
317                 usage2 ("%s: Warning threshold must be integer or percentage!\n\n", arg);
321 int
322 validate_arguments ()
324         float max_seconds;
326         if (wrta == UNKNOWN_TRIP_TIME) {
327                 printf ("<wrta> was not set\n");
328                 return ERROR;
329         }
330         else if (crta == UNKNOWN_TRIP_TIME) {
331                 printf ("<crta> was not set\n");
332                 return ERROR;
333         }
334         else if (wpl == UNKNOWN_PACKET_LOSS) {
335                 printf ("<wpl> was not set\n");
336                 return ERROR;
337         }
338         else if (cpl == UNKNOWN_PACKET_LOSS) {
339                 printf ("<cpl> was not set\n");
340                 return ERROR;
341         }
342         else if (wrta > crta) {
343                 printf ("<wrta> (%f) cannot be larger than <crta> (%f)\n", wrta, crta);
344                 return ERROR;
345         }
346         else if (wpl > cpl) {
347                 printf ("<wpl> (%d) cannot be larger than <cpl> (%d)\n", wpl, cpl);
348                 return ERROR;
349         }
351         if (max_packets == -1)
352                 max_packets = DEFAULT_MAX_PACKETS;
354         max_seconds = crta / 1000.0 * max_packets + max_packets;
355         if (max_seconds > timeout_interval)
356                 timeout_interval = (int)max_seconds;
358         return OK;
360 \f
362 int
363 run_ping (char *command_line)
365         char input_buffer[MAX_INPUT_BUFFER];
366         int result = STATE_UNKNOWN;
368         warn_text = malloc (1);
369         if (warn_text == NULL)
370                 terminate (STATE_UNKNOWN, "unable to malloc warn_text");
371         warn_text[0] = 0;
373         if ((child_process = spopen (command_line)) == NULL) {
374                 printf ("Cannot open pipe: ");
375                 terminate (STATE_UNKNOWN, command_line);
376         }
377         child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
378         if (child_stderr == NULL)
379                 printf ("Cannot open stderr for %s\n", command_line);
381         while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
383                 if (strstr (input_buffer, "(DUP!)")) {
384                         result = max (result, STATE_WARNING);
385                         warn_text = realloc (warn_text, strlen (WARN_DUPLICATES) + 1);
386                         if (warn_text == NULL)
387                                 terminate (STATE_UNKNOWN, "unable to realloc warn_text");
388                         strcpy (warn_text, WARN_DUPLICATES);
389                 }
391                 /* get the percent loss statistics */
392                 if (sscanf
393                                 (input_buffer,
394                                  "%*d packets transmitted, %*d packets received, +%*d errors, %d%% packet loss",
395                                  &pl) == 1
396                                 || sscanf (input_buffer,
397                                                                          "%*d packets transmitted, %*d packets received, %d%% packet loss",
398                                                                          &pl) == 1)
399                         continue;
401                 /* get the round trip average */
402                 else
403                         if (sscanf (input_buffer, "round-trip min/avg/max = %*f/%f/%*f", &rta)
404                                         == 1
405                                         || sscanf (input_buffer,
406                                                                                  "round-trip min/avg/max/mdev = %*f/%f/%*f/%*f",
407                                                                                  &rta) == 1
408                                         || sscanf (input_buffer,
409                                                                                  "round-trip min/avg/max/sdev = %*f/%f/%*f/%*f",
410                                                                                  &rta) == 1
411                                         || sscanf (input_buffer,
412                                                                                  "round-trip min/avg/max/stddev = %*f/%f/%*f/%*f",
413                                                                                  &rta) == 1
414                                         || sscanf (input_buffer,
415                                                                                  "round-trip min/avg/max/std-dev = %*f/%f/%*f/%*f",
416                                                                                  &rta) == 1
417                                         || sscanf (input_buffer, "round-trip (ms) min/avg/max = %*f/%f/%*f",
418                                                                                  &rta) == 1)
419                         continue;
420         }
422         /* this is needed because there is no rta if all packets are lost */
423         if (pl == 100)
424                 rta = crta;
427         /* check stderr */
428         while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
429                 if (strstr
430                                 (input_buffer,
431                                  "Warning: no SO_TIMESTAMP support, falling back to SIOCGSTAMP"))
432                                 continue;
434                 if (strstr (input_buffer, "Network is unreachable"))
435                         terminate (STATE_CRITICAL, "PING CRITICAL - Network unreachable (%s)",
436                                                                  server_address);
437                 else if (strstr (input_buffer, "Destination Host Unreachable"))
438                         terminate (STATE_CRITICAL, "PING CRITICAL - Host Unreachable (%s)",
439                                                                  server_address);
441                 warn_text =
442                         realloc (warn_text, strlen (warn_text) + strlen (input_buffer) + 2);
443                 if (warn_text == NULL)
444                         terminate (STATE_UNKNOWN, "unable to realloc warn_text");
445                 if (strlen (warn_text) == 0)
446                         strcpy (warn_text, input_buffer);
447                 else
448                         sprintf (warn_text, "%s %s", warn_text, input_buffer);
450                 if (strstr (input_buffer, "DUPLICATES FOUND"))
451                         result = max (result, STATE_WARNING);
452                 else
453                         result = max (result, STATE_CRITICAL);
454         }
455         (void) fclose (child_stderr);
458         /* close the pipe - WARNING if status is set */
459         if (spclose (child_process))
460                 result = max (result, STATE_WARNING);
462         return result;
464 \f
466 void
467 print_usage (void)
469         printf ("Usage:\n" " %s %s\n"
470 #ifdef HAVE_GETOPT_H
471                                         " %s (-h | --help) for detailed help\n"
472                                         " %s (-V | --version) for version information\n",
473 #else
474                                         " %s -h for detailed help\n"
475                                         " %s -V for version information\n",
476 #endif
477                                         PROGNAME, OPTIONS, PROGNAME, PROGNAME);
480 void
481 print_help (void)
483         print_revision (PROGNAME, REVISION);
484         printf
485                 ("Copyright (c) %s %s <%s>\n\n%s\n",
486                  COPYRIGHT, AUTHOR, EMAIL, SUMMARY);
487         print_usage ();
488         printf
489                 ("\nOptions:\n" LONGOPTIONS "\n" DESCRIPTION "\n", 
490                  DEFAULT_MAX_PACKETS, DEFAULT_SOCKET_TIMEOUT);
491         support ();