1 /*****************************************************************************
2 *
3 * CHECK_PING.C
4 *
5 * Program: Ping plugin for Nagios
6 * License: GPL
7 * Copyright (c) 1999 Ethan Galstad (nagios@nagios.org)
8 *
9 * $Id$
10 *
11 *****************************************************************************/
13 const char *progname = "check_ping";
14 #define REVISION "$Revision$"
15 #define COPYRIGHT "1999-2001"
16 #define AUTHOR "Ethan Galstad/Karl DeBisschop"
17 #define EMAIL "kdebisschop@users.sourceforge.net"
18 #define SUMMARY "Use ping to check connection statistics for a remote host.\n"
20 #define OPTIONS "\
21 -H <host_address> -w <wrta>,<wpl>%% -c <crta>,<cpl>%%\n\
22 [-p packets] [-t timeout] [-L]\n"
24 #define LONGOPTIONS "\
25 -H, --hostname=HOST\n\
26 host to ping\n\
27 -w, --warning=THRESHOLD\n\
28 warning threshold pair\n\
29 -c, --critical=THRESHOLD\n\
30 critical threshold pair\n\
31 -p, --packets=INTEGER\n\
32 number of ICMP ECHO packets to send (Default: %d)\n\
33 -t, --timeout=INTEGER\n\
34 optional specified timeout in second (Default: %d)\n\
35 -L, --link\n\
36 show HTML in the plugin output (obsoleted by urlize)\n\
37 THRESHOLD is <rta>,<pl>%% where <rta> is the round trip average travel\n\
38 time (ms) which triggers a WARNING or CRITICAL state, and <pl> is the\n\
39 percentage of packet loss to trigger an alarm state.\n"
41 #define DESCRIPTION "\
42 This plugin uses the ping command to probe the specified host for packet loss\n\
43 (percentage) and round trip average (milliseconds). It can produce HTML output\n\
44 linking to a traceroute CGI contributed by Ian Cass. The CGI can be found in\n\
45 the contrib area of the downloads section at http://www.nagios.org\n\n"
47 #include "config.h"
48 #include "common.h"
49 #include "popen.h"
50 #include "utils.h"
52 #define UNKNOWN_PACKET_LOSS 200 /* 200% */
53 #define UNKNOWN_TRIP_TIME -1.0 /* -1 seconds */
54 #define DEFAULT_MAX_PACKETS 5 /* default no. of ICMP ECHO packets */
56 #define WARN_DUPLICATES "DUPLICATES FOUND! "
58 int process_arguments (int, char **);
59 int get_threshold (char *, float *, int *);
60 int validate_arguments (void);
61 int run_ping (char *, char *);
62 void print_usage (void);
63 void print_help (void);
65 int display_html = FALSE;
66 int wpl = UNKNOWN_PACKET_LOSS;
67 int cpl = UNKNOWN_PACKET_LOSS;
68 float wrta = UNKNOWN_TRIP_TIME;
69 float crta = UNKNOWN_TRIP_TIME;
70 char **addresses = NULL;
71 int n_addresses;
72 int max_addr = 1;
73 int max_packets = -1;
74 int verbose = FALSE;
76 float rta = UNKNOWN_TRIP_TIME;
77 int pl = UNKNOWN_PACKET_LOSS;
79 char *warn_text = NULL;
81 int
82 main (int argc, char **argv)
83 {
84 char *command_line = NULL;
85 int result = STATE_UNKNOWN;
86 int i;
88 addresses = malloc (max_addr);
90 if (process_arguments (argc, argv) == ERROR)
91 usage ("Could not parse arguments");
92 exit;
94 /* Set signal handling and alarm */
95 if (signal (SIGALRM, popen_timeout_alarm_handler) == SIG_ERR) {
96 printf ("Cannot catch SIGALRM");
97 return STATE_UNKNOWN;
98 }
100 /* handle timeouts gracefully */
101 alarm (timeout_interval);
103 for (i = 0 ; i < n_addresses ; i++) {
105 /* does the host address of number of packets argument come first? */
106 #ifdef PING_PACKETS_FIRST
107 asprintf (&command_line, PING_COMMAND, max_packets, addresses[i]);
108 #else
109 asprintf (&command_line, PING_COMMAND, addresses[i], max_packets);
110 #endif
112 if (verbose)
113 printf ("%s ==> ", command_line);
115 /* run the command */
116 run_ping (command_line, addresses[i]);
118 if (pl == UNKNOWN_PACKET_LOSS || rta == UNKNOWN_TRIP_TIME) {
119 printf ("%s\n", command_line);
120 terminate (STATE_UNKNOWN,
121 "Error: Could not interpret output from ping command\n");
122 }
124 if (pl >= cpl || rta >= crta || rta < 0)
125 result = STATE_CRITICAL;
126 else if (pl >= wpl || rta >= wrta)
127 result = STATE_WARNING;
128 else if (pl < wpl && rta < wrta && pl >= 0 && rta >= 0)
129 /* cannot use the max function because STATE_UNKNOWN is now 3 gt STATE_OK
130 result = max (result, STATE_OK); */
131 if( !( (result == STATE_WARNING) || (result == STATE_CRITICAL) ) ) {
132 result = STATE_OK;
133 }
135 if (display_html == TRUE)
136 printf ("<A HREF='%s/traceroute.cgi?%s'>", CGIURL, addresses[i]);
137 if (pl == 100)
138 printf ("PING %s - %sPacket loss = %d%%", state_text (result), warn_text,
139 pl);
140 else
141 printf ("PING %s - %sPacket loss = %d%%, RTA = %2.2f ms",
142 state_text (result), warn_text, pl, rta);
143 if (display_html == TRUE)
144 printf ("</A>");
145 printf ("\n");
147 if (verbose)
148 printf ("%f:%d%% %f:%d%%\n", wrta, wpl, crta, cpl);
149 }
151 return result;
152 }
153 \f
155 /* process command-line arguments */
156 int
157 process_arguments (int argc, char **argv)
158 {
159 int c = 1;
161 #ifdef HAVE_GETOPT_H
162 int option_index = 0;
163 static struct option long_options[] = {
164 STD_LONG_OPTS,
165 {"packets", required_argument, 0, 'p'},
166 {"nohtml", no_argument, 0, 'n'},
167 {"link", no_argument, 0, 'L'},
168 {0, 0, 0, 0}
169 };
170 #endif
172 #define OPTCHARS "Vvht:c:w:H:p:nL"
174 if (argc < 2)
175 return ERROR;
177 for (c = 1; c < argc; c++) {
178 if (strcmp ("-to", argv[c]) == 0)
179 strcpy (argv[c], "-t");
180 if (strcmp ("-nohtml", argv[c]) == 0)
181 strcpy (argv[c], "-n");
182 }
184 while (1) {
185 #ifdef HAVE_GETOPT_H
186 c = getopt_long (argc, argv, OPTCHARS, long_options, &option_index);
187 #else
188 c = getopt (argc, argv, OPTCHARS);
189 #endif
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 'H': /* hostname */
209 if (is_host (optarg) == FALSE)
210 usage2 ("Invalid host name/address", optarg);
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] = optarg;
219 break;
220 case 'p': /* number of packets to send */
221 if (is_intnonneg (optarg))
222 max_packets = atoi (optarg);
223 else
224 usage2 ("<max_packets> (%s) must be a non-negative number\n", optarg);
225 break;
226 case 'n': /* no HTML */
227 display_html = FALSE;
228 break;
229 case 'L': /* show HTML */
230 display_html = TRUE;
231 break;
232 case 'c':
233 get_threshold (optarg, &crta, &cpl);
234 break;
235 case 'w':
236 get_threshold (optarg, &wrta, &wpl);
237 break;
238 }
239 }
241 c = optind;
242 if (c == argc)
243 return validate_arguments ();
245 if (addresses[0] == NULL) {
246 if (is_host (argv[c]) == FALSE) {
247 printf ("Invalid host name/address: %s\n\n", argv[c]);
248 return ERROR;
249 } else {
250 addresses[0] = argv[c++];
251 if (c == argc)
252 return validate_arguments ();
253 }
254 }
256 if (wpl == UNKNOWN_PACKET_LOSS) {
257 if (is_intpercent (argv[c]) == FALSE) {
258 printf ("<wpl> (%s) must be an integer percentage\n", argv[c]);
259 return ERROR;
260 } else {
261 wpl = atoi (argv[c++]);
262 if (c == argc)
263 return validate_arguments ();
264 }
265 }
267 if (cpl == UNKNOWN_PACKET_LOSS) {
268 if (is_intpercent (argv[c]) == FALSE) {
269 printf ("<cpl> (%s) must be an integer percentage\n", argv[c]);
270 return ERROR;
271 } else {
272 cpl = atoi (argv[c++]);
273 if (c == argc)
274 return validate_arguments ();
275 }
276 }
278 if (wrta == UNKNOWN_TRIP_TIME) {
279 if (is_negative (argv[c])) {
280 printf ("<wrta> (%s) must be a non-negative number\n", argv[c]);
281 return ERROR;
282 } else {
283 wrta = atof (argv[c++]);
284 if (c == argc)
285 return validate_arguments ();
286 }
287 }
289 if (crta == UNKNOWN_TRIP_TIME) {
290 if (is_negative (argv[c])) {
291 printf ("<crta> (%s) must be a non-negative number\n", argv[c]);
292 return ERROR;
293 } else {
294 crta = atof (argv[c++]);
295 if (c == argc)
296 return validate_arguments ();
297 }
298 }
300 if (max_packets == -1) {
301 if (is_intnonneg (argv[c])) {
302 max_packets = atoi (argv[c++]);
303 } else {
304 printf ("<max_packets> (%s) must be a non-negative number\n", argv[c]);
305 return ERROR;
306 }
307 }
309 return validate_arguments ();
310 }
312 int
313 get_threshold (char *arg, float *trta, int *tpl)
314 {
315 if (is_intnonneg (arg) && sscanf (arg, "%f", trta) == 1)
316 return OK;
317 else if (strpbrk (arg, ",:") && strstr (arg, "%") && sscanf (arg, "%f%*[:,]%d%%", trta, tpl) == 2)
318 return OK;
319 else if (strstr (arg, "%") && sscanf (arg, "%d%%", tpl) == 1)
320 return OK;
321 else
322 usage2 ("%s: Warning threshold must be integer or percentage!\n\n", arg);
323 }
325 int
326 validate_arguments ()
327 {
328 float max_seconds;
330 if (wrta == UNKNOWN_TRIP_TIME) {
331 printf ("<wrta> was not set\n");
332 return ERROR;
333 }
334 else if (crta == UNKNOWN_TRIP_TIME) {
335 printf ("<crta> was not set\n");
336 return ERROR;
337 }
338 else if (wpl == UNKNOWN_PACKET_LOSS) {
339 printf ("<wpl> was not set\n");
340 return ERROR;
341 }
342 else if (cpl == UNKNOWN_PACKET_LOSS) {
343 printf ("<cpl> was not set\n");
344 return ERROR;
345 }
346 else if (wrta > crta) {
347 printf ("<wrta> (%f) cannot be larger than <crta> (%f)\n", wrta, crta);
348 return ERROR;
349 }
350 else if (wpl > cpl) {
351 printf ("<wpl> (%d) cannot be larger than <cpl> (%d)\n", wpl, cpl);
352 return ERROR;
353 }
355 if (max_packets == -1)
356 max_packets = DEFAULT_MAX_PACKETS;
358 max_seconds = crta / 1000.0 * max_packets + max_packets;
359 if (max_seconds > timeout_interval)
360 timeout_interval = (int)max_seconds;
362 return OK;
363 }
364 \f
366 int
367 run_ping (char *command_line, char *server_address)
368 {
369 char input_buffer[MAX_INPUT_BUFFER];
370 int result = STATE_UNKNOWN;
372 warn_text = malloc (1);
373 if (warn_text == NULL)
374 terminate (STATE_UNKNOWN, "unable to malloc warn_text");
375 warn_text[0] = 0;
377 if ((child_process = spopen (command_line)) == NULL) {
378 printf ("Cannot open pipe: ");
379 terminate (STATE_UNKNOWN, command_line);
380 }
381 child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
382 if (child_stderr == NULL)
383 printf ("Cannot open stderr for %s\n", command_line);
385 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
387 if (strstr (input_buffer, "(DUP!)")) {
388 /* cannot use the max function since STATE_UNKNOWN is max
389 result = max (result, STATE_WARNING); */
390 if( !(result == STATE_CRITICAL) ){
391 result = STATE_WARNING;
392 }
394 warn_text = realloc (warn_text, strlen (WARN_DUPLICATES) + 1);
395 if (warn_text == NULL)
396 terminate (STATE_UNKNOWN, "unable to realloc warn_text");
397 strcpy (warn_text, WARN_DUPLICATES);
398 }
400 /* get the percent loss statistics */
401 if (sscanf
402 (input_buffer, "%*d packets transmitted, %*d packets received, +%*d errors, %d%% packet loss",
403 &pl) == 1
404 || sscanf
405 (input_buffer, "%*d packets transmitted, %*d packets received, %d%% packet loss",
406 &pl) == 1
407 || sscanf
408 (input_buffer, "%*d packets transmitted, %*d packets received, %d%% loss, time", &pl) == 1
409 || sscanf
410 (input_buffer, "%*d packets transmitted, %*d received, %d%% loss, time", &pl) == 1
411 /* Suse 8.0 as reported by Richard * Brodie */
412 )
413 continue;
415 /* get the round trip average */
416 else
417 if (sscanf (input_buffer, "round-trip min/avg/max = %*f/%f/%*f", &rta)
418 == 1
419 || sscanf (input_buffer,
420 "round-trip min/avg/max/mdev = %*f/%f/%*f/%*f",
421 &rta) == 1
422 || sscanf (input_buffer,
423 "round-trip min/avg/max/sdev = %*f/%f/%*f/%*f",
424 &rta) == 1
425 || sscanf (input_buffer,
426 "round-trip min/avg/max/stddev = %*f/%f/%*f/%*f",
427 &rta) == 1
428 || sscanf (input_buffer,
429 "round-trip min/avg/max/std-dev = %*f/%f/%*f/%*f",
430 &rta) == 1
431 || sscanf (input_buffer, "round-trip (ms) min/avg/max = %*f/%f/%*f",
432 &rta) == 1
433 || sscanf (input_buffer, "rtt min/avg/max/mdev = %*f/%f/%*f/%*f ms",
434 &rta) == 1
435 )
436 continue;
437 }
439 /* this is needed because there is no rta if all packets are lost */
440 if (pl == 100)
441 rta = crta;
444 /* check stderr */
445 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
446 if (strstr
447 (input_buffer,
448 "Warning: no SO_TIMESTAMP support, falling back to SIOCGSTAMP"))
449 continue;
451 if (strstr (input_buffer, "Network is unreachable"))
452 terminate (STATE_CRITICAL, "PING CRITICAL - Network unreachable (%s)",
453 server_address);
454 else if (strstr (input_buffer, "Destination Host Unreachable"))
455 terminate (STATE_CRITICAL, "PING CRITICAL - Host Unreachable (%s)",
456 server_address);
457 else if (strstr (input_buffer, "unknown host" ) )
458 terminate (STATE_CRITICAL, "PING CRITICAL - Host not found (%s)",
459 server_address);
461 warn_text =
462 realloc (warn_text, strlen (warn_text) + strlen (input_buffer) + 2);
463 if (warn_text == NULL)
464 terminate (STATE_UNKNOWN, "unable to realloc warn_text");
465 if (strlen (warn_text) == 0)
466 strcpy (warn_text, input_buffer);
467 else
468 sprintf (warn_text, "%s %s", warn_text, input_buffer);
470 if (strstr (input_buffer, "DUPLICATES FOUND")) {
471 if( !(result == STATE_CRITICAL) ){
472 result = STATE_WARNING;
473 }
474 }
475 else
476 result = STATE_CRITICAL ;
477 }
478 (void) fclose (child_stderr);
481 /* close the pipe - WARNING if status is set */
482 if (spclose (child_process))
483 result = max_state (result, STATE_WARNING);
485 return result;
486 }
489 void
490 print_usage (void)
491 {
492 printf ("Usage:\n" " %s %s\n"
493 #ifdef HAVE_GETOPT_H
494 " %s (-h | --help) for detailed help\n"
495 " %s (-V | --version) for version information\n",
496 #else
497 " %s -h for detailed help\n"
498 " %s -V for version information\n",
499 #endif
500 progname, OPTIONS, progname, progname);
501 }
503 void
504 print_help (void)
505 {
506 print_revision (progname, REVISION);
507 printf
508 ("Copyright (c) %s %s <%s>\n\n%s\n",
509 COPYRIGHT, AUTHOR, EMAIL, SUMMARY);
510 print_usage ();
511 printf
512 ("\nOptions:\n" LONGOPTIONS "\n" DESCRIPTION "\n",
513 DEFAULT_MAX_PACKETS, DEFAULT_SOCKET_TIMEOUT);
514 support ();
515 }