fd73b8cef8eb8f55d7f2e9506825d6cf7291a16a
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);
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;
151 }
157 \f
158 /* process command-line arguments */
159 int
160 process_arguments (int argc, char **argv)
161 {
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 ();
329 }
331 int
332 get_threshold (char *arg, float *trta, int *tpl)
333 {
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;
343 }
345 int
346 validate_arguments ()
347 {
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;
389 }
395 \f
396 int
397 run_ping (char *cmd, char *server_address)
398 {
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;
480 }
486 \f
487 void
488 print_usage (void)
489 {
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);
494 }
496 void
497 print_help (void)
498 {
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));
539 }