diff options
| author | Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> | 2025-11-04 12:08:59 +0100 |
|---|---|---|
| committer | Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> | 2025-11-04 12:08:59 +0100 |
| commit | b35853ee4e10c4485a9521d77c95aecae6573e64 (patch) | |
| tree | 29402e5bbf420523682d3ef4357e609c93f8526f | |
| parent | bb9fcf5bfaf34aeffd651919b5a14b631b9fceb4 (diff) | |
| download | monitoring-plugins-b35853ee4e10c4485a9521d77c95aecae6573e64.tar.gz | |
check_ntp_time: implement modern output
| -rw-r--r-- | plugins/check_ntp_time.c | 160 | ||||
| -rw-r--r-- | plugins/check_ntp_time.d/config.h | 13 |
2 files changed, 93 insertions, 80 deletions
diff --git a/plugins/check_ntp_time.c b/plugins/check_ntp_time.c index ad69b804..eee9c298 100644 --- a/plugins/check_ntp_time.c +++ b/plugins/check_ntp_time.c | |||
| @@ -34,12 +34,10 @@ | |||
| 34 | * | 34 | * |
| 35 | *****************************************************************************/ | 35 | *****************************************************************************/ |
| 36 | 36 | ||
| 37 | const char *progname = "check_ntp_time"; | 37 | #include "output.h" |
| 38 | const char *copyright = "2006-2024"; | ||
| 39 | const 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 | ||
| 48 | static int verbose = 0; | 46 | static int verbose = 0; |
| 49 | 47 | ||
| 48 | const char *progname = "check_ntp_time"; | ||
| 49 | const char *copyright = "2006-2024"; | ||
| 50 | const char *email = "devel@monitoring-plugins.org"; | ||
| 51 | |||
| 50 | typedef struct { | 52 | typedef 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 */ |
| 68 | typedef struct { | 67 | typedef 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)) | 171 | static 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. */ |
| 265 | int best_offset_server(const ntp_server_results *slist, int nservers) { | 266 | static 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 :( */ |
| 326 | double offset_request(const char *host, const char *port, mp_state_enum *status, int time_offset) { | 327 | typedef struct { |
| 328 | mp_state_enum offset_result; | ||
| 329 | double offset; | ||
| 330 | } offset_request_wrapper; | ||
| 331 | static 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,11 +467,16 @@ 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 { |
| 471 | /* finally, calculate the average offset */ | 481 | /* finally, calculate the average offset */ |
| 472 | for (int i = 0; i < servers[best_index].num_responses; i++) { | 482 | for (int i = 0; i < servers[best_index].num_responses; i++) { |
| @@ -488,10 +498,12 @@ double offset_request(const char *host, const char *port, mp_state_enum *status, | |||
| 488 | if (verbose) { | 498 | if (verbose) { |
| 489 | printf("overall average offset: %.10g\n", avg_offset); | 499 | printf("overall average offset: %.10g\n", avg_offset); |
| 490 | } | 500 | } |
| 491 | return avg_offset; | 501 | |
| 502 | result.offset = avg_offset; | ||
| 503 | return result; | ||
| 492 | } | 504 | } |
| 493 | 505 | ||
| 494 | check_ntp_time_config_wrapper process_arguments(int argc, char **argv) { | 506 | static check_ntp_time_config_wrapper process_arguments(int argc, char **argv) { |
| 495 | static struct option longopts[] = {{"version", no_argument, 0, 'V'}, | 507 | static struct option longopts[] = {{"version", no_argument, 0, 'V'}, |
| 496 | {"help", no_argument, 0, 'h'}, | 508 | {"help", no_argument, 0, 'h'}, |
| 497 | {"verbose", no_argument, 0, 'v'}, | 509 | {"verbose", no_argument, 0, 'v'}, |
| @@ -515,9 +527,6 @@ check_ntp_time_config_wrapper process_arguments(int argc, char **argv) { | |||
| 515 | .config = check_ntp_time_config_init(), | 527 | .config = check_ntp_time_config_init(), |
| 516 | }; | 528 | }; |
| 517 | 529 | ||
| 518 | char *owarn = "60"; | ||
| 519 | char *ocrit = "120"; | ||
| 520 | |||
| 521 | while (true) { | 530 | while (true) { |
| 522 | int option = 0; | 531 | int option = 0; |
| 523 | int option_char = getopt_long(argc, argv, "Vhv46qw:c:t:H:p:o:", longopts, &option); | 532 | int option_char = getopt_long(argc, argv, "Vhv46qw:c:t:H:p:o:", longopts, &option); |
| @@ -540,12 +549,24 @@ check_ntp_time_config_wrapper process_arguments(int argc, char **argv) { | |||
| 540 | case 'q': | 549 | case 'q': |
| 541 | result.config.quiet = true; | 550 | result.config.quiet = true; |
| 542 | break; | 551 | break; |
| 543 | case 'w': | 552 | case 'w': { |
| 544 | owarn = optarg; | 553 | mp_range_parsed tmp = mp_parse_range_string(optarg); |
| 545 | break; | 554 | if (tmp.error != MP_PARSING_SUCCES) { |
| 546 | case 'c': | 555 | die(STATE_UNKNOWN, "failed to parse warning threshold"); |
| 547 | ocrit = optarg; | 556 | } |
| 548 | break; | 557 | |
| 558 | result.config.offset_thresholds = | ||
| 559 | mp_thresholds_set_warn(result.config.offset_thresholds, tmp.range); | ||
| 560 | } break; | ||
| 561 | case 'c': { | ||
| 562 | mp_range_parsed tmp = mp_parse_range_string(optarg); | ||
| 563 | if (tmp.error != MP_PARSING_SUCCES) { | ||
| 564 | die(STATE_UNKNOWN, "failed to parse crit threshold"); | ||
| 565 | } | ||
| 566 | |||
| 567 | result.config.offset_thresholds = | ||
| 568 | mp_thresholds_set_crit(result.config.offset_thresholds, tmp.range); | ||
| 569 | } break; | ||
| 549 | case 'H': | 570 | case 'H': |
| 550 | if (!is_host(optarg)) { | 571 | if (!is_host(optarg)) { |
| 551 | usage2(_("Invalid hostname/address"), optarg); | 572 | usage2(_("Invalid hostname/address"), optarg); |
| @@ -582,16 +603,9 @@ check_ntp_time_config_wrapper process_arguments(int argc, char **argv) { | |||
| 582 | usage4(_("Hostname was not supplied")); | 603 | usage4(_("Hostname was not supplied")); |
| 583 | } | 604 | } |
| 584 | 605 | ||
| 585 | set_thresholds(&result.config.offset_thresholds, owarn, ocrit); | ||
| 586 | |||
| 587 | return result; | 606 | return result; |
| 588 | } | 607 | } |
| 589 | 608 | ||
| 590 | char *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 | |||
| 595 | int main(int argc, char *argv[]) { | 609 | int main(int argc, char *argv[]) { |
| 596 | setlocale(LC_ALL, ""); | 610 | setlocale(LC_ALL, ""); |
| 597 | bindtextdomain(PACKAGE, LOCALEDIR); | 611 | bindtextdomain(PACKAGE, LOCALEDIR); |
| @@ -614,46 +628,36 @@ int main(int argc, char *argv[]) { | |||
| 614 | /* set socket timeout */ | 628 | /* set socket timeout */ |
| 615 | alarm(socket_timeout); | 629 | alarm(socket_timeout); |
| 616 | 630 | ||
| 617 | mp_state_enum offset_result = STATE_OK; | 631 | 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 | 632 | ||
| 627 | char *result_line; | 633 | mp_subcheck sc_offset = mp_subcheck_init(); |
| 628 | switch (result) { | 634 | offset_request_wrapper offset_result = |
| 629 | case STATE_CRITICAL: | 635 | 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 | 636 | ||
| 643 | char *perfdata_line; | 637 | if (offset_result.offset_result == STATE_UNKNOWN) { |
| 644 | if (offset_result == STATE_UNKNOWN) { | 638 | sc_offset = |
| 645 | xasprintf(&result_line, "%s %s", result_line, _("Offset unknown")); | 639 | mp_set_subcheck_state(sc_offset, (!config.quiet) ? STATE_UNKNOWN : STATE_CRITICAL); |
| 646 | xasprintf(&perfdata_line, ""); | 640 | xasprintf(&sc_offset.output, "Offset unknown"); |
| 647 | } else { | 641 | mp_add_subcheck_to_check(&overall, sc_offset); |
| 648 | xasprintf(&result_line, "%s %s %.10g secs", result_line, _("Offset"), offset); | 642 | mp_exit(overall); |
| 649 | xasprintf(&perfdata_line, "%s", perfd_offset(offset, config.offset_thresholds)); | ||
| 650 | } | 643 | } |
| 651 | printf("%s|%s\n", result_line, perfdata_line); | 644 | |
| 645 | xasprintf(&sc_offset.output, "Offset: %.10g s", offset_result.offset); | ||
| 646 | |||
| 647 | mp_perfdata pd_offset = perfdata_init(); | ||
| 648 | pd_offset = mp_set_pd_value(pd_offset, fabs(offset_result.offset)); | ||
| 649 | pd_offset.label = "offset"; | ||
| 650 | pd_offset = mp_pd_set_thresholds(pd_offset, config.offset_thresholds); | ||
| 651 | |||
| 652 | sc_offset = mp_set_subcheck_state(sc_offset, mp_get_pd_status(pd_offset)); | ||
| 653 | |||
| 654 | mp_add_perfdata_to_subcheck(&sc_offset, pd_offset); | ||
| 655 | mp_add_subcheck_to_check(&overall, sc_offset); | ||
| 652 | 656 | ||
| 653 | if (config.server_address != NULL) { | 657 | if (config.server_address != NULL) { |
| 654 | free(config.server_address); | 658 | free(config.server_address); |
| 655 | } | 659 | } |
| 656 | exit(result); | 660 | mp_exit(overall); |
| 657 | } | 661 | } |
| 658 | 662 | ||
| 659 | void print_help(void) { | 663 | void print_help(void) { |
diff --git a/plugins/check_ntp_time.d/config.h b/plugins/check_ntp_time.d/config.h index 99dabbbd..a62e4ceb 100644 --- a/plugins/check_ntp_time.d/config.h +++ b/plugins/check_ntp_time.d/config.h | |||
| @@ -11,7 +11,7 @@ typedef struct { | |||
| 11 | bool quiet; | 11 | bool quiet; |
| 12 | int time_offset; | 12 | int time_offset; |
| 13 | 13 | ||
| 14 | thresholds *offset_thresholds; | 14 | mp_thresholds offset_thresholds; |
| 15 | } check_ntp_time_config; | 15 | } check_ntp_time_config; |
| 16 | 16 | ||
| 17 | check_ntp_time_config check_ntp_time_config_init() { | 17 | check_ntp_time_config check_ntp_time_config_init() { |
| @@ -22,7 +22,16 @@ check_ntp_time_config check_ntp_time_config_init() { | |||
| 22 | .quiet = false, | 22 | .quiet = false, |
| 23 | .time_offset = 0, | 23 | .time_offset = 0, |
| 24 | 24 | ||
| 25 | .offset_thresholds = NULL, | 25 | .offset_thresholds = mp_thresholds_init(), |
| 26 | }; | 26 | }; |
| 27 | |||
| 28 | mp_range warning = mp_range_init(); | ||
| 29 | warning = mp_range_set_end(warning, mp_create_pd_value(60)); | ||
| 30 | tmp.offset_thresholds = mp_thresholds_set_warn(tmp.offset_thresholds, warning); | ||
| 31 | |||
| 32 | mp_range critical = mp_range_init(); | ||
| 33 | critical = mp_range_set_end(warning, mp_create_pd_value(120)); | ||
| 34 | tmp.offset_thresholds = mp_thresholds_set_crit(tmp.offset_thresholds, critical); | ||
| 35 | |||
| 27 | return tmp; | 36 | return tmp; |
| 28 | } | 37 | } |
