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);
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;
152 }
158 \f
159 /* process command-line arguments */
160 int
161 process_arguments (int argc, char **argv)
162 {
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 ();
327 }
329 int
330 get_threshold (char *arg, float *trta, int *tpl)
331 {
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;
341 }
343 int
344 validate_arguments ()
345 {
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;
387 }
393 \f
394 int
395 run_ping (char *cmd, char *server_address)
396 {
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;
476 }
482 \f
483 void
484 print_usage (void)
485 {
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);
490 }
492 void
493 print_help (void)
494 {
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));
535 }