Code

fd73b8cef8eb8f55d7f2e9506825d6cf7291a16a
[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 ((size_t)max_addr);
74         if (process_arguments (argc, argv) == ERROR)
75                 usage (_("Could not parse arguments"));
77         /* Set signal handling and alarm */
78         if (signal (SIGALRM, popen_timeout_alarm_handler) == SIG_ERR) {
79                 printf (_("Cannot catch SIGALRM"));
80                 return STATE_UNKNOWN;
81         }
83         /* handle timeouts gracefully */
84         alarm (timeout_interval);
86         for (i = 0 ; i < n_addresses ; i++) {
88                 /* does the host address of number of packets argument come first? */
89 #ifdef PING6_COMMAND
90 # ifdef PING_PACKETS_FIRST
91         if (is_inet6_addr(addresses[i]) && address_family != AF_INET)
92                 asprintf (&cmd, PING6_COMMAND, max_packets, addresses[i]);
93         else
94                 asprintf (&cmd, PING_COMMAND, max_packets, addresses[i]);
95 # else
96         if (is_inet6_addr(addresses[i]) && address_family != AF_INET) 
97                 asprintf (&cmd, PING6_COMMAND, addresses[i], max_packets);
98         else
99                 asprintf (&cmd, PING_COMMAND, addresses[i], max_packets);
100 # endif
101 #else /* USE_IPV6 */
102 # ifdef PING_PACKETS_FIRST
103                 asprintf (&cmd, PING_COMMAND, max_packets, addresses[i]);
104 # else
105                 asprintf (&cmd, PING_COMMAND, addresses[i], max_packets);
106 # endif
107 #endif /* USE_IPV6 */
109                 if (verbose)
110                         printf ("%s ==> ", cmd);
112                 /* run the command */
113                 this_result = run_ping (cmd, addresses[i]);
115                 if (pl == UNKNOWN_PACKET_LOSS || rta < 0.0) {
116                         printf ("%s\n", cmd);
117                         die (STATE_UNKNOWN,
118                                    _("Error: Could not interpret output from ping command\n"));
119                 }
121                 if (pl >= cpl || rta >= crta || rta < 0)
122                         this_result = STATE_CRITICAL;
123                 else if (pl >= wpl || rta >= wrta)
124                         this_result = STATE_WARNING;
125                 else if (pl >= 0 && rta >= 0)
126                         this_result = max_state (STATE_OK, this_result);        
127         
128                 if (n_addresses > 1 && this_result != STATE_UNKNOWN)
129                         die (STATE_OK, "%s is alive\n", addresses[i]);
131                 if (display_html == TRUE)
132                         printf ("<A HREF='%s/traceroute.cgi?%s'>", CGIURL, addresses[i]);
133                 if (pl == 100)
134                         printf (_("PING %s - %sPacket loss = %d%%"), state_text (this_result), warn_text,
135                                                         pl);
136                 else
137                         printf (_("PING %s - %sPacket loss = %d%%, RTA = %2.2f ms"),
138                                                         state_text (this_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                 result = max_state (result, this_result);
148         }
150         return result;
157 \f
158 /* process command-line arguments */
159 int
160 process_arguments (int argc, char **argv)
162         int c = 1;
163         char *ptr;
165         int option_index = 0;
166         static struct option long_options[] = {
167                 STD_LONG_OPTS,
168                 {"packets", required_argument, 0, 'p'},
169                 {"nohtml", no_argument, 0, 'n'},
170                 {"link", no_argument, 0, 'L'},
171                 {"use-ipv4", no_argument, 0, '4'},
172                 {"use-ipv6", no_argument, 0, '6'},
173                 {0, 0, 0, 0}
174         };
176         if (argc < 2)
177                 return ERROR;
179         for (c = 1; c < argc; c++) {
180                 if (strcmp ("-to", argv[c]) == 0)
181                         strcpy (argv[c], "-t");
182                 if (strcmp ("-nohtml", argv[c]) == 0)
183                         strcpy (argv[c], "-n");
184         }
186         while (1) {
187                 c = getopt_long (argc, argv, "VvhnL46t:c:w:H:p:", long_options, &option_index);
189                 if (c == -1 || c == EOF)
190                         break;
192                 switch (c) {
193                 case '?':       /* usage */
194                         usage3 (_("Unknown argument"), optopt);
195                         break;
196                 case 'h':       /* help */
197                         print_help ();
198                         exit (STATE_OK);
199                         break;
200                 case 'V':       /* version */
201                         print_revision (progname, revision);
202                         exit (STATE_OK);
203                         break;
204                 case 't':       /* timeout period */
205                         timeout_interval = atoi (optarg);
206                         break;
207                 case 'v':       /* verbose mode */
208                         verbose = TRUE;
209                         break;
210                 case '4':       /* IPv4 only */
211                         address_family = AF_INET;
212                         break;
213                 case '6':       /* IPv6 only */
214 #ifdef USE_IPV6
215                         address_family = AF_INET6;
216 #else
217                         usage (_("IPv6 support not available\n"));
218 #endif
219                         break;
220                 case 'H':       /* hostname */
221                         ptr=optarg;
222                         while (1) {
223                                 n_addresses++;
224                                 if (n_addresses > max_addr) {
225                                         max_addr *= 2;
226                                         addresses = realloc (addresses, (size_t)max_addr);
227                                         if (addresses == NULL)
228                                                 die (STATE_UNKNOWN, _("Could not realloc() addresses\n"));
229                                 }
230                                 addresses[n_addresses-1] = ptr;
231                                 if ((ptr = index (ptr, ','))) {
232                                         strcpy (ptr, "");
233                                         ptr += sizeof(char);
234                                 } else {
235                                         break;
236                                 }
237                         }
238                         break;
239                 case 'p':       /* number of packets to send */
240                         if (is_intnonneg (optarg))
241                                 max_packets = atoi (optarg);
242                         else
243                                 usage2 (_("<max_packets> (%s) must be a non-negative number\n"), optarg);
244                         break;
245                 case 'n':       /* no HTML */
246                         display_html = FALSE;
247                         break;
248                 case 'L':       /* show HTML */
249                         display_html = TRUE;
250                         break;
251                 case 'c':
252                         get_threshold (optarg, &crta, &cpl);
253                         break;
254                 case 'w':
255                         get_threshold (optarg, &wrta, &wpl);
256                         break;
257                 }
258         }
260         c = optind;
261         if (c == argc)
262                 return validate_arguments ();
264         if (addresses[0] == NULL) {
265                 if (is_host (argv[c]) == FALSE) {
266                         printf (_("Invalid host name/address: %s\n\n"), argv[c]);
267                         return ERROR;
268                 } else {
269                         addresses[0] = argv[c++];
270                         if (c == argc)
271                                 return validate_arguments ();
272                 }
273         }
275         if (wpl == UNKNOWN_PACKET_LOSS) {
276                 if (is_intpercent (argv[c]) == FALSE) {
277                         printf (_("<wpl> (%s) must be an integer percentage\n"), argv[c]);
278                         return ERROR;
279                 } else {
280                         wpl = atoi (argv[c++]);
281                         if (c == argc)
282                                 return validate_arguments ();
283                 }
284         }
286         if (cpl == UNKNOWN_PACKET_LOSS) {
287                 if (is_intpercent (argv[c]) == FALSE) {
288                         printf (_("<cpl> (%s) must be an integer percentage\n"), argv[c]);
289                         return ERROR;
290                 } else {
291                         cpl = atoi (argv[c++]);
292                         if (c == argc)
293                                 return validate_arguments ();
294                 }
295         }
297         if (wrta < 0.0) {
298                 if (is_negative (argv[c])) {
299                         printf (_("<wrta> (%s) must be a non-negative number\n"), argv[c]);
300                         return ERROR;
301                 } else {
302                         wrta = atof (argv[c++]);
303                         if (c == argc)
304                                 return validate_arguments ();
305                 }
306         }
308         if (crta < 0.0) {
309                 if (is_negative (argv[c])) {
310                         printf (_("<crta> (%s) must be a non-negative number\n"), argv[c]);
311                         return ERROR;
312                 } else {
313                         crta = atof (argv[c++]);
314                         if (c == argc)
315                                 return validate_arguments ();
316                 }
317         }
319         if (max_packets == -1) {
320                 if (is_intnonneg (argv[c])) {
321                         max_packets = atoi (argv[c++]);
322                 } else {
323                         printf (_("<max_packets> (%s) must be a non-negative number\n"), argv[c]);
324                         return ERROR;
325                 }
326         }
328         return validate_arguments ();
331 int
332 get_threshold (char *arg, float *trta, int *tpl)
334         if (is_intnonneg (arg) && sscanf (arg, "%f", trta) == 1)
335                 return OK;
336         else if (strpbrk (arg, ",:") && strstr (arg, "%") && sscanf (arg, "%f%*[:,]%d%%", trta, tpl) == 2)
337                 return OK;
338         else if (strstr (arg, "%") && sscanf (arg, "%d%%", tpl) == 1) 
339                 return OK;
341         usage2 (_("%s: Warning threshold must be integer or percentage!\n\n"), arg);
342         return STATE_UNKNOWN;
345 int
346 validate_arguments ()
348         float max_seconds;
349         int i;
351         if (wrta < 0.0) {
352                 printf (_("<wrta> was not set\n"));
353                 return ERROR;
354         }
355         else if (crta < 0.0) {
356                 printf (_("<crta> was not set\n"));
357                 return ERROR;
358         }
359         else if (wpl == UNKNOWN_PACKET_LOSS) {
360                 printf (_("<wpl> was not set\n"));
361                 return ERROR;
362         }
363         else if (cpl == UNKNOWN_PACKET_LOSS) {
364                 printf (_("<cpl> was not set\n"));
365                 return ERROR;
366         }
367         else if (wrta > crta) {
368                 printf (_("<wrta> (%f) cannot be larger than <crta> (%f)\n"), wrta, crta);
369                 return ERROR;
370         }
371         else if (wpl > cpl) {
372                 printf (_("<wpl> (%d) cannot be larger than <cpl> (%d)\n"), wpl, cpl);
373                 return ERROR;
374         }
376         if (max_packets == -1)
377                 max_packets = DEFAULT_MAX_PACKETS;
379         max_seconds = crta / 1000.0 * max_packets + max_packets;
380         if (max_seconds > timeout_interval)
381                 timeout_interval = (int)max_seconds;
383         for (i=0; i<n_addresses; i++) {
384                 if (is_host(addresses[i]) == FALSE)
385                         usage2 (_("Invalid host name/address"), addresses[i]);
386         }
388         return OK;
395 \f
396 int
397 run_ping (char *cmd, char *server_address)
399         char buf[MAX_INPUT_BUFFER];
400         int result = STATE_UNKNOWN;
402         if ((child_process = spopen (cmd)) == NULL)
403                 die (STATE_UNKNOWN, _("Cannot open pipe: %s"), cmd);
405         child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
406         if (child_stderr == NULL)
407                 printf (_("Cannot open stderr for %s\n"), cmd);
409         while (fgets (buf, MAX_INPUT_BUFFER - 1, child_process)) {
411                 if (strstr (buf, _("(DUP!)"))) {
412                         result = max_state (result, STATE_WARNING);
413                         warn_text = strdup (WARN_DUPLICATES);
414                         if (!warn_text)
415                                 die (STATE_UNKNOWN, _("unable to realloc warn_text"));
416                 }
418                 /* get the percent loss statistics */
419                 if(sscanf(buf,"%*d packets transmitted, %*d packets received, +%*d errors, %d%% packet loss",&pl)==1 ||
420                          sscanf(buf,"%*d packets transmitted, %*d packets received, %d%% packet loss",&pl)==1   ||
421                          sscanf(buf,"%*d packets transmitted, %*d packets received, %d%% loss, time",&pl)==1 ||
422                          sscanf(buf,"%*d packets transmitted, %*d received, %d%% loss, time", &pl)==1)
423                         continue;
425                 /* get the round trip average */
426                 else
427                         if(sscanf(buf,"round-trip min/avg/max = %*f/%f/%*f",&rta)==1 ||
428                                  sscanf(buf,"round-trip min/avg/max/mdev = %*f/%f/%*f/%*f",&rta)==1 ||
429                                  sscanf(buf,"round-trip min/avg/max/sdev = %*f/%f/%*f/%*f",&rta)==1 ||
430                                  sscanf(buf,"round-trip min/avg/max/stddev = %*f/%f/%*f/%*f",&rta)==1 ||
431                                  sscanf(buf,"round-trip min/avg/max/std-dev = %*f/%f/%*f/%*f",&rta)==1 ||
432                                  sscanf(buf,"round-trip (ms) min/avg/max = %*f/%f/%*f",&rta)==1 ||
433                                  sscanf(buf,"rtt min/avg/max/mdev = %*f/%f/%*f/%*f ms",&rta)==1)
434                         continue;
435         }
437         /* this is needed because there is no rta if all packets are lost */
438         if (pl == 100)
439                 rta = crta;
441         /* check stderr */
442         while (fgets (buf, MAX_INPUT_BUFFER - 1, child_stderr)) {
443                 if (strstr(buf,"Warning: no SO_TIMESTAMP support, falling back to SIOCGSTAMP"))
444                                 continue;
446                 if (strstr (buf, "Network is unreachable"))
447                         die (STATE_CRITICAL,
448                                    _("PING CRITICAL - Network unreachable (%s)"),
449                                    server_address);
450                 else if (strstr (buf, "Destination Host Unreachable"))
451                         die (STATE_CRITICAL,
452                                    _("PING CRITICAL - Host Unreachable (%s)"),
453                                    server_address);
454                 else if (strstr (buf, "unknown host" ))
455                         die (STATE_CRITICAL,
456                                    _("PING CRITICAL - Host not found (%s)"),
457                                    server_address);
459                 if (warn_text == NULL)
460                         warn_text = strdup (buf);
461                 else if (asprintf (&warn_text, "%s %s", warn_text, buf) == -1)
462                         die (STATE_UNKNOWN, _("unable to realloc warn_text"));
464                 if (strstr (buf, "DUPLICATES FOUND"))
465                         result = max_state (result, STATE_WARNING);
466                 else
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         if (warn_text == NULL)
477                 warn_text = strdup("");
479         return result;
486 \f
487 void
488 print_usage (void)
490         printf (\
491 "Usage: %s -H <host_address> -w <wrta>,<wpl>%% -c <crta>,<cpl>%%\n\
492   [-p packets] [-t timeout] [-L] [-4|-6]\n", progname);
493         printf (_(UT_HLP_VRS), progname, progname);
496 void
497 print_help (void)
499         print_revision (progname, revision);
501         printf (_("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>"));
502         printf (_(COPYRIGHT), copyright, email);
504         printf (_("Use ping to check connection statistics for a remote host.\n\n"));
506         print_usage ();
508         printf (_(UT_HELP_VRSN));
510         printf (_(UT_IPv46));
512         printf (_("\
513 -H, --hostname=HOST\n\
514    host to ping\n\
515 -w, --warning=THRESHOLD\n\
516    warning threshold pair\n\
517 -c, --critical=THRESHOLD\n\
518    critical threshold pair\n\
519 -p, --packets=INTEGER\n\
520    number of ICMP ECHO packets to send (Default: %d)\n\
521 -L, --link\n\
522    show HTML in the plugin output (obsoleted by urlize)\n"),
523                 DEFAULT_MAX_PACKETS);
525         printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
527         printf (_("\
528 THRESHOLD is <rta>,<pl>%% where <rta> is the round trip average travel\n\
529 time (ms) which triggers a WARNING or CRITICAL state, and <pl> is the\n\
530 percentage of packet loss to trigger an alarm state.\n\n"));
532         printf (_("\
533 This plugin uses the ping command to probe the specified host for packet loss\n\
534 (percentage) and round trip average (milliseconds). It can produce HTML output\n\
535 linking to a traceroute CGI contributed by Ian Cass. The CGI can be found in\n\
536 the contrib area of the downloads section at http://www.nagios.org\n\n"));
538         printf (_(UT_SUPPORT));