Code

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