1 /*****************************************************************************
3 * Monitoring check_ping plugin
6 * Copyright (c) 2000-2024 Monitoring Plugins Development Team
10 * This file contains the check_ping plugin
12 * Use the ping program to check connection statistics for a remote host.
15 * This program is free software: you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation, either version 3 of the License, or
18 * (at your option) any later version.
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
25 * You should have received a copy of the GNU General Public License
26 * along with this program. If not, see <http://www.gnu.org/licenses/>.
29 *****************************************************************************/
31 const char *progname
= "check_ping";
32 const char *copyright
= "2000-2024";
33 const char *email
= "devel@monitoring-plugins.org";
42 #define WARN_DUPLICATES "DUPLICATES FOUND! "
43 #define UNKNOWN_TRIP_TIME -1.0 /* -1 seconds */
46 UNKNOWN_PACKET_LOSS
= 200, /* 200% */
47 DEFAULT_MAX_PACKETS
= 5 /* default no. of ICMP ECHO packets */
50 static int process_arguments(int /*argc*/, char ** /*argv*/);
51 static int get_threshold(char * /*arg*/, float * /*trta*/, int * /*tpl*/);
52 static int validate_arguments(void);
53 static int run_ping(const char *cmd
, const char *addr
);
54 static int error_scan(char buf
[MAX_INPUT_BUFFER
], const char *addr
);
55 static void print_help(void);
56 void print_usage(void);
58 static bool display_html
= false;
59 static int wpl
= UNKNOWN_PACKET_LOSS
;
60 static int cpl
= UNKNOWN_PACKET_LOSS
;
61 static float wrta
= UNKNOWN_TRIP_TIME
;
62 static float crta
= UNKNOWN_TRIP_TIME
;
63 static char **addresses
= NULL
;
64 static int n_addresses
= 0;
65 static int max_addr
= 1;
66 static int max_packets
= -1;
67 static int verbose
= 0;
69 static float rta
= UNKNOWN_TRIP_TIME
;
70 static int pl
= UNKNOWN_PACKET_LOSS
;
72 static char *warn_text
;
74 int main(int argc
, char **argv
) {
77 int result
= STATE_UNKNOWN
;
78 int this_result
= STATE_UNKNOWN
;
81 setlocale(LC_ALL
, "");
82 setlocale(LC_NUMERIC
, "C");
83 bindtextdomain(PACKAGE
, LOCALEDIR
);
86 addresses
= malloc(sizeof(char *) * max_addr
);
89 /* Parse extra opts if any */
90 argv
= np_extra_opts(&argc
, argv
, progname
);
92 if (process_arguments(argc
, argv
) == ERROR
)
93 usage4(_("Could not parse arguments"));
95 /* Set signal handling and alarm */
96 if (signal(SIGALRM
, popen_timeout_alarm_handler
) == SIG_ERR
) {
97 usage4(_("Cannot catch SIGALRM"));
100 /* If ./configure finds ping has timeout values, set plugin alarm slightly
101 * higher so that we can use response from command line ping */
102 #if defined(PING_PACKETS_FIRST) && defined(PING_HAS_TIMEOUT)
103 alarm(timeout_interval
+ 1);
105 alarm(timeout_interval
);
108 for (i
= 0; i
< n_addresses
; i
++) {
111 if (address_family
!= AF_INET
&& is_inet6_addr(addresses
[i
]))
112 rawcmd
= strdup(PING6_COMMAND
);
114 rawcmd
= strdup(PING_COMMAND
);
116 rawcmd
= strdup(PING_COMMAND
);
119 /* does the host address of number of packets argument come first? */
120 #ifdef PING_PACKETS_FIRST
121 # ifdef PING_HAS_TIMEOUT
122 xasprintf(&cmd
, rawcmd
, timeout_interval
, max_packets
, addresses
[i
]);
124 xasprintf(&cmd
, rawcmd
, max_packets
, addresses
[i
]);
127 xasprintf(&cmd
, rawcmd
, addresses
[i
], max_packets
);
131 printf("CMD: %s\n", cmd
);
133 /* run the command */
134 this_result
= run_ping(cmd
, addresses
[i
]);
136 if (pl
== UNKNOWN_PACKET_LOSS
|| rta
< 0.0) {
138 die(STATE_UNKNOWN
, _("CRITICAL - Could not interpret output from ping command\n"));
141 if (pl
>= cpl
|| rta
>= crta
|| rta
< 0)
142 this_result
= STATE_CRITICAL
;
143 else if (pl
>= wpl
|| rta
>= wrta
)
144 this_result
= STATE_WARNING
;
145 else if (pl
>= 0 && rta
>= 0)
146 this_result
= max_state(STATE_OK
, this_result
);
148 if (n_addresses
> 1 && this_result
!= STATE_UNKNOWN
)
149 die(STATE_OK
, "%s is alive\n", addresses
[i
]);
151 if (display_html
== true)
152 printf("<A HREF='%s/traceroute.cgi?%s'>", CGIURL
, addresses
[i
]);
154 printf(_("PING %s - %sPacket loss = %d%%"), state_text(this_result
), warn_text
, pl
);
156 printf(_("PING %s - %sPacket loss = %d%%, RTA = %2.2f ms"), state_text(this_result
), warn_text
, pl
, rta
);
157 if (display_html
== true)
160 /* Print performance data */
163 fperfdata("rta", (double)rta
, "ms", wrta
> 0 ? true : false, wrta
, crta
> 0 ? true : false, crta
, true, 0, false, 0));
165 printf("| rta=U;%f;%f;;", wrta
, crta
);
167 printf(" %s\n", perfdata("pl", (long)pl
, "%", wpl
> 0 ? true : false, wpl
, cpl
> 0 ? true : false, cpl
, true, 0, false, 0));
170 printf("%f:%d%% %f:%d%%\n", wrta
, wpl
, crta
, cpl
);
172 result
= max_state(result
, this_result
);
180 /* process command-line arguments */
181 int process_arguments(int argc
, char **argv
) {
186 static struct option longopts
[] = {STD_LONG_OPTS
,
187 {"packets", required_argument
, 0, 'p'},
188 {"nohtml", no_argument
, 0, 'n'},
189 {"link", no_argument
, 0, 'L'},
190 {"use-ipv4", no_argument
, 0, '4'},
191 {"use-ipv6", no_argument
, 0, '6'},
197 for (c
= 1; c
< argc
; c
++) {
198 if (strcmp("-to", argv
[c
]) == 0)
199 strcpy(argv
[c
], "-t");
200 if (strcmp("-nohtml", argv
[c
]) == 0)
201 strcpy(argv
[c
], "-n");
205 c
= getopt_long(argc
, argv
, "VvhnL46t:c:w:H:p:", longopts
, &option
);
207 if (c
== -1 || c
== EOF
)
211 case '?': /* usage */
217 case 'V': /* version */
218 print_revision(progname
, NP_VERSION
);
221 case 't': /* timeout period */
222 timeout_interval
= atoi(optarg
);
224 case 'v': /* verbose mode */
227 case '4': /* IPv4 only */
228 address_family
= AF_INET
;
230 case '6': /* IPv6 only */
232 address_family
= AF_INET6
;
234 usage(_("IPv6 support not available\n"));
237 case 'H': /* hostname */
241 if (n_addresses
> max_addr
) {
243 addresses
= realloc(addresses
, sizeof(char *) * max_addr
);
244 if (addresses
== NULL
)
245 die(STATE_UNKNOWN
, _("Could not realloc() addresses\n"));
247 addresses
[n_addresses
- 1] = ptr
;
248 if ((ptr
= index(ptr
, ','))) {
256 case 'p': /* number of packets to send */
257 if (is_intnonneg(optarg
))
258 max_packets
= atoi(optarg
);
260 usage2(_("<max_packets> (%s) must be a non-negative number\n"), optarg
);
262 case 'n': /* no HTML */
263 display_html
= false;
265 case 'L': /* show HTML */
269 get_threshold(optarg
, &crta
, &cpl
);
272 get_threshold(optarg
, &wrta
, &wpl
);
279 return validate_arguments();
281 if (addresses
[0] == NULL
) {
282 if (!is_host(argv
[c
])) {
283 usage2(_("Invalid hostname/address"), argv
[c
]);
285 addresses
[0] = argv
[c
++];
288 return validate_arguments();
292 if (wpl
== UNKNOWN_PACKET_LOSS
) {
293 if (!is_intpercent(argv
[c
])) {
294 printf(_("<wpl> (%s) must be an integer percentage\n"), argv
[c
]);
297 wpl
= atoi(argv
[c
++]);
299 return validate_arguments();
303 if (cpl
== UNKNOWN_PACKET_LOSS
) {
304 if (!is_intpercent(argv
[c
])) {
305 printf(_("<cpl> (%s) must be an integer percentage\n"), argv
[c
]);
308 cpl
= atoi(argv
[c
++]);
310 return validate_arguments();
315 if (is_negative(argv
[c
])) {
316 printf(_("<wrta> (%s) must be a non-negative number\n"), argv
[c
]);
319 wrta
= atof(argv
[c
++]);
321 return validate_arguments();
326 if (is_negative(argv
[c
])) {
327 printf(_("<crta> (%s) must be a non-negative number\n"), argv
[c
]);
330 crta
= atof(argv
[c
++]);
332 return validate_arguments();
336 if (max_packets
== -1) {
337 if (is_intnonneg(argv
[c
])) {
338 max_packets
= atoi(argv
[c
++]);
340 printf(_("<max_packets> (%s) must be a non-negative number\n"), argv
[c
]);
345 return validate_arguments();
348 int get_threshold(char *arg
, float *trta
, int *tpl
) {
349 if (is_intnonneg(arg
) && sscanf(arg
, "%f", trta
) == 1)
351 else if (strpbrk(arg
, ",:") && strstr(arg
, "%") && sscanf(arg
, "%f%*[:,]%d%%", trta
, tpl
) == 2)
353 else if (strstr(arg
, "%") && sscanf(arg
, "%d%%", tpl
) == 1)
356 usage2(_("%s: Warning threshold must be integer or percentage!\n\n"), arg
);
357 return STATE_UNKNOWN
;
360 int validate_arguments() {
365 printf(_("<wrta> was not set\n"));
367 } else if (crta
< 0.0) {
368 printf(_("<crta> was not set\n"));
370 } else if (wpl
== UNKNOWN_PACKET_LOSS
) {
371 printf(_("<wpl> was not set\n"));
373 } else if (cpl
== UNKNOWN_PACKET_LOSS
) {
374 printf(_("<cpl> was not set\n"));
376 } else if (wrta
> crta
) {
377 printf(_("<wrta> (%f) cannot be larger than <crta> (%f)\n"), wrta
, crta
);
379 } else if (wpl
> cpl
) {
380 printf(_("<wpl> (%d) cannot be larger than <cpl> (%d)\n"), wpl
, cpl
);
384 if (max_packets
== -1)
385 max_packets
= DEFAULT_MAX_PACKETS
;
387 max_seconds
= crta
/ 1000.0 * max_packets
+ max_packets
;
388 if (max_seconds
> timeout_interval
)
389 timeout_interval
= (int)max_seconds
;
391 for (i
= 0; i
< n_addresses
; i
++) {
392 if (!is_host(addresses
[i
]))
393 usage2(_("Invalid hostname/address"), addresses
[i
]);
396 if (n_addresses
== 0) {
397 usage(_("You must specify a server address or host name"));
403 int run_ping(const char *cmd
, const char *addr
) {
404 char buf
[MAX_INPUT_BUFFER
];
405 int result
= STATE_UNKNOWN
;
408 if ((child_process
= spopen(cmd
)) == NULL
)
409 die(STATE_UNKNOWN
, _("Could not open pipe: %s\n"), cmd
);
411 child_stderr
= fdopen(child_stderr_array
[fileno(child_process
)], "r");
412 if (child_stderr
== NULL
)
413 printf(_("Cannot open stderr for %s\n"), cmd
);
415 while (fgets(buf
, MAX_INPUT_BUFFER
- 1, child_process
)) {
418 printf("Output: %s", buf
);
420 result
= max_state(result
, error_scan(buf
, addr
));
422 /* get the percent loss statistics */
424 if ((sscanf(buf
, "%*d packets transmitted, %*d packets received, +%*d errors, %d%% packet loss%n", &pl
, &match
) && match
) ||
425 (sscanf(buf
, "%*d packets transmitted, %*d packets received, +%*d duplicates, %d%% packet loss%n", &pl
, &match
) && match
) ||
426 (sscanf(buf
, "%*d packets transmitted, %*d received, +%*d duplicates, %d%% packet loss%n", &pl
, &match
) && match
) ||
427 (sscanf(buf
, "%*d packets transmitted, %*d packets received, %d%% packet loss%n", &pl
, &match
) && match
) ||
428 (sscanf(buf
, "%*d packets transmitted, %*d packets received, %d%% loss, time%n", &pl
, &match
) && match
) ||
429 (sscanf(buf
, "%*d packets transmitted, %*d received, %d%% loss, time%n", &pl
, &match
) && match
) ||
430 (sscanf(buf
, "%*d packets transmitted, %*d received, %d%% packet loss, time%n", &pl
, &match
) && match
) ||
431 (sscanf(buf
, "%*d packets transmitted, %*d received, +%*d errors, %d%% packet loss%n", &pl
, &match
) && match
) ||
432 (sscanf(buf
, "%*d packets transmitted %*d received, +%*d errors, %d%% packet loss%n", &pl
, &match
) && match
) ||
433 (sscanf(buf
, "%*[^(](%d%% %*[^)])%n", &pl
, &match
) && match
))
436 /* get the round trip average */
437 else if ((sscanf(buf
, "round-trip min/avg/max = %*f/%f/%*f%n", &rta
, &match
) && match
) ||
438 (sscanf(buf
, "round-trip min/avg/max/mdev = %*f/%f/%*f/%*f%n", &rta
, &match
) && match
) ||
439 (sscanf(buf
, "round-trip min/avg/max/sdev = %*f/%f/%*f/%*f%n", &rta
, &match
) && match
) ||
440 (sscanf(buf
, "round-trip min/avg/max/stddev = %*f/%f/%*f/%*f%n", &rta
, &match
) && match
) ||
441 (sscanf(buf
, "round-trip min/avg/max/std-dev = %*f/%f/%*f/%*f%n", &rta
, &match
) && match
) ||
442 (sscanf(buf
, "round-trip (ms) min/avg/max = %*f/%f/%*f%n", &rta
, &match
) && match
) ||
443 (sscanf(buf
, "round-trip (ms) min/avg/max/stddev = %*f/%f/%*f/%*f%n", &rta
, &match
) && match
) ||
444 (sscanf(buf
, "rtt min/avg/max/mdev = %*f/%f/%*f/%*f ms%n", &rta
, &match
) && match
) ||
445 (sscanf(buf
, "%*[^=] = %*fms, %*[^=] = %*fms, %*[^=] = %fms%n", &rta
, &match
) && match
))
449 /* this is needed because there is no rta if all packets are lost */
453 /* check stderr, setting at least WARNING if there is output here */
454 /* Add warning into warn_text */
455 while (fgets(buf
, MAX_INPUT_BUFFER
- 1, child_stderr
)) {
456 if (!strstr(buf
, "WARNING - no SO_TIMESTAMP support, falling back to SIOCGSTAMP") && !strstr(buf
, "Warning: time of day goes back")
460 printf("Got stderr: %s", buf
);
462 if ((result
= error_scan(buf
, addr
)) == STATE_OK
) {
463 result
= STATE_WARNING
;
464 if (warn_text
== NULL
) {
465 warn_text
= strdup(_("System call sent warnings to stderr "));
467 xasprintf(&warn_text
, "%s %s", warn_text
, _("System call sent warnings to stderr "));
473 (void)fclose(child_stderr
);
475 spclose(child_process
);
477 if (warn_text
== NULL
)
478 warn_text
= strdup("");
483 int error_scan(char buf
[MAX_INPUT_BUFFER
], const char *addr
) {
484 if (strstr(buf
, "Network is unreachable") || strstr(buf
, "Destination Net Unreachable") || strstr(buf
, "No route"))
485 die(STATE_CRITICAL
, _("CRITICAL - Network Unreachable (%s)\n"), addr
);
486 else if (strstr(buf
, "Destination Host Unreachable") || strstr(buf
, "Address unreachable"))
487 die(STATE_CRITICAL
, _("CRITICAL - Host Unreachable (%s)\n"), addr
);
488 else if (strstr(buf
, "Destination Port Unreachable") || strstr(buf
, "Port unreachable"))
489 die(STATE_CRITICAL
, _("CRITICAL - Bogus ICMP: Port Unreachable (%s)\n"), addr
);
490 else if (strstr(buf
, "Destination Protocol Unreachable"))
491 die(STATE_CRITICAL
, _("CRITICAL - Bogus ICMP: Protocol Unreachable (%s)\n"), addr
);
492 else if (strstr(buf
, "Destination Net Prohibited"))
493 die(STATE_CRITICAL
, _("CRITICAL - Network Prohibited (%s)\n"), addr
);
494 else if (strstr(buf
, "Destination Host Prohibited"))
495 die(STATE_CRITICAL
, _("CRITICAL - Host Prohibited (%s)\n"), addr
);
496 else if (strstr(buf
, "Packet filtered") || strstr(buf
, "Administratively prohibited"))
497 die(STATE_CRITICAL
, _("CRITICAL - Packet Filtered (%s)\n"), addr
);
498 else if (strstr(buf
, "unknown host"))
499 die(STATE_CRITICAL
, _("CRITICAL - Host not found (%s)\n"), addr
);
500 else if (strstr(buf
, "Time to live exceeded") || strstr(buf
, "Time exceeded"))
501 die(STATE_CRITICAL
, _("CRITICAL - Time to live exceeded (%s)\n"), addr
);
502 else if (strstr(buf
, "Destination unreachable: "))
503 die(STATE_CRITICAL
, _("CRITICAL - Destination Unreachable (%s)\n"), addr
);
505 if (strstr(buf
, "(DUP!)") || strstr(buf
, "DUPLICATES FOUND")) {
506 if (warn_text
== NULL
)
507 warn_text
= strdup(_(WARN_DUPLICATES
));
508 else if (!strstr(warn_text
, _(WARN_DUPLICATES
)) && xasprintf(&warn_text
, "%s %s", warn_text
, _(WARN_DUPLICATES
)) == -1)
509 die(STATE_UNKNOWN
, _("Unable to realloc warn_text\n"));
510 return (STATE_WARNING
);
516 void print_help(void) {
517 print_revision(progname
, NP_VERSION
);
519 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
520 printf(COPYRIGHT
, copyright
, email
);
522 printf(_("Use ping to check connection statistics for a remote host."));
528 printf(UT_HELP_VRSN
);
529 printf(UT_EXTRA_OPTS
);
533 printf(" %s\n", "-H, --hostname=HOST");
534 printf(" %s\n", _("host to ping"));
535 printf(" %s\n", "-w, --warning=THRESHOLD");
536 printf(" %s\n", _("warning threshold pair"));
537 printf(" %s\n", "-c, --critical=THRESHOLD");
538 printf(" %s\n", _("critical threshold pair"));
539 printf(" %s\n", "-p, --packets=INTEGER");
540 printf(" %s ", _("number of ICMP ECHO packets to send"));
541 printf(_("(Default: %d)\n"), DEFAULT_MAX_PACKETS
);
542 printf(" %s\n", "-L, --link");
543 printf(" %s\n", _("show HTML in the plugin output (obsoleted by urlize)"));
545 printf(UT_CONN_TIMEOUT
, DEFAULT_SOCKET_TIMEOUT
);
548 printf("%s\n", _("THRESHOLD is <rta>,<pl>% where <rta> is the round trip average travel"));
549 printf("%s\n", _("time (ms) which triggers a WARNING or CRITICAL state, and <pl> is the"));
550 printf("%s\n", _("percentage of packet loss to trigger an alarm state."));
553 printf("%s\n", _("This plugin uses the ping command to probe the specified host for packet loss"));
554 printf("%s\n", _("(percentage) and round trip average (milliseconds). It can produce HTML output"));
555 printf("%s\n", _("linking to a traceroute CGI contributed by Ian Cass. The CGI can be found in"));
556 printf("%s\n", _("the contrib area of the downloads section at http://www.nagios.org/"));
561 void print_usage(void) {
562 printf("%s\n", _("Usage:"));
563 printf("%s -H <host_address> -w <wrta>,<wpl>%% -c <crta>,<cpl>%%\n", progname
);
564 printf(" [-p packets] [-t timeout] [-4|-6]\n");