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 result = max (result, STATE_OK);
126 if (display_html == TRUE)
127 printf ("<A HREF='%s/traceroute.cgi?%s'>", CGIURL, server_address);
128 if (pl == 100)
129 printf ("PING %s - %sPacket loss = %d%%", state_text (result), warn_text,
130 pl);
131 else
132 printf ("PING %s - %sPacket loss = %d%%, RTA = %2.2f ms",
133 state_text (result), warn_text, pl, rta);
134 if (display_html == TRUE)
135 printf ("</A>");
136 printf ("\n");
138 if (verbose)
139 printf ("%f:%d%% %f:%d%%\n", wrta, wpl, crta, cpl);
141 return result;
142 }
143 \f
145 /* process command-line arguments */
146 int
147 process_arguments (int argc, char **argv)
148 {
149 int c;
151 if (argc < 2)
152 return ERROR;
154 for (c = 1; c < argc; c++) {
155 if (strcmp ("-to", argv[c]) == 0)
156 strcpy (argv[c], "-t");
157 if (strcmp ("-nohtml", argv[c]) == 0)
158 strcpy (argv[c], "-n");
159 }
161 c = 0;
162 while ((c += call_getopt (argc - c, &argv[c])) < argc) {
164 if (is_option (argv[c]))
165 continue;
167 if (server_address == NULL) {
168 if (is_host (argv[c]) == FALSE) {
169 printf ("Invalid host name/address: %s\n\n", argv[c]);
170 return ERROR;
171 }
172 server_address = argv[c];
173 }
174 else if (wpl == UNKNOWN_PACKET_LOSS) {
175 if (is_intpercent (argv[c]) == FALSE) {
176 printf ("<wpl> (%s) must be an integer percentage\n", argv[c]);
177 return ERROR;
178 }
179 wpl = atoi (argv[c]);
180 }
181 else if (cpl == UNKNOWN_PACKET_LOSS) {
182 if (is_intpercent (argv[c]) == FALSE) {
183 printf ("<cpl> (%s) must be an integer percentage\n", argv[c]);
184 return ERROR;
185 }
186 cpl = atoi (argv[c]);
187 }
188 else if (wrta == UNKNOWN_TRIP_TIME) {
189 if (is_negative (argv[c])) {
190 printf ("<wrta> (%s) must be a non-negative number\n", argv[c]);
191 return ERROR;
192 }
193 wrta = atof (argv[c]);
194 }
195 else if (crta == UNKNOWN_TRIP_TIME) {
196 if (is_negative (argv[c])) {
197 printf ("<crta> (%s) must be a non-negative number\n", argv[c]);
198 return ERROR;
199 }
200 crta = atof (argv[c]);
201 }
202 else if (max_packets == -1) {
203 if (is_intnonneg (argv[c])) {
204 max_packets = atoi (argv[c]);
205 }
206 else {
207 printf ("<max_packets> (%s) must be a non-negative number\n",
208 argv[c]);
209 return ERROR;
210 }
211 }
213 }
215 return validate_arguments ();
216 }
218 int
219 call_getopt (int argc, char **argv)
220 {
221 int c, i = 0;
223 #ifdef HAVE_GETOPT_H
224 int option_index = 0;
225 static struct option long_options[] = {
226 {"help", no_argument, 0, 'h'},
227 {"version", no_argument, 0, 'V'},
228 {"verbose", no_argument, 0, 'v'},
229 {"nohtml", no_argument, 0, 'n'},
230 {"link", no_argument, 0, 'L'},
231 {"timeout", required_argument, 0, 't'},
232 {"critical", required_argument, 0, 'c'},
233 {"warning", required_argument, 0, 'w'},
234 {"hostname", required_argument, 0, 'H'},
235 {"packets", required_argument, 0, 'p'},
236 {0, 0, 0, 0}
237 };
238 #endif
240 while (1) {
241 #ifdef HAVE_GETOPT_H
242 c =
243 getopt_long (argc, argv, "+hVvt:c:w:H:p:nL", long_options,
244 &option_index);
245 #else
246 c = getopt (argc, argv, "+hVvt:c:w:H:p:nL");
247 #endif
249 i++;
251 if (c == -1 || c == EOF || c == 1)
252 break;
254 switch (c) {
255 case 't':
256 case 'c':
257 case 'w':
258 case 'H':
259 case 'p':
260 i++;
261 }
263 switch (c) {
264 case '?': /* print short usage statement if args not parsable */
265 usage2 ("Unknown argument", optarg);
266 case 'h': /* help */
267 print_help ();
268 exit (STATE_OK);
269 case 'V': /* version */
270 print_revision (PROGNAME, REVISION);
271 exit (STATE_OK);
272 case 't': /* timeout period */
273 timeout_interval = atoi (optarg);
274 break;
275 case 'v': /* verbose mode */
276 verbose = TRUE;
277 break;
278 case 'H': /* hostname */
279 if (is_host (optarg) == FALSE)
280 usage2 ("Invalid host name/address", optarg);
281 server_address = optarg;
282 break;
283 case 'p': /* number of packets to send */
284 if (is_intnonneg (optarg))
285 max_packets = atoi (optarg);
286 else
287 usage2 ("<max_packets> (%s) must be a non-negative number\n", optarg);
288 break;
289 case 'n': /* no HTML */
290 display_html = FALSE;
291 break;
292 case 'L': /* show HTML */
293 display_html = TRUE;
294 break;
295 case 'c':
296 get_threshold (optarg, &crta, &cpl);
297 break;
298 case 'w':
299 get_threshold (optarg, &wrta, &wpl);
300 break;
301 }
302 }
304 return i;
305 }
307 int
308 get_threshold (char *arg, float *trta, int *tpl)
309 {
310 if (is_intnonneg (arg) && sscanf (arg, "%f", trta) == 1)
311 return OK;
312 else if (strpbrk (arg, ",:") && strstr (arg, "%") && sscanf (arg, "%f%*[:,]%d%%", trta, tpl) == 2)
313 return OK;
314 else if (strstr (arg, "%") && sscanf (arg, "%d%%", tpl) == 1)
315 return OK;
316 else
317 usage2 ("%s: Warning threshold must be integer or percentage!\n\n", arg);
319 }
321 int
322 validate_arguments ()
323 {
324 float max_seconds;
326 if (wrta == UNKNOWN_TRIP_TIME) {
327 printf ("<wrta> was not set\n");
328 return ERROR;
329 }
330 else if (crta == UNKNOWN_TRIP_TIME) {
331 printf ("<crta> was not set\n");
332 return ERROR;
333 }
334 else if (wpl == UNKNOWN_PACKET_LOSS) {
335 printf ("<wpl> was not set\n");
336 return ERROR;
337 }
338 else if (cpl == UNKNOWN_PACKET_LOSS) {
339 printf ("<cpl> was not set\n");
340 return ERROR;
341 }
342 else if (wrta > crta) {
343 printf ("<wrta> (%f) cannot be larger than <crta> (%f)\n", wrta, crta);
344 return ERROR;
345 }
346 else if (wpl > cpl) {
347 printf ("<wpl> (%d) cannot be larger than <cpl> (%d)\n", wpl, cpl);
348 return ERROR;
349 }
351 if (max_packets == -1)
352 max_packets = DEFAULT_MAX_PACKETS;
354 max_seconds = crta / 1000.0 * max_packets + max_packets;
355 if (max_seconds > timeout_interval)
356 timeout_interval = (int)max_seconds;
358 return OK;
359 }
360 \f
362 int
363 run_ping (char *command_line)
364 {
365 char input_buffer[MAX_INPUT_BUFFER];
366 int result = STATE_UNKNOWN;
368 warn_text = malloc (1);
369 if (warn_text == NULL)
370 terminate (STATE_UNKNOWN, "unable to malloc warn_text");
371 warn_text[0] = 0;
373 if ((child_process = spopen (command_line)) == NULL) {
374 printf ("Cannot open pipe: ");
375 terminate (STATE_UNKNOWN, command_line);
376 }
377 child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
378 if (child_stderr == NULL)
379 printf ("Cannot open stderr for %s\n", command_line);
381 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
383 if (strstr (input_buffer, "(DUP!)")) {
384 result = max (result, STATE_WARNING);
385 warn_text = realloc (warn_text, strlen (WARN_DUPLICATES) + 1);
386 if (warn_text == NULL)
387 terminate (STATE_UNKNOWN, "unable to realloc warn_text");
388 strcpy (warn_text, WARN_DUPLICATES);
389 }
391 /* get the percent loss statistics */
392 if (sscanf
393 (input_buffer, "%*d packets transmitted, %*d packets received, +%*d errors, %d%% packet loss",
394 &pl) == 1
395 || sscanf
396 (input_buffer, "%*d packets transmitted, %*d packets received, %d%% packet loss",
397 &pl) == 1
398 || sscanf
399 (input_buffer, "%*d packets transmitted, %*d packets received, %d%% loss, time", &pl) == 1
400 || sscanf
401 (input_buffer, "%*d packets transmitted, %*d received, %d%% loss, time", &pl) == 1
402 /* Suse 8.0 as reported by Richard * Brodie */
403 )
404 continue;
406 /* get the round trip average */
407 else
408 if (sscanf (input_buffer, "round-trip min/avg/max = %*f/%f/%*f", &rta)
409 == 1
410 || sscanf (input_buffer,
411 "round-trip min/avg/max/mdev = %*f/%f/%*f/%*f",
412 &rta) == 1
413 || sscanf (input_buffer,
414 "round-trip min/avg/max/sdev = %*f/%f/%*f/%*f",
415 &rta) == 1
416 || sscanf (input_buffer,
417 "round-trip min/avg/max/stddev = %*f/%f/%*f/%*f",
418 &rta) == 1
419 || sscanf (input_buffer,
420 "round-trip min/avg/max/std-dev = %*f/%f/%*f/%*f",
421 &rta) == 1
422 || sscanf (input_buffer, "round-trip (ms) min/avg/max = %*f/%f/%*f",
423 &rta) == 1
424 || sscanf (input_buffer, "rtt min/avg/max/mdev = %*f/%f/%*f/%*f ms",
425 &rta) == 1
426 )
427 continue;
428 }
430 /* this is needed because there is no rta if all packets are lost */
431 if (pl == 100)
432 rta = crta;
435 /* check stderr */
436 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
437 if (strstr
438 (input_buffer,
439 "Warning: no SO_TIMESTAMP support, falling back to SIOCGSTAMP"))
440 continue;
442 if (strstr (input_buffer, "Network is unreachable"))
443 terminate (STATE_CRITICAL, "PING CRITICAL - Network unreachable (%s)",
444 server_address);
445 else if (strstr (input_buffer, "Destination Host Unreachable"))
446 terminate (STATE_CRITICAL, "PING CRITICAL - Host Unreachable (%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 result = max (result, STATE_WARNING);
460 else
461 result = max (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 (result, STATE_WARNING);
470 return result;
471 }
472 \f
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 }