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);
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;
146 }
147 \f
149 /* process command-line arguments */
150 int
151 process_arguments (int argc, char **argv)
152 {
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 ();
317 }
319 int
320 get_threshold (char *arg, float *trta, int *tpl)
321 {
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;
331 }
333 int
334 validate_arguments ()
335 {
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;
377 }
378 \f
380 int
381 run_ping (char *command_line, char *server_address)
382 {
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 }
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;
481 }
484 void
485 print_usage (void)
486 {
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);
491 }
493 void
494 print_help (void)
495 {
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));
536 }