Code

markup for translation
[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 UNKNOWN_PACKET_LOSS 200 /* 200% */
30 #define UNKNOWN_TRIP_TIME -1.0  /* -1 seconds */
31 #define DEFAULT_MAX_PACKETS 5           /* default no. of ICMP ECHO packets */
33 #define WARN_DUPLICATES "DUPLICATES FOUND! "
35 int process_arguments (int, char **);
36 int get_threshold (char *, float *, int *);
37 int validate_arguments (void);
38 int run_ping (char *, char *);
39 void print_usage (void);
40 void print_help (void);
42 int display_html = FALSE;
43 int wpl = UNKNOWN_PACKET_LOSS;
44 int cpl = UNKNOWN_PACKET_LOSS;
45 float wrta = UNKNOWN_TRIP_TIME;
46 float crta = UNKNOWN_TRIP_TIME;
47 char **addresses = NULL;
48 int n_addresses;
49 int max_addr = 1;
50 int max_packets = -1;
51 int verbose = FALSE;
53 float rta = UNKNOWN_TRIP_TIME;
54 int pl = UNKNOWN_PACKET_LOSS;
56 char *warn_text = NULL;
58 int
59 main (int argc, char **argv)
60 {
61         char *command_line = NULL;
62         int result = STATE_UNKNOWN;
63         int this_result = STATE_UNKNOWN;
64         int i;
66         addresses = malloc (max_addr);
68         if (process_arguments (argc, argv) == ERROR)
69                 usage (_("Could not parse arguments"));
70         exit;
72         /* Set signal handling and alarm */
73         if (signal (SIGALRM, popen_timeout_alarm_handler) == SIG_ERR) {
74                 printf (_("Cannot catch SIGALRM"));
75                 return STATE_UNKNOWN;
76         }
78         /* handle timeouts gracefully */
79         alarm (timeout_interval);
81         for (i = 0 ; i < n_addresses ; i++) {
83                 /* does the host address of number of packets argument come first? */
84 #ifdef PING6_COMMAND
85 # ifdef PING_PACKETS_FIRST
86         if (is_inet6_addr(addresses[i]) && address_family != AF_INET)
87                 asprintf (&command_line, PING6_COMMAND, max_packets, addresses[i]);
88         else
89                 asprintf (&command_line, PING_COMMAND, max_packets, addresses[i]);
90 # else
91         if (is_inet6_addr(addresses[i]) && address_family != AF_INET) 
92                 asprintf (&command_line, PING6_COMMAND, addresses[i], max_packets);
93         else
94                 asprintf (&command_line, PING_COMMAND, addresses[i], max_packets);
95 # endif
96 #else /* USE_IPV6 */
97 # ifdef PING_PACKETS_FIRST
98                 asprintf (&command_line, PING_COMMAND, max_packets, addresses[i]);
99 # else
100                 asprintf (&command_line, PING_COMMAND, addresses[i], max_packets);
101 # endif
102 #endif /* USE_IPV6 */
104                 if (verbose)
105                         printf ("%s ==> ", command_line);
107                 /* run the command */
108                 this_result = run_ping (command_line, addresses[i]);
110                 if (pl == UNKNOWN_PACKET_LOSS || rta == UNKNOWN_TRIP_TIME) {
111                         printf ("%s\n", command_line);
112                         terminate (STATE_UNKNOWN,
113                                    _("Error: Could not interpret output from ping command\n"));
114                 }
116                 if (pl >= cpl || rta >= crta || rta < 0)
117                         this_result = STATE_CRITICAL;
118                 else if (pl >= wpl || rta >= wrta)
119                         this_result = STATE_WARNING;
120                 else if (pl >= 0 && rta >= 0)
121                         this_result = max_state (STATE_OK, this_result);        
122         
123                 if (n_addresses > 1 && this_result != STATE_UNKNOWN)
124                         terminate (STATE_OK, "%s is alive\n", addresses[i]);
126                 if (display_html == TRUE)
127                         printf ("<A HREF='%s/traceroute.cgi?%s'>", CGIURL, addresses[i]);
128                 if (pl == 100)
129                         printf (_("PING %s - %sPacket loss = %d%%"), state_text (this_result), warn_text,
130                                                         pl);
131                 else
132                         printf (_("PING %s - %sPacket loss = %d%%, RTA = %2.2f ms"),
133                                                         state_text (this_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                 result = max_state (result, this_result);
143         }
145         return result;
147 \f
149 /* process command-line arguments */
150 int
151 process_arguments (int argc, char **argv)
153         int c = 1;
154         char *ptr;
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                 {"use-ipv4", no_argument, 0, '4'},
163                 {"use-ipv6", no_argument, 0, '6'},
164                 {0, 0, 0, 0}
165         };
167         if (argc < 2)
168                 return ERROR;
170         for (c = 1; c < argc; c++) {
171                 if (strcmp ("-to", argv[c]) == 0)
172                         strcpy (argv[c], "-t");
173                 if (strcmp ("-nohtml", argv[c]) == 0)
174                         strcpy (argv[c], "-n");
175         }
177         while (1) {
178                 c = getopt_long (argc, argv, "VvhnL46t:c:w:H:p:", long_options, &option_index);
180                 if (c == -1 || c == EOF)
181                         break;
183                 switch (c) {
184                 case '?':       /* usage */
185                         usage3 (_("Unknown argument"), optopt);
186                 case 'h':       /* help */
187                         print_help ();
188                         exit (STATE_OK);
189                 case 'V':       /* version */
190                         print_revision (progname, revision);
191                         exit (STATE_OK);
192                 case 't':       /* timeout period */
193                         timeout_interval = atoi (optarg);
194                         break;
195                 case 'v':       /* verbose mode */
196                         verbose = TRUE;
197                         break;
198                 case '4':       /* IPv4 only */
199                         address_family = AF_INET;
200                         break;
201                 case '6':       /* IPv6 only */
202 #ifdef USE_IPV6
203                         address_family = AF_INET6;
204 #else
205                         usage (_("IPv6 support not available\n"));
206 #endif
207                         break;
208                 case 'H':       /* hostname */
209                         ptr=optarg;
210                         while (1) {
211                                 n_addresses++;
212                                 if (n_addresses > max_addr) {
213                                         max_addr *= 2;
214                                         addresses = realloc (addresses, max_addr);
215                                         if (addresses == NULL)
216                                                 terminate (STATE_UNKNOWN, _("Could not realloc() addresses\n"));
217                                 }
218                                 addresses[n_addresses-1] = ptr;
219                                 if ((ptr = index (ptr, ','))) {
220                                         strcpy (ptr, "");
221                                         ptr += sizeof(char);
222                                 } else {
223                                         break;
224                                 }
225                         }
226                         break;
227                 case 'p':       /* number of packets to send */
228                         if (is_intnonneg (optarg))
229                                 max_packets = atoi (optarg);
230                         else
231                                 usage2 (_("<max_packets> (%s) must be a non-negative number\n"), optarg);
232                         break;
233                 case 'n':       /* no HTML */
234                         display_html = FALSE;
235                         break;
236                 case 'L':       /* show HTML */
237                         display_html = TRUE;
238                         break;
239                 case 'c':
240                         get_threshold (optarg, &crta, &cpl);
241                         break;
242                 case 'w':
243                         get_threshold (optarg, &wrta, &wpl);
244                         break;
245                 }
246         }
248         c = optind;
249         if (c == argc)
250                 return validate_arguments ();
252         if (addresses[0] == NULL) {
253                 if (is_host (argv[c]) == FALSE) {
254                         printf (_("Invalid host name/address: %s\n\n"), argv[c]);
255                         return ERROR;
256                 } else {
257                         addresses[0] = argv[c++];
258                         if (c == argc)
259                                 return validate_arguments ();
260                 }
261         }
263         if (wpl == UNKNOWN_PACKET_LOSS) {
264                 if (is_intpercent (argv[c]) == FALSE) {
265                         printf (_("<wpl> (%s) must be an integer percentage\n"), argv[c]);
266                         return ERROR;
267                 } else {
268                         wpl = atoi (argv[c++]);
269                         if (c == argc)
270                                 return validate_arguments ();
271                 }
272         }
274         if (cpl == UNKNOWN_PACKET_LOSS) {
275                 if (is_intpercent (argv[c]) == FALSE) {
276                         printf (_("<cpl> (%s) must be an integer percentage\n"), argv[c]);
277                         return ERROR;
278                 } else {
279                         cpl = atoi (argv[c++]);
280                         if (c == argc)
281                                 return validate_arguments ();
282                 }
283         }
285         if (wrta == UNKNOWN_TRIP_TIME) {
286                 if (is_negative (argv[c])) {
287                         printf (_("<wrta> (%s) must be a non-negative number\n"), argv[c]);
288                         return ERROR;
289                 } else {
290                         wrta = atof (argv[c++]);
291                         if (c == argc)
292                                 return validate_arguments ();
293                 }
294         }
296         if (crta == UNKNOWN_TRIP_TIME) {
297                 if (is_negative (argv[c])) {
298                         printf (_("<crta> (%s) must be a non-negative number\n"), argv[c]);
299                         return ERROR;
300                 } else {
301                         crta = atof (argv[c++]);
302                         if (c == argc)
303                                 return validate_arguments ();
304                 }
305         }
307         if (max_packets == -1) {
308                 if (is_intnonneg (argv[c])) {
309                         max_packets = atoi (argv[c++]);
310                 }       else {
311                         printf (_("<max_packets> (%s) must be a non-negative number\n"), argv[c]);
312                         return ERROR;
313                 }
314         }
316         return validate_arguments ();
319 int
320 get_threshold (char *arg, float *trta, int *tpl)
322         if (is_intnonneg (arg) && sscanf (arg, "%f", trta) == 1)
323                 return OK;
324         else if (strpbrk (arg, ",:") && strstr (arg, "%") && sscanf (arg, "%f%*[:,]%d%%", trta, tpl) == 2)
325                 return OK;
326         else if (strstr (arg, "%") && sscanf (arg, "%d%%", tpl) == 1) 
327                 return OK;
329         usage2 (_("%s: Warning threshold must be integer or percentage!\n\n"), arg);
330         return STATE_UNKNOWN;
333 int
334 validate_arguments ()
336         float max_seconds;
337         int i;
339         if (wrta == UNKNOWN_TRIP_TIME) {
340                 printf (_("<wrta> was not set\n"));
341                 return ERROR;
342         }
343         else if (crta == UNKNOWN_TRIP_TIME) {
344                 printf (_("<crta> was not set\n"));
345                 return ERROR;
346         }
347         else if (wpl == UNKNOWN_PACKET_LOSS) {
348                 printf (_("<wpl> was not set\n"));
349                 return ERROR;
350         }
351         else if (cpl == UNKNOWN_PACKET_LOSS) {
352                 printf (_("<cpl> was not set\n"));
353                 return ERROR;
354         }
355         else if (wrta > crta) {
356                 printf (_("<wrta> (%f) cannot be larger than <crta> (%f)\n"), wrta, crta);
357                 return ERROR;
358         }
359         else if (wpl > cpl) {
360                 printf (_("<wpl> (%d) cannot be larger than <cpl> (%d)\n"), wpl, cpl);
361                 return ERROR;
362         }
364         if (max_packets == -1)
365                 max_packets = DEFAULT_MAX_PACKETS;
367         max_seconds = crta / 1000.0 * max_packets + max_packets;
368         if (max_seconds > timeout_interval)
369                 timeout_interval = (int)max_seconds;
371         for (i=0; i<n_addresses; i++) {
372                 if (is_host(addresses[i]) == FALSE)
373                         usage2 (_("Invalid host name/address"), addresses[i]);
374         }
376         return OK;
378 \f
380 int
381 run_ping (char *command_line, char *server_address)
383         char buf[MAX_INPUT_BUFFER];
384         int result = STATE_UNKNOWN;
386         warn_text = malloc (1);
387         if (warn_text == NULL)
388                 terminate (STATE_UNKNOWN, _("unable to malloc warn_text"));
389         warn_text[0] = 0;
391         if ((child_process = spopen (command_line)) == NULL) {
392                 printf (_("Cannot open pipe: "));
393                 terminate (STATE_UNKNOWN, command_line);
394         }
395         child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
396         if (child_stderr == NULL)
397                 printf (_("Cannot open stderr for %s\n"), command_line);
399         while (fgets (buf, MAX_INPUT_BUFFER - 1, child_process)) {
401                 if (strstr (buf, _("(DUP!)"))) {
402                         /* cannot use the max function since STATE_UNKNOWN is max
403                         result = max (result, STATE_WARNING); */
404                         if( !(result == STATE_CRITICAL) ){
405                                 result = STATE_WARNING;
406                         }
407                         
408                         warn_text = realloc (warn_text, strlen (WARN_DUPLICATES) + 1);
409                         if (warn_text == NULL)
410                                 terminate (STATE_UNKNOWN, _("unable to realloc warn_text"));
411                         strcpy (warn_text, WARN_DUPLICATES);
412                 }
414                 /* get the percent loss statistics */
415                 if(sscanf(buf,"%*d packets transmitted, %*d packets received, +%*d errors, %d%% packet loss",&pl)==1 ||
416                          sscanf(buf,"%*d packets transmitted, %*d packets received, %d%% packet loss",&pl)==1   ||
417                          sscanf(buf,"%*d packets transmitted, %*d packets received, %d%% loss, time",&pl)==1 ||
418                          sscanf(buf,"%*d packets transmitted, %*d received, %d%% loss, time", &pl)==1)
419                         continue;
421                 /* get the round trip average */
422                 else
423                         if(sscanf(buf,"round-trip min/avg/max = %*f/%f/%*f",&rta)==1 ||
424                                  sscanf(buf,"round-trip min/avg/max/mdev = %*f/%f/%*f/%*f",&rta)==1 ||
425                                  sscanf(buf,"round-trip min/avg/max/sdev = %*f/%f/%*f/%*f",&rta)==1 ||
426                                  sscanf(buf,"round-trip min/avg/max/stddev = %*f/%f/%*f/%*f",&rta)==1 ||
427                                  sscanf(buf,"round-trip min/avg/max/std-dev = %*f/%f/%*f/%*f",&rta)==1 ||
428                                  sscanf(buf,"round-trip (ms) min/avg/max = %*f/%f/%*f",&rta)==1 ||
429                                  sscanf(buf,"rtt min/avg/max/mdev = %*f/%f/%*f/%*f ms",&rta)==1)
430                         continue;
431         }
433         /* this is needed because there is no rta if all packets are lost */
434         if (pl == 100)
435                 rta = crta;
438         /* check stderr */
439         while (fgets (buf, MAX_INPUT_BUFFER - 1, child_stderr)) {
440                 if (strstr(buf,"Warning: no SO_TIMESTAMP support, falling back to SIOCGSTAMP"))
441                                 continue;
443                 if (strstr (buf, "Network is unreachable"))
444                         terminate (STATE_CRITICAL,
445                                    _("PING CRITICAL - Network unreachable (%s)"),
446                                    server_address);
447                 else if (strstr (buf, "Destination Host Unreachable"))
448                         terminate (STATE_CRITICAL,
449                                    _("PING CRITICAL - Host Unreachable (%s)"),
450                                    server_address);
451                 else if (strstr (buf, "unknown host" ))
452                         terminate (STATE_CRITICAL,
453                                    _("PING CRITICAL - Host not found (%s)"),
454                                    server_address);
456                 warn_text =
457                         realloc (warn_text, strlen (warn_text) + strlen (buf) + 2);
458                 if (warn_text == NULL)
459                         terminate (STATE_UNKNOWN, _("unable to realloc warn_text"));
460                 if (strlen (warn_text) == 0)
461                         strcpy (warn_text, buf);
462                 else
463                         sprintf (warn_text, "%s %s", warn_text, buf);
465                 if (strstr (buf, "DUPLICATES FOUND")) {
466                         if( !(result == STATE_CRITICAL) ){
467                                 result = STATE_WARNING;
468                         }
469                 }
470                 else
471                         result = STATE_CRITICAL ;
472         }
473         (void) fclose (child_stderr);
476         /* close the pipe - WARNING if status is set */
477         if (spclose (child_process))
478                 result = max_state (result, STATE_WARNING);
480         return result;
484 void
485 print_usage (void)
487         printf (\
488 "Usage: %s -H <host_address> -w <wrta>,<wpl>%% -c <crta>,<cpl>%%\n\
489   [-p packets] [-t timeout] [-L] [-4|-6]\n", progname);
490         printf (_(UT_HLP_VRS), progname, progname);
493 void
494 print_help (void)
496         print_revision (progname, revision);
498         printf (_("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>"));
499         printf (_(COPYRIGHT), copyright, email);
501         printf (_("Use ping to check connection statistics for a remote host.\n\n"));
503         print_usage ();
505         printf (_(UT_HELP_VRSN));
507         printf (_(UT_IPv46));
509         printf (_("\
510 -H, --hostname=HOST\n\
511    host to ping\n\
512 -w, --warning=THRESHOLD\n\
513    warning threshold pair\n\
514 -c, --critical=THRESHOLD\n\
515    critical threshold pair\n\
516 -p, --packets=INTEGER\n\
517    number of ICMP ECHO packets to send (Default: %d)\n\
518 -L, --link\n\
519    show HTML in the plugin output (obsoleted by urlize)\n"),
520                 DEFAULT_MAX_PACKETS);
522         printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
524         printf (_("\
525 THRESHOLD is <rta>,<pl>%% where <rta> is the round trip average travel\n\
526 time (ms) which triggers a WARNING or CRITICAL state, and <pl> is the\n\
527 percentage of packet loss to trigger an alarm state.\n\n"));
529         printf (_("\
530 This plugin uses the ping command to probe the specified host for packet loss\n\
531 (percentage) and round trip average (milliseconds). It can produce HTML output\n\
532 linking to a traceroute CGI contributed by Ian Cass. The CGI can be found in\n\
533 the contrib area of the downloads section at http://www.nagios.org\n\n"));
535         printf (_(UT_SUPPORT));