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