420f148c140bdcf77883e6d083a7b23d5411a6ca
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");
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;
155 if (argc < 2)
156 return ERROR;
158 for (c = 1; c < argc; c++) {
159 if (strcmp ("-to", argv[c]) == 0)
160 strcpy (argv[c], "-t");
161 if (strcmp ("-nohtml", argv[c]) == 0)
162 strcpy (argv[c], "-n");
163 }
165 c = 0;
166 while ((c += call_getopt (argc - c, &argv[c])) < argc) {
168 if (is_option (argv[c]))
169 continue;
171 if (server_address == NULL) {
172 if (is_host (argv[c]) == FALSE) {
173 printf ("Invalid host name/address: %s\n\n", argv[c]);
174 return ERROR;
175 }
176 server_address = argv[c];
177 }
178 else if (wpl == UNKNOWN_PACKET_LOSS) {
179 if (is_intpercent (argv[c]) == FALSE) {
180 printf ("<wpl> (%s) must be an integer percentage\n", argv[c]);
181 return ERROR;
182 }
183 wpl = atoi (argv[c]);
184 }
185 else if (cpl == UNKNOWN_PACKET_LOSS) {
186 if (is_intpercent (argv[c]) == FALSE) {
187 printf ("<cpl> (%s) must be an integer percentage\n", argv[c]);
188 return ERROR;
189 }
190 cpl = atoi (argv[c]);
191 }
192 else if (wrta == UNKNOWN_TRIP_TIME) {
193 if (is_negative (argv[c])) {
194 printf ("<wrta> (%s) must be a non-negative number\n", argv[c]);
195 return ERROR;
196 }
197 wrta = atof (argv[c]);
198 }
199 else if (crta == UNKNOWN_TRIP_TIME) {
200 if (is_negative (argv[c])) {
201 printf ("<crta> (%s) must be a non-negative number\n", argv[c]);
202 return ERROR;
203 }
204 crta = atof (argv[c]);
205 }
206 else if (max_packets == -1) {
207 if (is_intnonneg (argv[c])) {
208 max_packets = atoi (argv[c]);
209 }
210 else {
211 printf ("<max_packets> (%s) must be a non-negative number\n",
212 argv[c]);
213 return ERROR;
214 }
215 }
217 }
219 return validate_arguments ();
220 }
222 int
223 call_getopt (int argc, char **argv)
224 {
225 int c, i = 0;
227 #ifdef HAVE_GETOPT_H
228 int option_index = 0;
229 static struct option long_options[] = {
230 {"help", no_argument, 0, 'h'},
231 {"version", no_argument, 0, 'V'},
232 {"verbose", no_argument, 0, 'v'},
233 {"nohtml", no_argument, 0, 'n'},
234 {"link", no_argument, 0, 'L'},
235 {"timeout", required_argument, 0, 't'},
236 {"critical", required_argument, 0, 'c'},
237 {"warning", required_argument, 0, 'w'},
238 {"hostname", required_argument, 0, 'H'},
239 {"packets", required_argument, 0, 'p'},
240 {0, 0, 0, 0}
241 };
242 #endif
244 while (1) {
245 #ifdef HAVE_GETOPT_H
246 c =
247 getopt_long (argc, argv, "+hVvt:c:w:H:p:nL", long_options,
248 &option_index);
249 #else
250 c = getopt (argc, argv, "+hVvt:c:w:H:p:nL");
251 #endif
253 i++;
255 if (c == -1 || c == EOF || c == 1)
256 break;
258 switch (c) {
259 case 't':
260 case 'c':
261 case 'w':
262 case 'H':
263 case 'p':
264 i++;
265 }
267 switch (c) {
268 case '?': /* print short usage statement if args not parsable */
269 usage2 ("Unknown argument", optarg);
270 case 'h': /* help */
271 print_help ();
272 exit (STATE_OK);
273 case 'V': /* version */
274 print_revision (PROGNAME, REVISION);
275 exit (STATE_OK);
276 case 't': /* timeout period */
277 timeout_interval = atoi (optarg);
278 break;
279 case 'v': /* verbose mode */
280 verbose = TRUE;
281 break;
282 case 'H': /* hostname */
283 if (is_host (optarg) == FALSE)
284 usage2 ("Invalid host name/address", optarg);
285 server_address = optarg;
286 break;
287 case 'p': /* number of packets to send */
288 if (is_intnonneg (optarg))
289 max_packets = atoi (optarg);
290 else
291 usage2 ("<max_packets> (%s) must be a non-negative number\n", optarg);
292 break;
293 case 'n': /* no HTML */
294 display_html = FALSE;
295 break;
296 case 'L': /* show HTML */
297 display_html = TRUE;
298 break;
299 case 'c':
300 get_threshold (optarg, &crta, &cpl);
301 break;
302 case 'w':
303 get_threshold (optarg, &wrta, &wpl);
304 break;
305 }
306 }
308 return i;
309 }
311 int
312 get_threshold (char *arg, float *trta, int *tpl)
313 {
314 if (is_intnonneg (arg) && sscanf (arg, "%f", trta) == 1)
315 return OK;
316 else if (strpbrk (arg, ",:") && strstr (arg, "%") && sscanf (arg, "%f%*[:,]%d%%", trta, tpl) == 2)
317 return OK;
318 else if (strstr (arg, "%") && sscanf (arg, "%d%%", tpl) == 1)
319 return OK;
320 else
321 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)
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 /* cannot use the max function since STATE_UNKNOWN is max
472 result = max (result, STATE_WARNING); */
473 if( !(result == STATE_CRITICAL) ){
474 result = STATE_WARNING;
475 }
476 else
477 /* cannot use the max function since STATE_UNKNOWN is max
478 result = max (result, STATE_CRITICAL); */
479 result = STATE_CRITICAL ;
480 }
481 (void) fclose (child_stderr);
484 /* close the pipe - WARNING if status is set */
485 if (spclose (child_process))
486 result = max (result, STATE_WARNING);
488 return result;
489 }
492 void
493 print_usage (void)
494 {
495 printf ("Usage:\n" " %s %s\n"
496 #ifdef HAVE_GETOPT_H
497 " %s (-h | --help) for detailed help\n"
498 " %s (-V | --version) for version information\n",
499 #else
500 " %s -h for detailed help\n"
501 " %s -V for version information\n",
502 #endif
503 PROGNAME, OPTIONS, PROGNAME, PROGNAME);
504 }
506 void
507 print_help (void)
508 {
509 print_revision (PROGNAME, REVISION);
510 printf
511 ("Copyright (c) %s %s <%s>\n\n%s\n",
512 COPYRIGHT, AUTHOR, EMAIL, SUMMARY);
513 print_usage ();
514 printf
515 ("\nOptions:\n" LONGOPTIONS "\n" DESCRIPTION "\n",
516 DEFAULT_MAX_PACKETS, DEFAULT_SOCKET_TIMEOUT);
517 support ();
518 }