summaryrefslogtreecommitdiffstats
path: root/plugins/check_ntp_time.c
diff options
context:
space:
mode:
authorLorenz Kästle <12514511+RincewindsHat@users.noreply.github.com>2025-11-05 13:54:43 +0100
committerLorenz Kästle <12514511+RincewindsHat@users.noreply.github.com>2025-11-05 13:54:43 +0100
commit8e5cb31dc1fac2760233352e7d8e3b09812a80a1 (patch)
treed4d84c58cd7746e1e0298d3eff0aebedefdab3dc /plugins/check_ntp_time.c
parentba6f90373333246bce196dbba494fa4af0bd9253 (diff)
parentad7acf4618f1ac8a49277a482fb4ca391a26f584 (diff)
downloadmonitoring-plugins-8e5cb31dc1fac2760233352e7d8e3b09812a80a1.tar.gz
Merge branch 'master' into modern_output/check_pgsql
Diffstat (limited to 'plugins/check_ntp_time.c')
-rw-r--r--plugins/check_ntp_time.c188
1 files changed, 108 insertions, 80 deletions
diff --git a/plugins/check_ntp_time.c b/plugins/check_ntp_time.c
index ad69b804..602b6010 100644
--- a/plugins/check_ntp_time.c
+++ b/plugins/check_ntp_time.c
@@ -34,12 +34,10 @@
34 * 34 *
35 *****************************************************************************/ 35 *****************************************************************************/
36 36
37const char *progname = "check_ntp_time"; 37#include "output.h"
38const char *copyright = "2006-2024";
39const char *email = "devel@monitoring-plugins.org";
40
41#include "common.h" 38#include "common.h"
42#include "netutils.h" 39#include "netutils.h"
40#include "perfdata.h"
43#include "utils.h" 41#include "utils.h"
44#include "states.h" 42#include "states.h"
45#include "thresholds.h" 43#include "thresholds.h"
@@ -47,6 +45,10 @@ const char *email = "devel@monitoring-plugins.org";
47 45
48static int verbose = 0; 46static int verbose = 0;
49 47
48const char *progname = "check_ntp_time";
49const char *copyright = "2006-2024";
50const char *email = "devel@monitoring-plugins.org";
51
50typedef struct { 52typedef struct {
51 int errorcode; 53 int errorcode;
52 check_ntp_time_config config; 54 check_ntp_time_config config;
@@ -61,9 +63,6 @@ void print_usage(void);
61# define AVG_NUM 4 63# define AVG_NUM 4
62#endif 64#endif
63 65
64/* max size of control message data */
65#define MAX_CM_SIZE 468
66
67/* this structure holds everything in an ntp request/response as per rfc1305 */ 66/* this structure holds everything in an ntp request/response as per rfc1305 */
68typedef struct { 67typedef struct {
69 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */ 68 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */
@@ -169,7 +168,9 @@ typedef struct {
169 : 0) 168 : 0)
170 169
171/* convert a struct timeval to a double */ 170/* convert a struct timeval to a double */
172#define TVasDOUBLE(x) (double)(x.tv_sec + (0.000001 * x.tv_usec)) 171static double TVasDOUBLE(struct timeval time) {
172 return ((double)time.tv_sec + (0.000001 * (double)time.tv_usec));
173}
173 174
174/* convert an ntp 64-bit fp number to a struct timeval */ 175/* convert an ntp 64-bit fp number to a struct timeval */
175#define NTP64toTV(n, t) \ 176#define NTP64toTV(n, t) \
@@ -262,8 +263,8 @@ void setup_request(ntp_message *message) {
262/* select the "best" server from a list of servers, and return its index. 263/* select the "best" server from a list of servers, and return its index.
263 * this is done by filtering servers based on stratum, dispersion, and 264 * this is done by filtering servers based on stratum, dispersion, and
264 * finally round-trip delay. */ 265 * finally round-trip delay. */
265int best_offset_server(const ntp_server_results *slist, int nservers) { 266static int best_offset_server(const ntp_server_results *slist, int nservers) {
266 int best_server = -1; 267 int best_server_index = -1;
267 268
268 /* for each server */ 269 /* for each server */
269 for (int cserver = 0; cserver < nservers; cserver++) { 270 for (int cserver = 0; cserver < nservers; cserver++) {
@@ -286,33 +287,33 @@ int best_offset_server(const ntp_server_results *slist, int nservers) {
286 } 287 }
287 288
288 /* If we don't have a server yet, use the first one */ 289 /* If we don't have a server yet, use the first one */
289 if (best_server == -1) { 290 if (best_server_index == -1) {
290 best_server = cserver; 291 best_server_index = cserver;
291 DBG(printf("using peer %d as our first candidate\n", best_server)); 292 DBG(printf("using peer %d as our first candidate\n", best_server_index));
292 continue; 293 continue;
293 } 294 }
294 295
295 /* compare the server to the best one we've seen so far */ 296 /* compare the server to the best one we've seen so far */
296 /* does it have an equal or better stratum? */ 297 /* does it have an equal or better stratum? */
297 DBG(printf("comparing peer %d with peer %d\n", cserver, best_server)); 298 DBG(printf("comparing peer %d with peer %d\n", cserver, best_server_index));
298 if (slist[cserver].stratum <= slist[best_server].stratum) { 299 if (slist[cserver].stratum <= slist[best_server_index].stratum) {
299 DBG(printf("stratum for peer %d <= peer %d\n", cserver, best_server)); 300 DBG(printf("stratum for peer %d <= peer %d\n", cserver, best_server_index));
300 /* does it have an equal or better dispersion? */ 301 /* does it have an equal or better dispersion? */
301 if (slist[cserver].rtdisp <= slist[best_server].rtdisp) { 302 if (slist[cserver].rtdisp <= slist[best_server_index].rtdisp) {
302 DBG(printf("dispersion for peer %d <= peer %d\n", cserver, best_server)); 303 DBG(printf("dispersion for peer %d <= peer %d\n", cserver, best_server_index));
303 /* does it have a better rtdelay? */ 304 /* does it have a better rtdelay? */
304 if (slist[cserver].rtdelay < slist[best_server].rtdelay) { 305 if (slist[cserver].rtdelay < slist[best_server_index].rtdelay) {
305 DBG(printf("rtdelay for peer %d < peer %d\n", cserver, best_server)); 306 DBG(printf("rtdelay for peer %d < peer %d\n", cserver, best_server_index));
306 best_server = cserver; 307 best_server_index = cserver;
307 DBG(printf("peer %d is now our best candidate\n", best_server)); 308 DBG(printf("peer %d is now our best candidate\n", best_server_index));
308 } 309 }
309 } 310 }
310 } 311 }
311 } 312 }
312 313
313 if (best_server >= 0) { 314 if (best_server_index >= 0) {
314 DBG(printf("best server selected: peer %d\n", best_server)); 315 DBG(printf("best server selected: peer %d\n", best_server_index));
315 return best_server; 316 return best_server_index;
316 } 317 }
317 DBG(printf("no peers meeting synchronization criteria :(\n")); 318 DBG(printf("no peers meeting synchronization criteria :(\n"));
318 return -1; 319 return -1;
@@ -323,7 +324,11 @@ int best_offset_server(const ntp_server_results *slist, int nservers) {
323 * we don't waste time sitting around waiting for single packets. 324 * we don't waste time sitting around waiting for single packets.
324 * - we also "manually" handle resolving host names and connecting, because 325 * - we also "manually" handle resolving host names and connecting, because
325 * we have to do it in a way that our lazy macros don't handle currently :( */ 326 * we have to do it in a way that our lazy macros don't handle currently :( */
326double offset_request(const char *host, const char *port, mp_state_enum *status, int time_offset) { 327typedef struct {
328 mp_state_enum offset_result;
329 double offset;
330} offset_request_wrapper;
331static offset_request_wrapper offset_request(const char *host, const char *port, int time_offset) {
327 /* setup hints to only return results from getaddrinfo that we'd like */ 332 /* setup hints to only return results from getaddrinfo that we'd like */
328 struct addrinfo hints; 333 struct addrinfo hints;
329 memset(&hints, 0, sizeof(struct addrinfo)); 334 memset(&hints, 0, sizeof(struct addrinfo));
@@ -462,12 +467,18 @@ double offset_request(const char *host, const char *port, mp_state_enum *status,
462 die(STATE_CRITICAL, "NTP CRITICAL: No response from NTP server\n"); 467 die(STATE_CRITICAL, "NTP CRITICAL: No response from NTP server\n");
463 } 468 }
464 469
470 offset_request_wrapper result = {
471 .offset = 0,
472 .offset_result = STATE_UNKNOWN,
473 };
474
465 /* now, pick the best server from the list */ 475 /* now, pick the best server from the list */
466 double avg_offset = 0.; 476 double avg_offset = 0.;
467 int best_index = best_offset_server(servers, num_hosts); 477 int best_index = best_offset_server(servers, num_hosts);
468 if (best_index < 0) { 478 if (best_index < 0) {
469 *status = STATE_UNKNOWN; 479 result.offset_result = STATE_UNKNOWN;
470 } else { 480 } else {
481 result.offset_result = STATE_OK;
471 /* finally, calculate the average offset */ 482 /* finally, calculate the average offset */
472 for (int i = 0; i < servers[best_index].num_responses; i++) { 483 for (int i = 0; i < servers[best_index].num_responses; i++) {
473 avg_offset += servers[best_index].offset[i]; 484 avg_offset += servers[best_index].offset[i];
@@ -488,22 +499,30 @@ double offset_request(const char *host, const char *port, mp_state_enum *status,
488 if (verbose) { 499 if (verbose) {
489 printf("overall average offset: %.10g\n", avg_offset); 500 printf("overall average offset: %.10g\n", avg_offset);
490 } 501 }
491 return avg_offset; 502
503 result.offset = avg_offset;
504 return result;
492} 505}
493 506
494check_ntp_time_config_wrapper process_arguments(int argc, char **argv) { 507static check_ntp_time_config_wrapper process_arguments(int argc, char **argv) {
508
509 enum {
510 output_format_index = CHAR_MAX + 1,
511 };
512
495 static struct option longopts[] = {{"version", no_argument, 0, 'V'}, 513 static struct option longopts[] = {{"version", no_argument, 0, 'V'},
496 {"help", no_argument, 0, 'h'}, 514 {"help", no_argument, 0, 'h'},
497 {"verbose", no_argument, 0, 'v'}, 515 {"verbose", no_argument, 0, 'v'},
498 {"use-ipv4", no_argument, 0, '4'}, 516 {"use-ipv4", no_argument, 0, '4'},
499 {"use-ipv6", no_argument, 0, '6'}, 517 {"use-ipv6", no_argument, 0, '6'},
500 {"quiet", no_argument, 0, 'q'}, 518 {"quiet", no_argument, 0, 'q'},
501 {"time-offset", optional_argument, 0, 'o'}, 519 {"time-offset", required_argument, 0, 'o'},
502 {"warning", required_argument, 0, 'w'}, 520 {"warning", required_argument, 0, 'w'},
503 {"critical", required_argument, 0, 'c'}, 521 {"critical", required_argument, 0, 'c'},
504 {"timeout", required_argument, 0, 't'}, 522 {"timeout", required_argument, 0, 't'},
505 {"hostname", required_argument, 0, 'H'}, 523 {"hostname", required_argument, 0, 'H'},
506 {"port", required_argument, 0, 'p'}, 524 {"port", required_argument, 0, 'p'},
525 {"output-format", required_argument, 0, output_format_index},
507 {0, 0, 0, 0}}; 526 {0, 0, 0, 0}};
508 527
509 if (argc < 2) { 528 if (argc < 2) {
@@ -515,9 +534,6 @@ check_ntp_time_config_wrapper process_arguments(int argc, char **argv) {
515 .config = check_ntp_time_config_init(), 534 .config = check_ntp_time_config_init(),
516 }; 535 };
517 536
518 char *owarn = "60";
519 char *ocrit = "120";
520
521 while (true) { 537 while (true) {
522 int option = 0; 538 int option = 0;
523 int option_char = getopt_long(argc, argv, "Vhv46qw:c:t:H:p:o:", longopts, &option); 539 int option_char = getopt_long(argc, argv, "Vhv46qw:c:t:H:p:o:", longopts, &option);
@@ -526,6 +542,17 @@ check_ntp_time_config_wrapper process_arguments(int argc, char **argv) {
526 } 542 }
527 543
528 switch (option_char) { 544 switch (option_char) {
545 case output_format_index: {
546 parsed_output_format parser = mp_parse_output_format(optarg);
547 if (!parser.parsing_success) {
548 printf("Invalid output format: %s\n", optarg);
549 exit(STATE_UNKNOWN);
550 }
551
552 result.config.output_format_is_set = true;
553 result.config.output_format = parser.output_format;
554 break;
555 }
529 case 'h': 556 case 'h':
530 print_help(); 557 print_help();
531 exit(STATE_UNKNOWN); 558 exit(STATE_UNKNOWN);
@@ -540,12 +567,24 @@ check_ntp_time_config_wrapper process_arguments(int argc, char **argv) {
540 case 'q': 567 case 'q':
541 result.config.quiet = true; 568 result.config.quiet = true;
542 break; 569 break;
543 case 'w': 570 case 'w': {
544 owarn = optarg; 571 mp_range_parsed tmp = mp_parse_range_string(optarg);
545 break; 572 if (tmp.error != MP_PARSING_SUCCES) {
546 case 'c': 573 die(STATE_UNKNOWN, "failed to parse warning threshold");
547 ocrit = optarg; 574 }
548 break; 575
576 result.config.offset_thresholds =
577 mp_thresholds_set_warn(result.config.offset_thresholds, tmp.range);
578 } break;
579 case 'c': {
580 mp_range_parsed tmp = mp_parse_range_string(optarg);
581 if (tmp.error != MP_PARSING_SUCCES) {
582 die(STATE_UNKNOWN, "failed to parse crit threshold");
583 }
584
585 result.config.offset_thresholds =
586 mp_thresholds_set_crit(result.config.offset_thresholds, tmp.range);
587 } break;
549 case 'H': 588 case 'H':
550 if (!is_host(optarg)) { 589 if (!is_host(optarg)) {
551 usage2(_("Invalid hostname/address"), optarg); 590 usage2(_("Invalid hostname/address"), optarg);
@@ -582,16 +621,9 @@ check_ntp_time_config_wrapper process_arguments(int argc, char **argv) {
582 usage4(_("Hostname was not supplied")); 621 usage4(_("Hostname was not supplied"));
583 } 622 }
584 623
585 set_thresholds(&result.config.offset_thresholds, owarn, ocrit);
586
587 return result; 624 return result;
588} 625}
589 626
590char *perfd_offset(double offset, thresholds *offset_thresholds) {
591 return fperfdata("offset", offset, "s", true, offset_thresholds->warning->end, true,
592 offset_thresholds->critical->end, false, 0, false, 0);
593}
594
595int main(int argc, char *argv[]) { 627int main(int argc, char *argv[]) {
596 setlocale(LC_ALL, ""); 628 setlocale(LC_ALL, "");
597 bindtextdomain(PACKAGE, LOCALEDIR); 629 bindtextdomain(PACKAGE, LOCALEDIR);
@@ -608,52 +640,47 @@ int main(int argc, char *argv[]) {
608 640
609 const check_ntp_time_config config = tmp_config.config; 641 const check_ntp_time_config config = tmp_config.config;
610 642
643 if (config.output_format_is_set) {
644 mp_set_format(config.output_format);
645 }
646
611 /* initialize alarm signal handling */ 647 /* initialize alarm signal handling */
612 signal(SIGALRM, socket_timeout_alarm_handler); 648 signal(SIGALRM, socket_timeout_alarm_handler);
613 649
614 /* set socket timeout */ 650 /* set socket timeout */
615 alarm(socket_timeout); 651 alarm(socket_timeout);
616 652
617 mp_state_enum offset_result = STATE_OK; 653 mp_check overall = mp_check_init();
618 mp_state_enum result = STATE_OK;
619 double offset =
620 offset_request(config.server_address, config.port, &offset_result, config.time_offset);
621 if (offset_result == STATE_UNKNOWN) {
622 result = ((!config.quiet) ? STATE_UNKNOWN : STATE_CRITICAL);
623 } else {
624 result = get_status(fabs(offset), config.offset_thresholds);
625 }
626 654
627 char *result_line; 655 mp_subcheck sc_offset = mp_subcheck_init();
628 switch (result) { 656 offset_request_wrapper offset_result =
629 case STATE_CRITICAL: 657 offset_request(config.server_address, config.port, config.time_offset);
630 xasprintf(&result_line, _("NTP CRITICAL:"));
631 break;
632 case STATE_WARNING:
633 xasprintf(&result_line, _("NTP WARNING:"));
634 break;
635 case STATE_OK:
636 xasprintf(&result_line, _("NTP OK:"));
637 break;
638 default:
639 xasprintf(&result_line, _("NTP UNKNOWN:"));
640 break;
641 }
642 658
643 char *perfdata_line; 659 if (offset_result.offset_result == STATE_UNKNOWN) {
644 if (offset_result == STATE_UNKNOWN) { 660 sc_offset =
645 xasprintf(&result_line, "%s %s", result_line, _("Offset unknown")); 661 mp_set_subcheck_state(sc_offset, (!config.quiet) ? STATE_UNKNOWN : STATE_CRITICAL);
646 xasprintf(&perfdata_line, ""); 662 xasprintf(&sc_offset.output, "Offset unknown");
647 } else { 663 mp_add_subcheck_to_check(&overall, sc_offset);
648 xasprintf(&result_line, "%s %s %.10g secs", result_line, _("Offset"), offset); 664 mp_exit(overall);
649 xasprintf(&perfdata_line, "%s", perfd_offset(offset, config.offset_thresholds));
650 } 665 }
651 printf("%s|%s\n", result_line, perfdata_line); 666
667 xasprintf(&sc_offset.output, "Offset: %.6fs", offset_result.offset);
668
669 mp_perfdata pd_offset = perfdata_init();
670 pd_offset = mp_set_pd_value(pd_offset, fabs(offset_result.offset));
671 pd_offset.label = "offset";
672 pd_offset.uom = "s";
673 pd_offset = mp_pd_set_thresholds(pd_offset, config.offset_thresholds);
674
675 sc_offset = mp_set_subcheck_state(sc_offset, mp_get_pd_status(pd_offset));
676
677 mp_add_perfdata_to_subcheck(&sc_offset, pd_offset);
678 mp_add_subcheck_to_check(&overall, sc_offset);
652 679
653 if (config.server_address != NULL) { 680 if (config.server_address != NULL) {
654 free(config.server_address); 681 free(config.server_address);
655 } 682 }
656 exit(result); 683 mp_exit(overall);
657} 684}
658 685
659void print_help(void) { 686void print_help(void) {
@@ -677,10 +704,11 @@ void print_help(void) {
677 printf(" %s\n", _("Offset to result in warning status (seconds)")); 704 printf(" %s\n", _("Offset to result in warning status (seconds)"));
678 printf(" %s\n", "-c, --critical=THRESHOLD"); 705 printf(" %s\n", "-c, --critical=THRESHOLD");
679 printf(" %s\n", _("Offset to result in critical status (seconds)")); 706 printf(" %s\n", _("Offset to result in critical status (seconds)"));
680 printf(" %s\n", "-o, --time_offset=INTEGER"); 707 printf(" %s\n", "-o, --time-offset=INTEGER");
681 printf(" %s\n", _("Expected offset of the ntp server relative to local server (seconds)")); 708 printf(" %s\n", _("Expected offset of the ntp server relative to local server (seconds)"));
682 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 709 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
683 printf(UT_VERBOSE); 710 printf(UT_VERBOSE);
711 printf(UT_OUTPUT_FORMAT);
684 712
685 printf("\n"); 713 printf("\n");
686 printf("%s\n", _("This plugin checks the clock offset between the local host and a")); 714 printf("%s\n", _("This plugin checks the clock offset between the local host and a"));