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