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 #define 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 *);
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 *server_address = NULL;
71 int max_packets = -1;
72 int verbose = FALSE;
74 float rta = UNKNOWN_TRIP_TIME;
75 int pl = UNKNOWN_PACKET_LOSS;
77 char *warn_text = NULL;
79 int
80 main (int argc, char **argv)
81 {
82 char *command_line = NULL;
83 int result = STATE_UNKNOWN;
85 if (process_arguments (argc, argv) == ERROR)
86 usage ("Could not parse arguments");
87 exit;
89 /* does the host address of number of packets argument come first? */
90 #ifdef PING_PACKETS_FIRST
91 command_line =
92 ssprintf (command_line, PING_COMMAND, max_packets, server_address);
93 #else
94 command_line =
95 ssprintf (command_line, PING_COMMAND, server_address, max_packets);
96 #endif
98 /* Set signal handling and alarm */
99 if (signal (SIGALRM, popen_timeout_alarm_handler) == SIG_ERR) {
100 printf ("Cannot catch SIGALRM");
101 return STATE_UNKNOWN;
102 }
104 /* handle timeouts gracefully */
105 alarm (timeout_interval);
107 if (verbose)
108 printf ("%s ==> ", command_line);
110 /* run the command */
111 run_ping (command_line);
113 if (pl == UNKNOWN_PACKET_LOSS || rta == UNKNOWN_TRIP_TIME) {
114 printf ("%s\n", command_line);
115 terminate (STATE_UNKNOWN,
116 "Error: Could not interpret output from ping command\n");
117 }
119 if (pl >= cpl || rta >= crta || rta < 0)
120 result = STATE_CRITICAL;
121 else if (pl >= wpl || rta >= wrta)
122 result = STATE_WARNING;
123 else if (pl < wpl && rta < wrta && pl >= 0 && rta >= 0)
124 /* cannot use the max function because STATE_UNKNOWN is now 3 gt STATE_OK
125 result = max (result, STATE_OK); */
126 if( !( (result == STATE_WARNING) || (result == STATE_CRITICAL) ) ) {
127 result = STATE_OK;
128 }
130 if (display_html == TRUE)
131 printf ("<A HREF='%s/traceroute.cgi?%s'>", CGIURL, server_address);
132 if (pl == 100)
133 printf ("PING %s - %sPacket loss = %d%%", state_text (result), warn_text,
134 pl);
135 else
136 printf ("PING %s - %sPacket loss = %d%%, RTA = %2.2f ms",
137 state_text (result), warn_text, pl, rta);
138 if (display_html == TRUE)
139 printf ("</A>");
140 printf ("\n");
142 if (verbose)
143 printf ("%f:%d%% %f:%d%%\n", wrta, wpl, crta, cpl);
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;
155 #ifdef HAVE_GETOPT_H
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 {0, 0, 0, 0}
163 };
164 #endif
166 #define OPTCHARS "Vvht:c:w:H:p:nL"
168 if (argc < 2)
169 return ERROR;
171 for (c = 1; c < argc; c++) {
172 if (strcmp ("-to", argv[c]) == 0)
173 strcpy (argv[c], "-t");
174 if (strcmp ("-nohtml", argv[c]) == 0)
175 strcpy (argv[c], "-n");
176 }
178 while (1) {
179 #ifdef HAVE_GETOPT_H
180 c = getopt_long (argc, argv, OPTCHARS, long_options, &option_index);
181 #else
182 c = getopt (argc, argv, OPTCHARS);
183 #endif
184 if (c == -1 || c == EOF)
185 break;
187 switch (c) {
188 case '?': /* usage */
189 usage3 ("Unknown argument", optopt);
190 case 'h': /* help */
191 print_help ();
192 exit (STATE_OK);
193 case 'V': /* version */
194 print_revision (PROGNAME, REVISION);
195 exit (STATE_OK);
196 case 't': /* timeout period */
197 timeout_interval = atoi (optarg);
198 break;
199 case 'v': /* verbose mode */
200 verbose = TRUE;
201 break;
202 case 'H': /* hostname */
203 if (is_host (optarg) == FALSE)
204 usage2 ("Invalid host name/address", optarg);
205 server_address = optarg;
206 break;
207 case 'p': /* number of packets to send */
208 if (is_intnonneg (optarg))
209 max_packets = atoi (optarg);
210 else
211 usage2 ("<max_packets> (%s) must be a non-negative number\n", optarg);
212 break;
213 case 'n': /* no HTML */
214 display_html = FALSE;
215 break;
216 case 'L': /* show HTML */
217 display_html = TRUE;
218 break;
219 case 'c':
220 get_threshold (optarg, &crta, &cpl);
221 break;
222 case 'w':
223 get_threshold (optarg, &wrta, &wpl);
224 break;
225 }
226 }
228 c = optind;
229 if (c == argc)
230 return validate_arguments ();
232 if (server_address == NULL) {
233 if (is_host (argv[c]) == FALSE) {
234 printf ("Invalid host name/address: %s\n\n", argv[c]);
235 return ERROR;
236 } else {
237 server_address = argv[c++];
238 if (c == argc)
239 return validate_arguments ();
240 }
241 }
243 if (wpl == UNKNOWN_PACKET_LOSS) {
244 if (is_intpercent (argv[c]) == FALSE) {
245 printf ("<wpl> (%s) must be an integer percentage\n", argv[c]);
246 return ERROR;
247 } else {
248 wpl = atoi (argv[c++]);
249 if (c == argc)
250 return validate_arguments ();
251 }
252 }
254 if (cpl == UNKNOWN_PACKET_LOSS) {
255 if (is_intpercent (argv[c]) == FALSE) {
256 printf ("<cpl> (%s) must be an integer percentage\n", argv[c]);
257 return ERROR;
258 } else {
259 cpl = atoi (argv[c++]);
260 if (c == argc)
261 return validate_arguments ();
262 }
263 }
265 if (wrta == UNKNOWN_TRIP_TIME) {
266 if (is_negative (argv[c])) {
267 printf ("<wrta> (%s) must be a non-negative number\n", argv[c]);
268 return ERROR;
269 } else {
270 wrta = atof (argv[c++]);
271 if (c == argc)
272 return validate_arguments ();
273 }
274 }
276 if (crta == UNKNOWN_TRIP_TIME) {
277 if (is_negative (argv[c])) {
278 printf ("<crta> (%s) must be a non-negative number\n", argv[c]);
279 return ERROR;
280 } else {
281 crta = atof (argv[c++]);
282 if (c == argc)
283 return validate_arguments ();
284 }
285 }
287 if (max_packets == -1) {
288 if (is_intnonneg (argv[c])) {
289 max_packets = atoi (argv[c++]);
290 } else {
291 printf ("<max_packets> (%s) must be a non-negative number\n", argv[c]);
292 return ERROR;
293 }
294 }
296 return validate_arguments ();
297 }
299 int
300 get_threshold (char *arg, float *trta, int *tpl)
301 {
302 if (is_intnonneg (arg) && sscanf (arg, "%f", trta) == 1)
303 return OK;
304 else if (strpbrk (arg, ",:") && strstr (arg, "%") && sscanf (arg, "%f%*[:,]%d%%", trta, tpl) == 2)
305 return OK;
306 else if (strstr (arg, "%") && sscanf (arg, "%d%%", tpl) == 1)
307 return OK;
308 else
309 usage2 ("%s: Warning threshold must be integer or percentage!\n\n", arg);
310 }
312 int
313 validate_arguments ()
314 {
315 float max_seconds;
317 if (wrta == UNKNOWN_TRIP_TIME) {
318 printf ("<wrta> was not set\n");
319 return ERROR;
320 }
321 else if (crta == UNKNOWN_TRIP_TIME) {
322 printf ("<crta> was not set\n");
323 return ERROR;
324 }
325 else if (wpl == UNKNOWN_PACKET_LOSS) {
326 printf ("<wpl> was not set\n");
327 return ERROR;
328 }
329 else if (cpl == UNKNOWN_PACKET_LOSS) {
330 printf ("<cpl> was not set\n");
331 return ERROR;
332 }
333 else if (wrta > crta) {
334 printf ("<wrta> (%f) cannot be larger than <crta> (%f)\n", wrta, crta);
335 return ERROR;
336 }
337 else if (wpl > cpl) {
338 printf ("<wpl> (%d) cannot be larger than <cpl> (%d)\n", wpl, cpl);
339 return ERROR;
340 }
342 if (max_packets == -1)
343 max_packets = DEFAULT_MAX_PACKETS;
345 max_seconds = crta / 1000.0 * max_packets + max_packets;
346 if (max_seconds > timeout_interval)
347 timeout_interval = (int)max_seconds;
349 return OK;
350 }
351 \f
353 int
354 run_ping (char *command_line)
355 {
356 char input_buffer[MAX_INPUT_BUFFER];
357 int result = STATE_UNKNOWN;
359 warn_text = malloc (1);
360 if (warn_text == NULL)
361 terminate (STATE_UNKNOWN, "unable to malloc warn_text");
362 warn_text[0] = 0;
364 if ((child_process = spopen (command_line)) == NULL) {
365 printf ("Cannot open pipe: ");
366 terminate (STATE_UNKNOWN, command_line);
367 }
368 child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
369 if (child_stderr == NULL)
370 printf ("Cannot open stderr for %s\n", command_line);
372 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
374 if (strstr (input_buffer, "(DUP!)")) {
375 /* cannot use the max function since STATE_UNKNOWN is max
376 result = max (result, STATE_WARNING); */
377 if( !(result == STATE_CRITICAL) ){
378 result = STATE_WARNING;
379 }
381 warn_text = realloc (warn_text, strlen (WARN_DUPLICATES) + 1);
382 if (warn_text == NULL)
383 terminate (STATE_UNKNOWN, "unable to realloc warn_text");
384 strcpy (warn_text, WARN_DUPLICATES);
385 }
387 /* get the percent loss statistics */
388 if (sscanf
389 (input_buffer, "%*d packets transmitted, %*d packets received, +%*d errors, %d%% packet loss",
390 &pl) == 1
391 || sscanf
392 (input_buffer, "%*d packets transmitted, %*d packets received, %d%% packet loss",
393 &pl) == 1
394 || sscanf
395 (input_buffer, "%*d packets transmitted, %*d packets received, %d%% loss, time", &pl) == 1
396 || sscanf
397 (input_buffer, "%*d packets transmitted, %*d received, %d%% loss, time", &pl) == 1
398 /* Suse 8.0 as reported by Richard * Brodie */
399 )
400 continue;
402 /* get the round trip average */
403 else
404 if (sscanf (input_buffer, "round-trip min/avg/max = %*f/%f/%*f", &rta)
405 == 1
406 || sscanf (input_buffer,
407 "round-trip min/avg/max/mdev = %*f/%f/%*f/%*f",
408 &rta) == 1
409 || sscanf (input_buffer,
410 "round-trip min/avg/max/sdev = %*f/%f/%*f/%*f",
411 &rta) == 1
412 || sscanf (input_buffer,
413 "round-trip min/avg/max/stddev = %*f/%f/%*f/%*f",
414 &rta) == 1
415 || sscanf (input_buffer,
416 "round-trip min/avg/max/std-dev = %*f/%f/%*f/%*f",
417 &rta) == 1
418 || sscanf (input_buffer, "round-trip (ms) min/avg/max = %*f/%f/%*f",
419 &rta) == 1
420 || sscanf (input_buffer, "rtt min/avg/max/mdev = %*f/%f/%*f/%*f ms",
421 &rta) == 1
422 )
423 continue;
424 }
426 /* this is needed because there is no rta if all packets are lost */
427 if (pl == 100)
428 rta = crta;
431 /* check stderr */
432 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
433 if (strstr
434 (input_buffer,
435 "Warning: no SO_TIMESTAMP support, falling back to SIOCGSTAMP"))
436 continue;
438 if (strstr (input_buffer, "Network is unreachable"))
439 terminate (STATE_CRITICAL, "PING CRITICAL - Network unreachable (%s)",
440 server_address);
441 else if (strstr (input_buffer, "Destination Host Unreachable"))
442 terminate (STATE_CRITICAL, "PING CRITICAL - Host Unreachable (%s)",
443 server_address);
444 else if (strstr (input_buffer, "unknown host" ) )
445 terminate (STATE_CRITICAL, "PING CRITICAL - Host not found (%s)",
446 server_address);
448 warn_text =
449 realloc (warn_text, strlen (warn_text) + strlen (input_buffer) + 2);
450 if (warn_text == NULL)
451 terminate (STATE_UNKNOWN, "unable to realloc warn_text");
452 if (strlen (warn_text) == 0)
453 strcpy (warn_text, input_buffer);
454 else
455 sprintf (warn_text, "%s %s", warn_text, input_buffer);
457 if (strstr (input_buffer, "DUPLICATES FOUND")) {
458 if( !(result == STATE_CRITICAL) ){
459 result = STATE_WARNING;
460 }
461 }
462 else
463 result = STATE_CRITICAL ;
464 }
465 (void) fclose (child_stderr);
468 /* close the pipe - WARNING if status is set */
469 if (spclose (child_process))
470 result = max_state (result, STATE_WARNING);
472 return result;
473 }
476 void
477 print_usage (void)
478 {
479 printf ("Usage:\n" " %s %s\n"
480 #ifdef HAVE_GETOPT_H
481 " %s (-h | --help) for detailed help\n"
482 " %s (-V | --version) for version information\n",
483 #else
484 " %s -h for detailed help\n"
485 " %s -V for version information\n",
486 #endif
487 PROGNAME, OPTIONS, PROGNAME, PROGNAME);
488 }
490 void
491 print_help (void)
492 {
493 print_revision (PROGNAME, REVISION);
494 printf
495 ("Copyright (c) %s %s <%s>\n\n%s\n",
496 COPYRIGHT, AUTHOR, EMAIL, SUMMARY);
497 print_usage ();
498 printf
499 ("\nOptions:\n" LONGOPTIONS "\n" DESCRIPTION "\n",
500 DEFAULT_MAX_PACKETS, DEFAULT_SOCKET_TIMEOUT);
501 support ();
502 }