eb3a6dbf9642fae6edf47f3fb53c3b13566c1afd
1 /******************************************************************************
2 *
3 * Nagios check_ping plugin
4 *
5 * License: GPL
6 * Copyright (c) 2000-2006 nagios-plugins team
7 *
8 * Last Modified: $Date$
9 *
10 * Description:
11 *
12 * This file contains the check_ping plugin
13 *
14 * Use the ping program to check connection statistics for a remote host.
15 *
16 *
17 * License Information:
18 *
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
23 *
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, write to the Free Software
31 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
33 $Id$
35 ******************************************************************************/
37 const char *progname = "check_ping";
38 const char *revision = "$Revision$";
39 const char *copyright = "2000-2006";
40 const char *email = "nagiosplug-devel@lists.sourceforge.net";
42 #include "common.h"
43 #include "netutils.h"
44 #include "popen.h"
45 #include "utils.h"
47 #define WARN_DUPLICATES "DUPLICATES FOUND! "
48 #define UNKNOWN_TRIP_TIME -1.0 /* -1 seconds */
50 enum {
51 UNKNOWN_PACKET_LOSS = 200, /* 200% */
52 DEFAULT_MAX_PACKETS = 5 /* default no. of ICMP ECHO packets */
53 };
55 int process_arguments (int, char **);
56 int get_threshold (char *, float *, int *);
57 int validate_arguments (void);
58 int run_ping (const char *cmd, const char *addr);
59 int error_scan (char buf[MAX_INPUT_BUFFER], const char *addr);
60 void print_usage (void);
61 void print_help (void);
63 int display_html = FALSE;
64 int wpl = UNKNOWN_PACKET_LOSS;
65 int cpl = UNKNOWN_PACKET_LOSS;
66 float wrta = UNKNOWN_TRIP_TIME;
67 float crta = UNKNOWN_TRIP_TIME;
68 char **addresses = NULL;
69 int n_addresses = 0;
70 int max_addr = 1;
71 int max_packets = -1;
72 int verbose = 0;
74 float rta = UNKNOWN_TRIP_TIME;
75 int pl = UNKNOWN_PACKET_LOSS;
77 char *warn_text;
81 int
82 main (int argc, char **argv)
83 {
84 char *cmd = NULL;
85 char *rawcmd = NULL;
86 int result = STATE_UNKNOWN;
87 int this_result = STATE_UNKNOWN;
88 int i;
90 setlocale (LC_ALL, "");
91 setlocale (LC_NUMERIC, "C");
92 bindtextdomain (PACKAGE, LOCALEDIR);
93 textdomain (PACKAGE);
95 addresses = malloc (sizeof(char*) * max_addr);
96 addresses[0] = NULL;
98 if (process_arguments (argc, argv) == ERROR)
99 usage4 (_("Could not parse arguments"));
101 /* Set signal handling and alarm */
102 if (signal (SIGALRM, popen_timeout_alarm_handler) == SIG_ERR) {
103 usage4 (_("Cannot catch SIGALRM"));
104 }
106 /* handle timeouts gracefully */
107 alarm (timeout_interval + 1);
109 for (i = 0 ; i < n_addresses ; i++) {
111 #ifdef PING6_COMMAND
112 if (address_family != AF_INET && is_inet6_addr(addresses[i]))
113 rawcmd = strdup(PING6_COMMAND);
114 else
115 rawcmd = strdup(PING_COMMAND);
116 #else
117 rawcmd = strdup(PING_COMMAND);
118 #endif
120 /* does the host address of number of packets argument come first? */
121 #ifdef PING_PACKETS_FIRST
122 # ifdef PING_HAS_TIMEOUT
123 asprintf (&cmd, rawcmd, timeout_interval, max_packets, addresses[i]);
124 # else
125 asprintf (&cmd, rawcmd, max_packets, addresses[i]);
126 # endif
127 #else
128 asprintf (&cmd, rawcmd, addresses[i], max_packets);
129 #endif
131 if (verbose >= 2)
132 printf ("CMD: %s\n", cmd);
134 /* run the command */
135 this_result = run_ping (cmd, addresses[i]);
137 if (pl == UNKNOWN_PACKET_LOSS || rta < 0.0) {
138 printf ("%s\n", cmd);
139 die (STATE_UNKNOWN,
140 _("CRITICAL - Could not interpret output from ping command\n"));
141 }
143 if (pl >= cpl || rta >= crta || rta < 0)
144 this_result = STATE_CRITICAL;
145 else if (pl >= wpl || rta >= wrta)
146 this_result = STATE_WARNING;
147 else if (pl >= 0 && rta >= 0)
148 this_result = max_state (STATE_OK, this_result);
150 if (n_addresses > 1 && this_result != STATE_UNKNOWN)
151 die (STATE_OK, "%s is alive\n", addresses[i]);
153 if (display_html == TRUE)
154 printf ("<A HREF='%s/traceroute.cgi?%s'>", CGIURL, addresses[i]);
155 if (pl == 100)
156 printf (_("PING %s - %sPacket loss = %d%%"), state_text (this_result), warn_text,
157 pl);
158 else
159 printf (_("PING %s - %sPacket loss = %d%%, RTA = %2.2f ms"),
160 state_text (this_result), warn_text, pl, rta);
161 if (display_html == TRUE)
162 printf ("</A>");
163 printf ("\n");
165 if (verbose >= 2)
166 printf ("%f:%d%% %f:%d%%\n", wrta, wpl, crta, cpl);
168 result = max_state (result, this_result);
169 free (rawcmd);
170 free (cmd);
171 }
173 return result;
174 }
178 /* process command-line arguments */
179 int
180 process_arguments (int argc, char **argv)
181 {
182 int c = 1;
183 char *ptr;
185 int option = 0;
186 static struct option longopts[] = {
187 STD_LONG_OPTS,
188 {"packets", required_argument, 0, 'p'},
189 {"nohtml", no_argument, 0, 'n'},
190 {"link", no_argument, 0, 'L'},
191 {"use-ipv4", no_argument, 0, '4'},
192 {"use-ipv6", no_argument, 0, '6'},
193 {0, 0, 0, 0}
194 };
196 if (argc < 2)
197 return ERROR;
199 for (c = 1; c < argc; c++) {
200 if (strcmp ("-to", argv[c]) == 0)
201 strcpy (argv[c], "-t");
202 if (strcmp ("-nohtml", argv[c]) == 0)
203 strcpy (argv[c], "-n");
204 }
206 while (1) {
207 c = getopt_long (argc, argv, "VvhnL46t:c:w:H:p:", longopts, &option);
209 if (c == -1 || c == EOF)
210 break;
212 switch (c) {
213 case '?': /* usage */
214 usage2 (_("Unknown argument"), optarg);
215 case 'h': /* help */
216 print_help ();
217 exit (STATE_OK);
218 break;
219 case 'V': /* version */
220 print_revision (progname, revision);
221 exit (STATE_OK);
222 break;
223 case 't': /* timeout period */
224 timeout_interval = atoi (optarg);
225 break;
226 case 'v': /* verbose mode */
227 verbose++;
228 break;
229 case '4': /* IPv4 only */
230 address_family = AF_INET;
231 break;
232 case '6': /* IPv6 only */
233 #ifdef USE_IPV6
234 address_family = AF_INET6;
235 #else
236 usage (_("IPv6 support not available\n"));
237 #endif
238 break;
239 case 'H': /* hostname */
240 ptr=optarg;
241 while (1) {
242 n_addresses++;
243 if (n_addresses > max_addr) {
244 max_addr *= 2;
245 addresses = realloc (addresses, sizeof(char*) * max_addr);
246 if (addresses == NULL)
247 die (STATE_UNKNOWN, _("Could not realloc() addresses\n"));
248 }
249 addresses[n_addresses-1] = ptr;
250 if ((ptr = index (ptr, ','))) {
251 strcpy (ptr, "");
252 ptr += sizeof(char);
253 } else {
254 break;
255 }
256 }
257 break;
258 case 'p': /* number of packets to send */
259 if (is_intnonneg (optarg))
260 max_packets = atoi (optarg);
261 else
262 usage2 (_("<max_packets> (%s) must be a non-negative number\n"), optarg);
263 break;
264 case 'n': /* no HTML */
265 display_html = FALSE;
266 break;
267 case 'L': /* show HTML */
268 display_html = TRUE;
269 break;
270 case 'c':
271 get_threshold (optarg, &crta, &cpl);
272 break;
273 case 'w':
274 get_threshold (optarg, &wrta, &wpl);
275 break;
276 }
277 }
279 c = optind;
280 if (c == argc)
281 return validate_arguments ();
283 if (addresses[0] == NULL) {
284 if (is_host (argv[c]) == FALSE) {
285 usage2 (_("Invalid hostname/address"), argv[c]);
286 } else {
287 addresses[0] = argv[c++];
288 n_addresses++;
289 if (c == argc)
290 return validate_arguments ();
291 }
292 }
294 if (wpl == UNKNOWN_PACKET_LOSS) {
295 if (is_intpercent (argv[c]) == FALSE) {
296 printf (_("<wpl> (%s) must be an integer percentage\n"), argv[c]);
297 return ERROR;
298 } else {
299 wpl = atoi (argv[c++]);
300 if (c == argc)
301 return validate_arguments ();
302 }
303 }
305 if (cpl == UNKNOWN_PACKET_LOSS) {
306 if (is_intpercent (argv[c]) == FALSE) {
307 printf (_("<cpl> (%s) must be an integer percentage\n"), argv[c]);
308 return ERROR;
309 } else {
310 cpl = atoi (argv[c++]);
311 if (c == argc)
312 return validate_arguments ();
313 }
314 }
316 if (wrta < 0.0) {
317 if (is_negative (argv[c])) {
318 printf (_("<wrta> (%s) must be a non-negative number\n"), argv[c]);
319 return ERROR;
320 } else {
321 wrta = atof (argv[c++]);
322 if (c == argc)
323 return validate_arguments ();
324 }
325 }
327 if (crta < 0.0) {
328 if (is_negative (argv[c])) {
329 printf (_("<crta> (%s) must be a non-negative number\n"), argv[c]);
330 return ERROR;
331 } else {
332 crta = atof (argv[c++]);
333 if (c == argc)
334 return validate_arguments ();
335 }
336 }
338 if (max_packets == -1) {
339 if (is_intnonneg (argv[c])) {
340 max_packets = atoi (argv[c++]);
341 } else {
342 printf (_("<max_packets> (%s) must be a non-negative number\n"), argv[c]);
343 return ERROR;
344 }
345 }
347 return validate_arguments ();
348 }
352 int
353 get_threshold (char *arg, float *trta, int *tpl)
354 {
355 if (is_intnonneg (arg) && sscanf (arg, "%f", trta) == 1)
356 return OK;
357 else if (strpbrk (arg, ",:") && strstr (arg, "%") && sscanf (arg, "%f%*[:,]%d%%", trta, tpl) == 2)
358 return OK;
359 else if (strstr (arg, "%") && sscanf (arg, "%d%%", tpl) == 1)
360 return OK;
362 usage2 (_("%s: Warning threshold must be integer or percentage!\n\n"), arg);
363 return STATE_UNKNOWN;
364 }
368 int
369 validate_arguments ()
370 {
371 float max_seconds;
372 int i;
374 if (wrta < 0.0) {
375 printf (_("<wrta> was not set\n"));
376 return ERROR;
377 }
378 else if (crta < 0.0) {
379 printf (_("<crta> was not set\n"));
380 return ERROR;
381 }
382 else if (wpl == UNKNOWN_PACKET_LOSS) {
383 printf (_("<wpl> was not set\n"));
384 return ERROR;
385 }
386 else if (cpl == UNKNOWN_PACKET_LOSS) {
387 printf (_("<cpl> was not set\n"));
388 return ERROR;
389 }
390 else if (wrta > crta) {
391 printf (_("<wrta> (%f) cannot be larger than <crta> (%f)\n"), wrta, crta);
392 return ERROR;
393 }
394 else if (wpl > cpl) {
395 printf (_("<wpl> (%d) cannot be larger than <cpl> (%d)\n"), wpl, cpl);
396 return ERROR;
397 }
399 if (max_packets == -1)
400 max_packets = DEFAULT_MAX_PACKETS;
402 max_seconds = crta / 1000.0 * max_packets + max_packets;
403 if (max_seconds > timeout_interval)
404 timeout_interval = (int)max_seconds;
406 for (i=0; i<n_addresses; i++) {
407 if (is_host(addresses[i]) == FALSE)
408 usage2 (_("Invalid hostname/address"), addresses[i]);
409 }
411 if (n_addresses == 0) {
412 usage (_("You must specify a server address or host name"));
413 }
415 return OK;
416 }
420 int
421 run_ping (const char *cmd, const char *addr)
422 {
423 char buf[MAX_INPUT_BUFFER];
424 int result = STATE_UNKNOWN;
426 if ((child_process = spopen (cmd)) == NULL)
427 die (STATE_UNKNOWN, _("Could not open pipe: %s\n"), cmd);
429 child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
430 if (child_stderr == NULL)
431 printf (_("Cannot open stderr for %s\n"), cmd);
433 while (fgets (buf, MAX_INPUT_BUFFER - 1, child_process)) {
435 if (verbose >= 3)
436 printf("Output: %s", buf);
438 result = max_state (result, error_scan (buf, addr));
440 /* get the percent loss statistics */
441 if(sscanf(buf,"%*d packets transmitted, %*d packets received, +%*d errors, %d%% packet loss",&pl)==1 ||
442 sscanf(buf,"%*d packets transmitted, %*d packets received, +%*d duplicates, %d%% packet loss", &pl) == 1 ||
443 sscanf(buf,"%*d packets transmitted, %*d received, +%*d duplicates, %d%% packet loss", &pl) == 1 ||
444 sscanf(buf,"%*d packets transmitted, %*d packets received, %d%% packet loss",&pl)==1 ||
445 sscanf(buf,"%*d packets transmitted, %*d packets received, %d%% loss, time",&pl)==1 ||
446 sscanf(buf,"%*d packets transmitted, %*d received, %d%% loss, time", &pl)==1 ||
447 sscanf(buf,"%*d packets transmitted, %*d received, %d%% packet loss, time", &pl)==1 ||
448 sscanf(buf,"%*d packets transmitted, %*d received, +%*d errors, %d%% packet loss", &pl) == 1 ||
449 sscanf(buf,"%*d packets transmitted %*d received, +%*d errors, %d%% packet loss", &pl) == 1
450 )
451 continue;
453 /* get the round trip average */
454 else
455 if(sscanf(buf,"round-trip min/avg/max = %*f/%f/%*f",&rta)==1 ||
456 sscanf(buf,"round-trip min/avg/max/mdev = %*f/%f/%*f/%*f",&rta)==1 ||
457 sscanf(buf,"round-trip min/avg/max/sdev = %*f/%f/%*f/%*f",&rta)==1 ||
458 sscanf(buf,"round-trip min/avg/max/stddev = %*f/%f/%*f/%*f",&rta)==1 ||
459 sscanf(buf,"round-trip min/avg/max/std-dev = %*f/%f/%*f/%*f",&rta)==1 ||
460 sscanf(buf,"round-trip (ms) min/avg/max = %*f/%f/%*f",&rta)==1 ||
461 sscanf(buf,"round-trip (ms) min/avg/max/stddev = %*f/%f/%*f/%*f",&rta)==1 ||
462 sscanf(buf,"rtt min/avg/max/mdev = %*f/%f/%*f/%*f ms",&rta)==1)
463 continue;
464 }
466 /* this is needed because there is no rta if all packets are lost */
467 if (pl == 100)
468 rta = crta;
470 /* check stderr, setting at least WARNING if there is output here */
471 /* Add warning into warn_text */
472 while (fgets (buf, MAX_INPUT_BUFFER - 1, child_stderr)) {
473 if (! strstr(buf,"WARNING - no SO_TIMESTAMP support, falling back to SIOCGSTAMP")) {
474 if (verbose >= 3) {
475 printf("Got stderr: %s", buf);
476 }
477 if ((result=error_scan(buf, addr)) == STATE_OK) {
478 result = STATE_WARNING;
479 if (warn_text == NULL) {
480 warn_text = strdup(_("System call sent warnings to stderr "));
481 } else {
482 asprintf(&warn_text, "%s %s", warn_text, _("System call sent warnings to stderr "));
483 }
484 }
485 }
486 }
488 (void) fclose (child_stderr);
491 /* close the pipe - WARNING if status is set */
492 if (spclose (child_process))
493 result = max_state (result, STATE_WARNING);
495 if (warn_text == NULL)
496 warn_text = strdup("");
498 return result;
499 }
503 int
504 error_scan (char buf[MAX_INPUT_BUFFER], const char *addr)
505 {
506 if (strstr (buf, "Network is unreachable"))
507 die (STATE_CRITICAL, _("CRITICAL - Network unreachable (%s)"), addr);
508 else if (strstr (buf, "Destination Host Unreachable"))
509 die (STATE_CRITICAL, _("CRITICAL - Host Unreachable (%s)"), addr);
510 else if (strstr (buf, "unknown host" ))
511 die (STATE_CRITICAL, _("CRITICAL - Host not found (%s)"), addr);
512 else if (strstr (buf, "Time to live exceeded"))
513 die (STATE_CRITICAL, _("CRITICAL - Time to live exceeded (%s)"), addr);
515 if (strstr (buf, "(DUP!)") || strstr (buf, "DUPLICATES FOUND")) {
516 if (warn_text == NULL)
517 warn_text = strdup (_(WARN_DUPLICATES));
518 else if (! strstr (warn_text, _(WARN_DUPLICATES)) &&
519 asprintf (&warn_text, "%s %s", warn_text, _(WARN_DUPLICATES)) == -1)
520 die (STATE_UNKNOWN, _("Unable to realloc warn_text"));
521 return (STATE_WARNING);
522 }
524 return (STATE_OK);
525 }
529 void
530 print_help (void)
531 {
532 print_revision (progname, revision);
534 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>");
535 printf (COPYRIGHT, copyright, email);
537 printf (_("Use ping to check connection statistics for a remote host."));
539 printf ("\n\n");
541 print_usage ();
543 printf (_(UT_HELP_VRSN));
545 printf (_(UT_IPv46));
547 printf (" %s\n", "-H, --hostname=HOST");
548 printf (" %s\n", _("host to ping"));
549 printf (" %s\n", "-w, --warning=THRESHOLD");
550 printf (" %s\n", _("warning threshold pair"));
551 printf (" %s\n", "-c, --critical=THRESHOLD");
552 printf (" %s\n", _("critical threshold pair"));
553 printf (" %s\n", "-p, --packets=INTEGER");
554 printf (" %s\n", _("number of ICMP ECHO packets to send"));
555 printf (_("(Default: %d)"), DEFAULT_MAX_PACKETS);
556 printf (" %s\n", "-L, --link");
557 printf (" %s\n", _("show HTML in the plugin output (obsoleted by urlize)"));
559 printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
561 printf ("%s\n", _("THRESHOLD is <rta>,<pl>%% where <rta> is the round trip average travel"));
562 printf ("%s\n", _("time (ms) which triggers a WARNING or CRITICAL state, and <pl> is the"));
563 printf ("%s\n", _("percentage of packet loss to trigger an alarm state."));
565 printf ("\n\n");
567 printf ("%s\n", _("This plugin uses the ping command to probe the specified host for packet loss"));
568 printf ("%s\n", _("(percentage) and round trip average (milliseconds). It can produce HTML output"));
569 printf ("%s\n", _("linking to a traceroute CGI contributed by Ian Cass. The CGI can be found in"));
570 printf ("%s\n", _("the contrib area of the downloads section at http://www.nagios.org/"));
572 printf ("\n\n");
574 printf (_(UT_SUPPORT));
575 }
577 void
578 print_usage (void)
579 {
580 printf (_("Usage:"));
581 printf ("%s -H <host_address> -w <wrta>,<wpl>%% -c <crta>,<cpl>%%\n", progname);
582 printf (" [-p packets] [-t timeout] [-L] [-4|-6]\n");
583 }