Code

remove call_getopt
[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         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 = 1;
155 #ifdef HAVE_GETOPT_H
156         int option_index = 0;
157         static struct option long_options[] = {
158                 STD_LONG_OPTS,
159                 {"packets", required_argument, 0, 'p'},
160                 {"nohtml", no_argument, 0, 'n'},
161                 {"link", no_argument, 0, 'L'},
162                 {0, 0, 0, 0}
163         };
164 #endif
166 #define OPTCHARS "Vvht:c:w:H:p:nL"
168         if (argc < 2)
169                 return ERROR;
171         for (c = 1; c < argc; c++) {
172                 if (strcmp ("-to", argv[c]) == 0)
173                         strcpy (argv[c], "-t");
174                 if (strcmp ("-nohtml", argv[c]) == 0)
175                         strcpy (argv[c], "-n");
176         }
178         while (1) {
179 #ifdef HAVE_GETOPT_H
180                 c = getopt_long (argc, argv, OPTCHARS, long_options, &option_index);
181 #else
182                 c = getopt (argc, argv, OPTCHARS);
183 #endif
184                 if (c == -1 || c == EOF)
185                         break;
187                 switch (c) {
188                 case '?':       /* usage */
189                         usage3 ("Unknown argument", optopt);
190                 case 'h':       /* help */
191                         print_help ();
192                         exit (STATE_OK);
193                 case 'V':       /* version */
194                         print_revision (PROGNAME, REVISION);
195                         exit (STATE_OK);
196                 case 't':       /* timeout period */
197                         timeout_interval = atoi (optarg);
198                         break;
199                 case 'v':       /* verbose mode */
200                         verbose = TRUE;
201                         break;
202                 case 'H':       /* hostname */
203                         if (is_host (optarg) == FALSE)
204                                 usage2 ("Invalid host name/address", optarg);
205                         server_address = optarg;
206                         break;
207                 case 'p':       /* number of packets to send */
208                         if (is_intnonneg (optarg))
209                                 max_packets = atoi (optarg);
210                         else
211                                 usage2 ("<max_packets> (%s) must be a non-negative number\n", optarg);
212                         break;
213                 case 'n':       /* no HTML */
214                         display_html = FALSE;
215                         break;
216                 case 'L':       /* show HTML */
217                         display_html = TRUE;
218                         break;
219                 case 'c':
220                         get_threshold (optarg, &crta, &cpl);
221                         break;
222                 case 'w':
223                         get_threshold (optarg, &wrta, &wpl);
224                         break;
225                 }
226         }
228         c = optind;
229         if (c == argc)
230                 return validate_arguments ();
232         if (server_address == NULL) {
233                 if (is_host (argv[c]) == FALSE) {
234                         printf ("Invalid host name/address: %s\n\n", argv[c]);
235                         return ERROR;
236                 } else {
237                         server_address = argv[c++];
238                         if (c == argc)
239                                 return validate_arguments ();
240                 }
241         }
243         if (wpl == UNKNOWN_PACKET_LOSS) {
244                 if (is_intpercent (argv[c]) == FALSE) {
245                         printf ("<wpl> (%s) must be an integer percentage\n", argv[c]);
246                         return ERROR;
247                 } else {
248                         wpl = atoi (argv[c++]);
249                         if (c == argc)
250                                 return validate_arguments ();
251                 }
252         }
254         if (cpl == UNKNOWN_PACKET_LOSS) {
255                 if (is_intpercent (argv[c]) == FALSE) {
256                         printf ("<cpl> (%s) must be an integer percentage\n", argv[c]);
257                         return ERROR;
258                 } else {
259                         cpl = atoi (argv[c++]);
260                         if (c == argc)
261                                 return validate_arguments ();
262                 }
263         }
265         if (wrta == UNKNOWN_TRIP_TIME) {
266                 if (is_negative (argv[c])) {
267                         printf ("<wrta> (%s) must be a non-negative number\n", argv[c]);
268                         return ERROR;
269                 } else {
270                         wrta = atof (argv[c++]);
271                         if (c == argc)
272                                 return validate_arguments ();
273                 }
274         }
276         if (crta == UNKNOWN_TRIP_TIME) {
277                 if (is_negative (argv[c])) {
278                         printf ("<crta> (%s) must be a non-negative number\n", argv[c]);
279                         return ERROR;
280                 } else {
281                         crta = atof (argv[c++]);
282                         if (c == argc)
283                                 return validate_arguments ();
284                 }
285         }
287         if (max_packets == -1) {
288                 if (is_intnonneg (argv[c])) {
289                         max_packets = atoi (argv[c++]);
290                 }       else {
291                         printf ("<max_packets> (%s) must be a non-negative number\n", argv[c]);
292                         return ERROR;
293                 }
294         }
296         return validate_arguments ();
299 int
300 get_threshold (char *arg, float *trta, int *tpl)
302         if (is_intnonneg (arg) && sscanf (arg, "%f", trta) == 1)
303                 return OK;
304         else if (strpbrk (arg, ",:") && strstr (arg, "%") && sscanf (arg, "%f%*[:,]%d%%", trta, tpl) == 2)
305                 return OK;
306         else if (strstr (arg, "%") && sscanf (arg, "%d%%", tpl) == 1) 
307                 return OK;
308         else
309                 usage2 ("%s: Warning threshold must be integer or percentage!\n\n", arg);
312 int
313 validate_arguments ()
315         float max_seconds;
317         if (wrta == UNKNOWN_TRIP_TIME) {
318                 printf ("<wrta> was not set\n");
319                 return ERROR;
320         }
321         else if (crta == UNKNOWN_TRIP_TIME) {
322                 printf ("<crta> was not set\n");
323                 return ERROR;
324         }
325         else if (wpl == UNKNOWN_PACKET_LOSS) {
326                 printf ("<wpl> was not set\n");
327                 return ERROR;
328         }
329         else if (cpl == UNKNOWN_PACKET_LOSS) {
330                 printf ("<cpl> was not set\n");
331                 return ERROR;
332         }
333         else if (wrta > crta) {
334                 printf ("<wrta> (%f) cannot be larger than <crta> (%f)\n", wrta, crta);
335                 return ERROR;
336         }
337         else if (wpl > cpl) {
338                 printf ("<wpl> (%d) cannot be larger than <cpl> (%d)\n", wpl, cpl);
339                 return ERROR;
340         }
342         if (max_packets == -1)
343                 max_packets = DEFAULT_MAX_PACKETS;
345         max_seconds = crta / 1000.0 * max_packets + max_packets;
346         if (max_seconds > timeout_interval)
347                 timeout_interval = (int)max_seconds;
349         return OK;
351 \f
353 int
354 run_ping (char *command_line)
356         char input_buffer[MAX_INPUT_BUFFER];
357         int result = STATE_UNKNOWN;
359         warn_text = malloc (1);
360         if (warn_text == NULL)
361                 terminate (STATE_UNKNOWN, "unable to malloc warn_text");
362         warn_text[0] = 0;
364         if ((child_process = spopen (command_line)) == NULL) {
365                 printf ("Cannot open pipe: ");
366                 terminate (STATE_UNKNOWN, command_line);
367         }
368         child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
369         if (child_stderr == NULL)
370                 printf ("Cannot open stderr for %s\n", command_line);
372         while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
374                 if (strstr (input_buffer, "(DUP!)")) {
375                         /* cannot use the max function since STATE_UNKNOWN is max
376                         result = max (result, STATE_WARNING); */
377                         if( !(result == STATE_CRITICAL) ){
378                                 result = STATE_WARNING;
379                         }
380                         
381                         warn_text = realloc (warn_text, strlen (WARN_DUPLICATES) + 1);
382                         if (warn_text == NULL)
383                                 terminate (STATE_UNKNOWN, "unable to realloc warn_text");
384                         strcpy (warn_text, WARN_DUPLICATES);
385                 }
387                 /* get the percent loss statistics */
388                 if (sscanf
389                                         (input_buffer, "%*d packets transmitted, %*d packets received, +%*d errors, %d%% packet loss",
390                                                  &pl) == 1
391                                 || sscanf 
392                                         (input_buffer, "%*d packets transmitted, %*d packets received, %d%% packet loss",
393                                                 &pl) == 1
394                                 || sscanf 
395                                         (input_buffer, "%*d packets transmitted, %*d packets received, %d%% loss, time", &pl) == 1
396                                 || sscanf
397                                         (input_buffer, "%*d packets transmitted, %*d received, %d%% loss, time", &pl) == 1
398                                         /* Suse 8.0 as reported by Richard * Brodie */
399                                 )
400                         continue;
402                 /* get the round trip average */
403                 else
404                         if (sscanf (input_buffer, "round-trip min/avg/max = %*f/%f/%*f", &rta)
405                                         == 1
406                                         || sscanf (input_buffer,
407                                                                                  "round-trip min/avg/max/mdev = %*f/%f/%*f/%*f",
408                                                                                  &rta) == 1
409                                         || sscanf (input_buffer,
410                                                                                  "round-trip min/avg/max/sdev = %*f/%f/%*f/%*f",
411                                                                                  &rta) == 1
412                                         || sscanf (input_buffer,
413                                                                                  "round-trip min/avg/max/stddev = %*f/%f/%*f/%*f",
414                                                                                  &rta) == 1
415                                         || sscanf (input_buffer,
416                                                                                  "round-trip min/avg/max/std-dev = %*f/%f/%*f/%*f",
417                                                                                  &rta) == 1
418                                         || sscanf (input_buffer, "round-trip (ms) min/avg/max = %*f/%f/%*f",
419                                                                                  &rta) == 1
420                                         || sscanf (input_buffer, "rtt min/avg/max/mdev = %*f/%f/%*f/%*f ms",
421                                                                                  &rta) == 1
422                                                                                 )
423                         continue;
424         }
426         /* this is needed because there is no rta if all packets are lost */
427         if (pl == 100)
428                 rta = crta;
431         /* check stderr */
432         while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
433                 if (strstr
434                                 (input_buffer,
435                                  "Warning: no SO_TIMESTAMP support, falling back to SIOCGSTAMP"))
436                                 continue;
438                 if (strstr (input_buffer, "Network is unreachable"))
439                         terminate (STATE_CRITICAL, "PING CRITICAL - Network unreachable (%s)",
440                                                                  server_address);
441                 else if (strstr (input_buffer, "Destination Host Unreachable"))
442                         terminate (STATE_CRITICAL, "PING CRITICAL - Host Unreachable (%s)",
443                                                                  server_address);
444                 else if (strstr (input_buffer, "unknown host" ) )
445                         terminate (STATE_CRITICAL, "PING CRITICAL - Host not found (%s)",
446                                                                 server_address);
448                 warn_text =
449                         realloc (warn_text, strlen (warn_text) + strlen (input_buffer) + 2);
450                 if (warn_text == NULL)
451                         terminate (STATE_UNKNOWN, "unable to realloc warn_text");
452                 if (strlen (warn_text) == 0)
453                         strcpy (warn_text, input_buffer);
454                 else
455                         sprintf (warn_text, "%s %s", warn_text, input_buffer);
457                 if (strstr (input_buffer, "DUPLICATES FOUND")) {
458                         if( !(result == STATE_CRITICAL) ){
459                                 result = STATE_WARNING;
460                         }
461                 }
462                 else
463                         result = STATE_CRITICAL ;
464         }
465         (void) fclose (child_stderr);
468         /* close the pipe - WARNING if status is set */
469         if (spclose (child_process))
470                 result = max_state (result, STATE_WARNING);
472         return result;
476 void
477 print_usage (void)
479         printf ("Usage:\n" " %s %s\n"
480 #ifdef HAVE_GETOPT_H
481                                         " %s (-h | --help) for detailed help\n"
482                                         " %s (-V | --version) for version information\n",
483 #else
484                                         " %s -h for detailed help\n"
485                                         " %s -V for version information\n",
486 #endif
487                                         PROGNAME, OPTIONS, PROGNAME, PROGNAME);
490 void
491 print_help (void)
493         print_revision (PROGNAME, REVISION);
494         printf
495                 ("Copyright (c) %s %s <%s>\n\n%s\n",
496                  COPYRIGHT, AUTHOR, EMAIL, SUMMARY);
497         print_usage ();
498         printf
499                 ("\nOptions:\n" LONGOPTIONS "\n" DESCRIPTION "\n", 
500                  DEFAULT_MAX_PACKETS, DEFAULT_SOCKET_TIMEOUT);
501         support ();