Code

replace "terminate" with "die" for shorter name and better readability
[nagiosplug.git] / plugins / check_ping.c
1 /******************************************************************************
3  This program is free software; you can redistribute it and/or modify
4  it under the terms of the GNU General Public License as published by
5  the Free Software Foundation; either version 2 of the License, or
6  (at your option) any later version.
8  This program is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  GNU General Public License for more details.
13  You should have received a copy of the GNU General Public License
14  along with this program; if not, write to the Free Software
15  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 ******************************************************************************/
19 const char *progname = "check_ping";
20 const char *revision = "$Revision$";
21 const char *copyright = "2000-2003";
22 const char *email = "nagiosplug-devel@lists.sourceforge.net";
24 #include "common.h"
25 #include "netutils.h"
26 #include "popen.h"
27 #include "utils.h"
29 #define WARN_DUPLICATES "DUPLICATES FOUND! "
30 #define UNKNOWN_TRIP_TIME -1.0  /* -1 seconds */
32 enum {
33         UNKNOWN_PACKET_LOSS = 200,    /* 200% */
34         DEFAULT_MAX_PACKETS = 5       /* default no. of ICMP ECHO packets */
35 };
37 int process_arguments (int, char **);
38 int get_threshold (char *, float *, int *);
39 int validate_arguments (void);
40 int run_ping (char *, char *);
41 void print_usage (void);
42 void print_help (void);
44 int display_html = FALSE;
45 int wpl = UNKNOWN_PACKET_LOSS;
46 int cpl = UNKNOWN_PACKET_LOSS;
47 float wrta = UNKNOWN_TRIP_TIME;
48 float crta = UNKNOWN_TRIP_TIME;
49 char **addresses = NULL;
50 int n_addresses;
51 int max_addr = 1;
52 int max_packets = -1;
53 int verbose = FALSE;
55 float rta = UNKNOWN_TRIP_TIME;
56 int pl = UNKNOWN_PACKET_LOSS;
58 char *warn_text = "";
63 \f
64 int
65 main (int argc, char **argv)
66 {
67         char *cmd = NULL;
68         int result = STATE_UNKNOWN;
69         int this_result = STATE_UNKNOWN;
70         int i;
72         addresses = malloc (max_addr);
74         if (process_arguments (argc, argv) == ERROR)
75                 usage (_("Could not parse arguments"));
76         exit;
78         /* Set signal handling and alarm */
79         if (signal (SIGALRM, popen_timeout_alarm_handler) == SIG_ERR) {
80                 printf (_("Cannot catch SIGALRM"));
81                 return STATE_UNKNOWN;
82         }
84         /* handle timeouts gracefully */
85         alarm (timeout_interval);
87         for (i = 0 ; i < n_addresses ; i++) {
89                 /* does the host address of number of packets argument come first? */
90 #ifdef PING6_COMMAND
91 # ifdef PING_PACKETS_FIRST
92         if (is_inet6_addr(addresses[i]) && address_family != AF_INET)
93                 asprintf (&cmd, PING6_COMMAND, max_packets, addresses[i]);
94         else
95                 asprintf (&cmd, PING_COMMAND, max_packets, addresses[i]);
96 # else
97         if (is_inet6_addr(addresses[i]) && address_family != AF_INET) 
98                 asprintf (&cmd, PING6_COMMAND, addresses[i], max_packets);
99         else
100                 asprintf (&cmd, PING_COMMAND, addresses[i], max_packets);
101 # endif
102 #else /* USE_IPV6 */
103 # ifdef PING_PACKETS_FIRST
104                 asprintf (&cmd, PING_COMMAND, max_packets, addresses[i]);
105 # else
106                 asprintf (&cmd, PING_COMMAND, addresses[i], max_packets);
107 # endif
108 #endif /* USE_IPV6 */
110                 if (verbose)
111                         printf ("%s ==> ", cmd);
113                 /* run the command */
114                 this_result = run_ping (cmd, addresses[i]);
116                 if (pl == UNKNOWN_PACKET_LOSS || rta == UNKNOWN_TRIP_TIME) {
117                         printf ("%s\n", cmd);
118                         die (STATE_UNKNOWN,
119                                    _("Error: Could not interpret output from ping command\n"));
120                 }
122                 if (pl >= cpl || rta >= crta || rta < 0)
123                         this_result = STATE_CRITICAL;
124                 else if (pl >= wpl || rta >= wrta)
125                         this_result = STATE_WARNING;
126                 else if (pl >= 0 && rta >= 0)
127                         this_result = max_state (STATE_OK, this_result);        
128         
129                 if (n_addresses > 1 && this_result != STATE_UNKNOWN)
130                         die (STATE_OK, "%s is alive\n", addresses[i]);
132                 if (display_html == TRUE)
133                         printf ("<A HREF='%s/traceroute.cgi?%s'>", CGIURL, addresses[i]);
134                 if (pl == 100)
135                         printf (_("PING %s - %sPacket loss = %d%%"), state_text (this_result), warn_text,
136                                                         pl);
137                 else
138                         printf (_("PING %s - %sPacket loss = %d%%, RTA = %2.2f ms"),
139                                                         state_text (this_result), warn_text, pl, rta);
140                 if (display_html == TRUE)
141                         printf ("</A>");
142                 printf ("\n");
144                 if (verbose)
145                         printf ("%f:%d%% %f:%d%%\n", wrta, wpl, crta, cpl);
147                 result = max_state (result, this_result);
149         }
151         return result;
158 \f
159 /* process command-line arguments */
160 int
161 process_arguments (int argc, char **argv)
163         int c = 1;
164         char *ptr;
166         int option_index = 0;
167         static struct option long_options[] = {
168                 STD_LONG_OPTS,
169                 {"packets", required_argument, 0, 'p'},
170                 {"nohtml", no_argument, 0, 'n'},
171                 {"link", no_argument, 0, 'L'},
172                 {"use-ipv4", no_argument, 0, '4'},
173                 {"use-ipv6", no_argument, 0, '6'},
174                 {0, 0, 0, 0}
175         };
177         if (argc < 2)
178                 return ERROR;
180         for (c = 1; c < argc; c++) {
181                 if (strcmp ("-to", argv[c]) == 0)
182                         strcpy (argv[c], "-t");
183                 if (strcmp ("-nohtml", argv[c]) == 0)
184                         strcpy (argv[c], "-n");
185         }
187         while (1) {
188                 c = getopt_long (argc, argv, "VvhnL46t:c:w:H:p:", long_options, &option_index);
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 '4':       /* IPv4 only */
209                         address_family = AF_INET;
210                         break;
211                 case '6':       /* IPv6 only */
212 #ifdef USE_IPV6
213                         address_family = AF_INET6;
214 #else
215                         usage (_("IPv6 support not available\n"));
216 #endif
217                         break;
218                 case 'H':       /* hostname */
219                         ptr=optarg;
220                         while (1) {
221                                 n_addresses++;
222                                 if (n_addresses > max_addr) {
223                                         max_addr *= 2;
224                                         addresses = realloc (addresses, max_addr);
225                                         if (addresses == NULL)
226                                                 die (STATE_UNKNOWN, _("Could not realloc() addresses\n"));
227                                 }
228                                 addresses[n_addresses-1] = ptr;
229                                 if ((ptr = index (ptr, ','))) {
230                                         strcpy (ptr, "");
231                                         ptr += sizeof(char);
232                                 } else {
233                                         break;
234                                 }
235                         }
236                         break;
237                 case 'p':       /* number of packets to send */
238                         if (is_intnonneg (optarg))
239                                 max_packets = atoi (optarg);
240                         else
241                                 usage2 (_("<max_packets> (%s) must be a non-negative number\n"), optarg);
242                         break;
243                 case 'n':       /* no HTML */
244                         display_html = FALSE;
245                         break;
246                 case 'L':       /* show HTML */
247                         display_html = TRUE;
248                         break;
249                 case 'c':
250                         get_threshold (optarg, &crta, &cpl);
251                         break;
252                 case 'w':
253                         get_threshold (optarg, &wrta, &wpl);
254                         break;
255                 }
256         }
258         c = optind;
259         if (c == argc)
260                 return validate_arguments ();
262         if (addresses[0] == NULL) {
263                 if (is_host (argv[c]) == FALSE) {
264                         printf (_("Invalid host name/address: %s\n\n"), argv[c]);
265                         return ERROR;
266                 } else {
267                         addresses[0] = argv[c++];
268                         if (c == argc)
269                                 return validate_arguments ();
270                 }
271         }
273         if (wpl == UNKNOWN_PACKET_LOSS) {
274                 if (is_intpercent (argv[c]) == FALSE) {
275                         printf (_("<wpl> (%s) must be an integer percentage\n"), argv[c]);
276                         return ERROR;
277                 } else {
278                         wpl = atoi (argv[c++]);
279                         if (c == argc)
280                                 return validate_arguments ();
281                 }
282         }
284         if (cpl == UNKNOWN_PACKET_LOSS) {
285                 if (is_intpercent (argv[c]) == FALSE) {
286                         printf (_("<cpl> (%s) must be an integer percentage\n"), argv[c]);
287                         return ERROR;
288                 } else {
289                         cpl = atoi (argv[c++]);
290                         if (c == argc)
291                                 return validate_arguments ();
292                 }
293         }
295         if (wrta == UNKNOWN_TRIP_TIME) {
296                 if (is_negative (argv[c])) {
297                         printf (_("<wrta> (%s) must be a non-negative number\n"), argv[c]);
298                         return ERROR;
299                 } else {
300                         wrta = atof (argv[c++]);
301                         if (c == argc)
302                                 return validate_arguments ();
303                 }
304         }
306         if (crta == UNKNOWN_TRIP_TIME) {
307                 if (is_negative (argv[c])) {
308                         printf (_("<crta> (%s) must be a non-negative number\n"), argv[c]);
309                         return ERROR;
310                 } else {
311                         crta = atof (argv[c++]);
312                         if (c == argc)
313                                 return validate_arguments ();
314                 }
315         }
317         if (max_packets == -1) {
318                 if (is_intnonneg (argv[c])) {
319                         max_packets = atoi (argv[c++]);
320                 } else {
321                         printf (_("<max_packets> (%s) must be a non-negative number\n"), argv[c]);
322                         return ERROR;
323                 }
324         }
326         return validate_arguments ();
329 int
330 get_threshold (char *arg, float *trta, int *tpl)
332         if (is_intnonneg (arg) && sscanf (arg, "%f", trta) == 1)
333                 return OK;
334         else if (strpbrk (arg, ",:") && strstr (arg, "%") && sscanf (arg, "%f%*[:,]%d%%", trta, tpl) == 2)
335                 return OK;
336         else if (strstr (arg, "%") && sscanf (arg, "%d%%", tpl) == 1) 
337                 return OK;
339         usage2 (_("%s: Warning threshold must be integer or percentage!\n\n"), arg);
340         return STATE_UNKNOWN;
343 int
344 validate_arguments ()
346         float max_seconds;
347         int i;
349         if (wrta == UNKNOWN_TRIP_TIME) {
350                 printf (_("<wrta> was not set\n"));
351                 return ERROR;
352         }
353         else if (crta == UNKNOWN_TRIP_TIME) {
354                 printf (_("<crta> was not set\n"));
355                 return ERROR;
356         }
357         else if (wpl == UNKNOWN_PACKET_LOSS) {
358                 printf (_("<wpl> was not set\n"));
359                 return ERROR;
360         }
361         else if (cpl == UNKNOWN_PACKET_LOSS) {
362                 printf (_("<cpl> was not set\n"));
363                 return ERROR;
364         }
365         else if (wrta > crta) {
366                 printf (_("<wrta> (%f) cannot be larger than <crta> (%f)\n"), wrta, crta);
367                 return ERROR;
368         }
369         else if (wpl > cpl) {
370                 printf (_("<wpl> (%d) cannot be larger than <cpl> (%d)\n"), wpl, cpl);
371                 return ERROR;
372         }
374         if (max_packets == -1)
375                 max_packets = DEFAULT_MAX_PACKETS;
377         max_seconds = crta / 1000.0 * max_packets + max_packets;
378         if (max_seconds > timeout_interval)
379                 timeout_interval = (int)max_seconds;
381         for (i=0; i<n_addresses; i++) {
382                 if (is_host(addresses[i]) == FALSE)
383                         usage2 (_("Invalid host name/address"), addresses[i]);
384         }
386         return OK;
393 \f
394 int
395 run_ping (char *cmd, char *server_address)
397         char buf[MAX_INPUT_BUFFER];
398         int result = STATE_UNKNOWN;
400         if ((child_process = spopen (cmd)) == NULL) {
401                 printf (_("Cannot open pipe: "));
402                 die (STATE_UNKNOWN, cmd);
403         }
404         child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
405         if (child_stderr == NULL)
406                 printf (_("Cannot open stderr for %s\n"), cmd);
408         while (fgets (buf, MAX_INPUT_BUFFER - 1, child_process)) {
410                 if (strstr (buf, _("(DUP!)"))) {
411                         result = max_state (result, STATE_WARNING);
412                         warn_text = strdup (WARN_DUPLICATES);
413                         if (!warn_text)
414                                 die (STATE_UNKNOWN, _("unable to realloc warn_text"));
415                 }
417                 /* get the percent loss statistics */
418                 if(sscanf(buf,"%*d packets transmitted, %*d packets received, +%*d errors, %d%% packet loss",&pl)==1 ||
419                          sscanf(buf,"%*d packets transmitted, %*d packets received, %d%% packet loss",&pl)==1   ||
420                          sscanf(buf,"%*d packets transmitted, %*d packets received, %d%% loss, time",&pl)==1 ||
421                          sscanf(buf,"%*d packets transmitted, %*d received, %d%% loss, time", &pl)==1)
422                         continue;
424                 /* get the round trip average */
425                 else
426                         if(sscanf(buf,"round-trip min/avg/max = %*f/%f/%*f",&rta)==1 ||
427                                  sscanf(buf,"round-trip min/avg/max/mdev = %*f/%f/%*f/%*f",&rta)==1 ||
428                                  sscanf(buf,"round-trip min/avg/max/sdev = %*f/%f/%*f/%*f",&rta)==1 ||
429                                  sscanf(buf,"round-trip min/avg/max/stddev = %*f/%f/%*f/%*f",&rta)==1 ||
430                                  sscanf(buf,"round-trip min/avg/max/std-dev = %*f/%f/%*f/%*f",&rta)==1 ||
431                                  sscanf(buf,"round-trip (ms) min/avg/max = %*f/%f/%*f",&rta)==1 ||
432                                  sscanf(buf,"rtt min/avg/max/mdev = %*f/%f/%*f/%*f ms",&rta)==1)
433                         continue;
434         }
436         /* this is needed because there is no rta if all packets are lost */
437         if (pl == 100)
438                 rta = crta;
440         /* check stderr */
441         while (fgets (buf, MAX_INPUT_BUFFER - 1, child_stderr)) {
442                 if (strstr(buf,"Warning: no SO_TIMESTAMP support, falling back to SIOCGSTAMP"))
443                                 continue;
445                 if (strstr (buf, "Network is unreachable"))
446                         die (STATE_CRITICAL,
447                                    _("PING CRITICAL - Network unreachable (%s)"),
448                                    server_address);
449                 else if (strstr (buf, "Destination Host Unreachable"))
450                         die (STATE_CRITICAL,
451                                    _("PING CRITICAL - Host Unreachable (%s)"),
452                                    server_address);
453                 else if (strstr (buf, "unknown host" ))
454                         die (STATE_CRITICAL,
455                                    _("PING CRITICAL - Host not found (%s)"),
456                                    server_address);
458                 if (strlen (warn_text) == 0)
459                         warn_text = strdup (buf);
460                 else if (asprintf (&warn_text, "%s %s", warn_text, buf) == -1)
461                         die (STATE_UNKNOWN, _("unable to realloc warn_text"));
463                 if (strstr (buf, "DUPLICATES FOUND"))
464                         result = max_state (result, STATE_WARNING);
465                 else
466                         result = STATE_CRITICAL ;
467         }
468         (void) fclose (child_stderr);
471         /* close the pipe - WARNING if status is set */
472         if (spclose (child_process))
473                 result = max_state (result, STATE_WARNING);
475         return result;
482 \f
483 void
484 print_usage (void)
486         printf (\
487 "Usage: %s -H <host_address> -w <wrta>,<wpl>%% -c <crta>,<cpl>%%\n\
488   [-p packets] [-t timeout] [-L] [-4|-6]\n", progname);
489         printf (_(UT_HLP_VRS), progname, progname);
492 void
493 print_help (void)
495         print_revision (progname, revision);
497         printf (_("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>"));
498         printf (_(COPYRIGHT), copyright, email);
500         printf (_("Use ping to check connection statistics for a remote host.\n\n"));
502         print_usage ();
504         printf (_(UT_HELP_VRSN));
506         printf (_(UT_IPv46));
508         printf (_("\
509 -H, --hostname=HOST\n\
510    host to ping\n\
511 -w, --warning=THRESHOLD\n\
512    warning threshold pair\n\
513 -c, --critical=THRESHOLD\n\
514    critical threshold pair\n\
515 -p, --packets=INTEGER\n\
516    number of ICMP ECHO packets to send (Default: %d)\n\
517 -L, --link\n\
518    show HTML in the plugin output (obsoleted by urlize)\n"),
519                 DEFAULT_MAX_PACKETS);
521         printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
523         printf (_("\
524 THRESHOLD is <rta>,<pl>%% where <rta> is the round trip average travel\n\
525 time (ms) which triggers a WARNING or CRITICAL state, and <pl> is the\n\
526 percentage of packet loss to trigger an alarm state.\n\n"));
528         printf (_("\
529 This plugin uses the ping command to probe the specified host for packet loss\n\
530 (percentage) and round trip average (milliseconds). It can produce HTML output\n\
531 linking to a traceroute CGI contributed by Ian Cass. The CGI can be found in\n\
532 the contrib area of the downloads section at http://www.nagios.org\n\n"));
534         printf (_(UT_SUPPORT));