summaryrefslogtreecommitdiffstats
path: root/plugins/check_ntp_peer.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/check_ntp_peer.c')
-rw-r--r--plugins/check_ntp_peer.c434
1 files changed, 259 insertions, 175 deletions
diff --git a/plugins/check_ntp_peer.c b/plugins/check_ntp_peer.c
index 6e76bf23..26f74286 100644
--- a/plugins/check_ntp_peer.c
+++ b/plugins/check_ntp_peer.c
@@ -35,11 +35,14 @@
35 * 35 *
36 *****************************************************************************/ 36 *****************************************************************************/
37 37
38#include "thresholds.h"
39const char *progname = "check_ntp_peer"; 38const char *progname = "check_ntp_peer";
40const char *copyright = "2006-2024"; 39const char *copyright = "2006-2024";
41const char *email = "devel@monitoring-plugins.org"; 40const char *email = "devel@monitoring-plugins.org";
42 41
42#include "output.h"
43#include "perfdata.h"
44#include <openssl/x509.h>
45#include "thresholds.h"
43#include "common.h" 46#include "common.h"
44#include "netutils.h" 47#include "netutils.h"
45#include "utils.h" 48#include "utils.h"
@@ -47,8 +50,6 @@ const char *email = "devel@monitoring-plugins.org";
47#include "check_ntp_peer.d/config.h" 50#include "check_ntp_peer.d/config.h"
48 51
49static int verbose = 0; 52static int verbose = 0;
50static bool syncsource_found = false;
51static bool li_alarm = false;
52 53
53typedef struct { 54typedef struct {
54 int errorcode; 55 int errorcode;
@@ -83,9 +84,9 @@ typedef struct {
83/* bits 1,2 are the leap indicator */ 84/* bits 1,2 are the leap indicator */
84#define LI_MASK 0xc0 85#define LI_MASK 0xc0
85#define LI(x) ((x & LI_MASK) >> 6) 86#define LI(x) ((x & LI_MASK) >> 6)
86#define LI_SET(x, y) \ 87#define LI_SET(x, y) \
87 do { \ 88 do { \
88 x |= ((y << 6) & LI_MASK); \ 89 x |= ((y << 6) & LI_MASK); \
89 } while (0) 90 } while (0)
90/* and these are the values of the leap indicator */ 91/* and these are the values of the leap indicator */
91#define LI_NOWARNING 0x00 92#define LI_NOWARNING 0x00
@@ -95,17 +96,17 @@ typedef struct {
95/* bits 3,4,5 are the ntp version */ 96/* bits 3,4,5 are the ntp version */
96#define VN_MASK 0x38 97#define VN_MASK 0x38
97#define VN(x) ((x & VN_MASK) >> 3) 98#define VN(x) ((x & VN_MASK) >> 3)
98#define VN_SET(x, y) \ 99#define VN_SET(x, y) \
99 do { \ 100 do { \
100 x |= ((y << 3) & VN_MASK); \ 101 x |= ((y << 3) & VN_MASK); \
101 } while (0) 102 } while (0)
102#define VN_RESERVED 0x02 103#define VN_RESERVED 0x02
103/* bits 6,7,8 are the ntp mode */ 104/* bits 6,7,8 are the ntp mode */
104#define MODE_MASK 0x07 105#define MODE_MASK 0x07
105#define MODE(x) (x & MODE_MASK) 106#define MODE(x) (x & MODE_MASK)
106#define MODE_SET(x, y) \ 107#define MODE_SET(x, y) \
107 do { \ 108 do { \
108 x |= (y & MODE_MASK); \ 109 x |= (y & MODE_MASK); \
109 } while (0) 110 } while (0)
110/* here are some values */ 111/* here are some values */
111#define MODE_CLIENT 0x03 112#define MODE_CLIENT 0x03
@@ -117,9 +118,9 @@ typedef struct {
117#define REM_MORE 0x20 118#define REM_MORE 0x20
118/* In control message, bits 11 - 15 are opcode */ 119/* In control message, bits 11 - 15 are opcode */
119#define OP_MASK 0x1f 120#define OP_MASK 0x1f
120#define OP_SET(x, y) \ 121#define OP_SET(x, y) \
121 do { \ 122 do { \
122 x |= (y & OP_MASK); \ 123 x |= (y & OP_MASK); \
123 } while (0) 124 } while (0)
124#define OP_READSTAT 0x01 125#define OP_READSTAT 0x01
125#define OP_READVAR 0x02 126#define OP_READVAR 0x02
@@ -132,18 +133,19 @@ typedef struct {
132/* NTP control message header is 12 bytes, plus any data in the data 133/* NTP control message header is 12 bytes, plus any data in the data
133 * field, plus null padding to the nearest 32-bit boundary per rfc. 134 * field, plus null padding to the nearest 32-bit boundary per rfc.
134 */ 135 */
135#define SIZEOF_NTPCM(m) (12 + ntohs(m.count) + ((ntohs(m.count) % 4) ? 4 - (ntohs(m.count) % 4) : 0)) 136#define SIZEOF_NTPCM(m) \
137 (12 + ntohs(m.count) + ((ntohs(m.count) % 4) ? 4 - (ntohs(m.count) % 4) : 0))
136 138
137/* finally, a little helper or two for debugging: */ 139/* finally, a little helper or two for debugging: */
138#define DBG(x) \ 140#define DBG(x) \
139 do { \ 141 do { \
140 if (verbose > 1) { \ 142 if (verbose > 1) { \
141 x; \ 143 x; \
142 } \ 144 } \
143 } while (0); 145 } while (0);
144#define PRINTSOCKADDR(x) \ 146#define PRINTSOCKADDR(x) \
145 do { \ 147 do { \
146 printf("%u.%u.%u.%u", (x >> 24) & 0xff, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff); \ 148 printf("%u.%u.%u.%u", (x >> 24) & 0xff, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff); \
147 } while (0); 149 } while (0);
148 150
149void print_ntp_control_message(const ntp_control_message *message) { 151void print_ntp_control_message(const ntp_control_message *message) {
@@ -197,9 +199,7 @@ void setup_control_request(ntp_control_message *message, uint8_t opcode, uint16_
197 * positive value means a success retrieving the value. 199 * positive value means a success retrieving the value.
198 * - status is set to WARNING if there's no sync.peer (otherwise OK) and is 200 * - status is set to WARNING if there's no sync.peer (otherwise OK) and is
199 * the return value of the function. 201 * the return value of the function.
200 * status is pretty much useless as syncsource_found is a global variable 202 */
201 * used later in main to check is the server was synchronized. It works
202 * so I left it alone */
203typedef struct { 203typedef struct {
204 mp_state_enum state; 204 mp_state_enum state;
205 mp_state_enum offset_result; 205 mp_state_enum offset_result;
@@ -207,6 +207,8 @@ typedef struct {
207 double jitter; 207 double jitter;
208 long stratum; 208 long stratum;
209 int num_truechimers; 209 int num_truechimers;
210 bool syncsource_found;
211 bool li_alarm;
210} ntp_request_result; 212} ntp_request_result;
211ntp_request_result ntp_request(const check_ntp_peer_config config) { 213ntp_request_result ntp_request(const check_ntp_peer_config config) {
212 214
@@ -216,6 +218,8 @@ ntp_request_result ntp_request(const check_ntp_peer_config config) {
216 .jitter = -1, 218 .jitter = -1,
217 .stratum = -1, 219 .stratum = -1,
218 .num_truechimers = 0, 220 .num_truechimers = 0,
221 .syncsource_found = false,
222 .li_alarm = false,
219 }; 223 };
220 224
221 /* Long-winded explanation: 225 /* Long-winded explanation:
@@ -234,19 +238,16 @@ ntp_request_result ntp_request(const check_ntp_peer_config config) {
234 * 4) Extract the offset, jitter and stratum value from the data[] 238 * 4) Extract the offset, jitter and stratum value from the data[]
235 * (it's ASCII) 239 * (it's ASCII)
236 */ 240 */
237 int min_peer_sel = PEER_INCLUDED;
238 int num_candidates = 0;
239 void *tmp;
240 ntp_assoc_status_pair *peers = NULL;
241 int peer_offset = 0;
242 size_t peers_size = 0;
243 size_t npeers = 0;
244 int conn = -1; 241 int conn = -1;
245 my_udp_connect(config.server_address, config.port, &conn); 242 my_udp_connect(config.server_address, config.port, &conn);
246 243
247 /* keep sending requests until the server stops setting the 244 /* keep sending requests until the server stops setting the
248 * REM_MORE bit, though usually this is only 1 packet. */ 245 * REM_MORE bit, though usually this is only 1 packet. */
249 ntp_control_message req; 246 ntp_control_message req;
247 ntp_assoc_status_pair *peers = NULL;
248 int peer_offset = 0;
249 size_t peers_size = 0;
250 size_t npeers = 0;
250 do { 251 do {
251 setup_control_request(&req, OP_READSTAT, 1); 252 setup_control_request(&req, OP_READSTAT, 1);
252 DBG(printf("sending READSTAT request")); 253 DBG(printf("sending READSTAT request"));
@@ -268,12 +269,13 @@ ntp_request_result ntp_request(const check_ntp_peer_config config) {
268 } while (!(req.op & OP_READSTAT && ntohs(req.seq) == 1)); 269 } while (!(req.op & OP_READSTAT && ntohs(req.seq) == 1));
269 270
270 if (LI(req.flags) == LI_ALARM) { 271 if (LI(req.flags) == LI_ALARM) {
271 li_alarm = true; 272 result.li_alarm = true;
272 } 273 }
273 /* Each peer identifier is 4 bytes in the data section, which 274 /* Each peer identifier is 4 bytes in the data section, which
274 * we represent as a ntp_assoc_status_pair datatype. 275 * we represent as a ntp_assoc_status_pair datatype.
275 */ 276 */
276 peers_size += ntohs(req.count); 277 peers_size += ntohs(req.count);
278 void *tmp;
277 if ((tmp = realloc(peers, peers_size)) == NULL) { 279 if ((tmp = realloc(peers, peers_size)) == NULL) {
278 free(peers), die(STATE_UNKNOWN, "can not (re)allocate 'peers' buffer\n"); 280 free(peers), die(STATE_UNKNOWN, "can not (re)allocate 'peers' buffer\n");
279 } 281 }
@@ -286,13 +288,15 @@ ntp_request_result ntp_request(const check_ntp_peer_config config) {
286 /* first, let's find out if we have a sync source, or if there are 288 /* first, let's find out if we have a sync source, or if there are
287 * at least some candidates. In the latter case we'll issue 289 * at least some candidates. In the latter case we'll issue
288 * a warning but go ahead with the check on them. */ 290 * a warning but go ahead with the check on them. */
291 int min_peer_sel = PEER_INCLUDED;
292 int num_candidates = 0;
289 for (size_t i = 0; i < npeers; i++) { 293 for (size_t i = 0; i < npeers; i++) {
290 if (PEER_SEL(peers[i].status) >= PEER_TRUECHIMER) { 294 if (PEER_SEL(peers[i].status) >= PEER_TRUECHIMER) {
291 result.num_truechimers++; 295 result.num_truechimers++;
292 if (PEER_SEL(peers[i].status) >= PEER_INCLUDED) { 296 if (PEER_SEL(peers[i].status) >= PEER_INCLUDED) {
293 num_candidates++; 297 num_candidates++;
294 if (PEER_SEL(peers[i].status) >= PEER_SYNCSOURCE) { 298 if (PEER_SEL(peers[i].status) >= PEER_SYNCSOURCE) {
295 syncsource_found = true; 299 result.syncsource_found = true;
296 min_peer_sel = PEER_SYNCSOURCE; 300 min_peer_sel = PEER_SYNCSOURCE;
297 } 301 }
298 } 302 }
@@ -301,18 +305,18 @@ ntp_request_result ntp_request(const check_ntp_peer_config config) {
301 305
302 if (verbose) { 306 if (verbose) {
303 printf("%d candidate peers available\n", num_candidates); 307 printf("%d candidate peers available\n", num_candidates);
304 } 308 if (result.syncsource_found) {
305 if (verbose && syncsource_found) { 309 printf("synchronization source found\n");
306 printf("synchronization source found\n"); 310 }
307 } 311 }
308 312
309 if (!syncsource_found) { 313 if (!result.syncsource_found) {
310 result.state = STATE_WARNING; 314 result.state = STATE_WARNING;
311 if (verbose) { 315 if (verbose) {
312 printf("warning: no synchronization source found\n"); 316 printf("warning: no synchronization source found\n");
313 } 317 }
314 } 318 }
315 if (li_alarm) { 319 if (result.li_alarm) {
316 result.state = STATE_WARNING; 320 result.state = STATE_WARNING;
317 if (verbose) { 321 if (verbose) {
318 printf("warning: LI_ALARM bit is set\n"); 322 printf("warning: LI_ALARM bit is set\n");
@@ -328,7 +332,7 @@ ntp_request_result ntp_request(const check_ntp_peer_config config) {
328 if (verbose) { 332 if (verbose) {
329 printf("Getting offset, jitter and stratum for peer %.2x\n", ntohs(peers[i].assoc)); 333 printf("Getting offset, jitter and stratum for peer %.2x\n", ntohs(peers[i].assoc));
330 } 334 }
331 xasprintf(&data, ""); 335 data = strdup("");
332 do { 336 do {
333 setup_control_request(&req, OP_READVAR, 2); 337 setup_control_request(&req, OP_READVAR, 2);
334 req.assoc = peers[i].assoc; 338 req.assoc = peers[i].assoc;
@@ -360,7 +364,8 @@ ntp_request_result ntp_request(const check_ntp_peer_config config) {
360 if (req.op & REM_ERROR) { 364 if (req.op & REM_ERROR) {
361 if (strstr(getvar, "jitter")) { 365 if (strstr(getvar, "jitter")) {
362 if (verbose) { 366 if (verbose) {
363 printf("The command failed. This is usually caused by servers refusing the 'jitter'\nvariable. Restarting with " 367 printf("The command failed. This is usually caused by servers refusing the "
368 "'jitter'\nvariable. Restarting with "
364 "'dispersion'...\n"); 369 "'dispersion'...\n");
365 } 370 }
366 getvar = "stratum,offset,dispersion"; 371 getvar = "stratum,offset,dispersion";
@@ -404,7 +409,8 @@ ntp_request_result ntp_request(const check_ntp_peer_config config) {
404 if (verbose) { 409 if (verbose) {
405 printf("%.10g\n", tmp_offset); 410 printf("%.10g\n", tmp_offset);
406 } 411 }
407 if (result.offset_result == STATE_UNKNOWN || fabs(tmp_offset) < fabs(result.offset)) { 412 if (result.offset_result == STATE_UNKNOWN ||
413 fabs(tmp_offset) < fabs(result.offset)) {
408 result.offset = tmp_offset; 414 result.offset = tmp_offset;
409 result.offset_result = STATE_OK; 415 result.offset_result = STATE_OK;
410 } else { 416 } else {
@@ -416,10 +422,12 @@ ntp_request_result ntp_request(const check_ntp_peer_config config) {
416 if (config.do_jitter) { 422 if (config.do_jitter) {
417 /* get the jitter */ 423 /* get the jitter */
418 if (verbose) { 424 if (verbose) {
419 printf("parsing %s from peer %.2x: ", strstr(getvar, "dispersion") != NULL ? "dispersion" : "jitter", 425 printf("parsing %s from peer %.2x: ",
426 strstr(getvar, "dispersion") != NULL ? "dispersion" : "jitter",
420 ntohs(peers[i].assoc)); 427 ntohs(peers[i].assoc));
421 } 428 }
422 value = np_extract_ntpvar(data, strstr(getvar, "dispersion") != NULL ? "dispersion" : "jitter"); 429 value = np_extract_ntpvar(data, strstr(getvar, "dispersion") != NULL ? "dispersion"
430 : "jitter");
423 nptr = NULL; 431 nptr = NULL;
424 /* Convert the value if we have one */ 432 /* Convert the value if we have one */
425 if (value != NULL) { 433 if (value != NULL) {
@@ -470,13 +478,30 @@ ntp_request_result ntp_request(const check_ntp_peer_config config) {
470} 478}
471 479
472check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) { 480check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) {
473 static struct option longopts[] = { 481
474 {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, {"verbose", no_argument, 0, 'v'}, 482 enum {
475 {"use-ipv4", no_argument, 0, '4'}, {"use-ipv6", no_argument, 0, '6'}, {"quiet", no_argument, 0, 'q'}, 483 output_format_index = CHAR_MAX + 1,
476 {"warning", required_argument, 0, 'w'}, {"critical", required_argument, 0, 'c'}, {"swarn", required_argument, 0, 'W'}, 484 };
477 {"scrit", required_argument, 0, 'C'}, {"jwarn", required_argument, 0, 'j'}, {"jcrit", required_argument, 0, 'k'}, 485
478 {"twarn", required_argument, 0, 'm'}, {"tcrit", required_argument, 0, 'n'}, {"timeout", required_argument, 0, 't'}, 486 static struct option longopts[] = {{"version", no_argument, 0, 'V'},
479 {"hostname", required_argument, 0, 'H'}, {"port", required_argument, 0, 'p'}, {0, 0, 0, 0}}; 487 {"help", no_argument, 0, 'h'},
488 {"verbose", no_argument, 0, 'v'},
489 {"use-ipv4", no_argument, 0, '4'},
490 {"use-ipv6", no_argument, 0, '6'},
491 {"quiet", no_argument, 0, 'q'},
492 {"warning", required_argument, 0, 'w'},
493 {"critical", required_argument, 0, 'c'},
494 {"swarn", required_argument, 0, 'W'},
495 {"scrit", required_argument, 0, 'C'},
496 {"jwarn", required_argument, 0, 'j'},
497 {"jcrit", required_argument, 0, 'k'},
498 {"twarn", required_argument, 0, 'm'},
499 {"tcrit", required_argument, 0, 'n'},
500 {"timeout", required_argument, 0, 't'},
501 {"hostname", required_argument, 0, 'H'},
502 {"port", required_argument, 0, 'p'},
503 {"output-format", required_argument, 0, output_format_index},
504 {0, 0, 0, 0}};
480 505
481 if (argc < 2) { 506 if (argc < 2) {
482 usage("\n"); 507 usage("\n");
@@ -489,12 +514,24 @@ check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) {
489 514
490 while (true) { 515 while (true) {
491 int option = 0; 516 int option = 0;
492 int option_char = getopt_long(argc, argv, "Vhv46qw:c:W:C:j:k:m:n:t:H:p:", longopts, &option); 517 int option_char =
518 getopt_long(argc, argv, "Vhv46qw:c:W:C:j:k:m:n:t:H:p:", longopts, &option);
493 if (option_char == -1 || option_char == EOF || option_char == 1) { 519 if (option_char == -1 || option_char == EOF || option_char == 1) {
494 break; 520 break;
495 } 521 }
496 522
497 switch (option_char) { 523 switch (option_char) {
524 case output_format_index: {
525 parsed_output_format parser = mp_parse_output_format(optarg);
526 if (!parser.parsing_success) {
527 printf("Invalid output format: %s\n", optarg);
528 exit(STATE_UNKNOWN);
529 }
530
531 result.config.output_format_is_set = true;
532 result.config.output_format = parser.output_format;
533 break;
534 }
498 case 'h': 535 case 'h':
499 print_help(); 536 print_help();
500 exit(STATE_UNKNOWN); 537 exit(STATE_UNKNOWN);
@@ -509,38 +546,86 @@ check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) {
509 case 'q': 546 case 'q':
510 result.config.quiet = true; 547 result.config.quiet = true;
511 break; 548 break;
512 case 'w': 549 case 'w': {
513 result.config.owarn = optarg; 550 mp_range_parsed tmp = mp_parse_range_string(optarg);
514 break; 551 if (tmp.error != MP_PARSING_SUCCES) {
515 case 'c': 552 die(STATE_UNKNOWN, "failed to parse warning offset threshold");
516 result.config.ocrit = optarg; 553 }
517 break; 554
518 case 'W': 555 result.config.offset_thresholds =
556 mp_thresholds_set_warn(result.config.offset_thresholds, tmp.range);
557 } break;
558 case 'c': {
559 mp_range_parsed tmp = mp_parse_range_string(optarg);
560 if (tmp.error != MP_PARSING_SUCCES) {
561 die(STATE_UNKNOWN, "failed to parse critical offset threshold");
562 }
563
564 result.config.offset_thresholds =
565 mp_thresholds_set_crit(result.config.offset_thresholds, tmp.range);
566 } break;
567 case 'W': {
519 result.config.do_stratum = true; 568 result.config.do_stratum = true;
520 result.config.swarn = optarg; 569 mp_range_parsed tmp = mp_parse_range_string(optarg);
521 break; 570 if (tmp.error != MP_PARSING_SUCCES) {
522 case 'C': 571 die(STATE_UNKNOWN, "failed to parse warning stratum threshold");
572 }
573
574 result.config.stratum_thresholds =
575 mp_thresholds_set_warn(result.config.stratum_thresholds, tmp.range);
576 } break;
577 case 'C': {
523 result.config.do_stratum = true; 578 result.config.do_stratum = true;
524 result.config.scrit = optarg; 579 mp_range_parsed tmp = mp_parse_range_string(optarg);
525 break; 580 if (tmp.error != MP_PARSING_SUCCES) {
526 case 'j': 581 die(STATE_UNKNOWN, "failed to parse critical stratum threshold");
582 }
583
584 result.config.stratum_thresholds =
585 mp_thresholds_set_crit(result.config.stratum_thresholds, tmp.range);
586 } break;
587 case 'j': {
527 result.config.do_jitter = true; 588 result.config.do_jitter = true;
528 result.config.jwarn = optarg; 589 mp_range_parsed tmp = mp_parse_range_string(optarg);
529 break; 590 if (tmp.error != MP_PARSING_SUCCES) {
530 case 'k': 591 die(STATE_UNKNOWN, "failed to parse warning jitter threshold");
592 }
593
594 result.config.jitter_thresholds =
595 mp_thresholds_set_warn(result.config.jitter_thresholds, tmp.range);
596 } break;
597 case 'k': {
531 result.config.do_jitter = true; 598 result.config.do_jitter = true;
532 result.config.jcrit = optarg; 599 mp_range_parsed tmp = mp_parse_range_string(optarg);
533 break; 600 if (tmp.error != MP_PARSING_SUCCES) {
534 case 'm': 601 die(STATE_UNKNOWN, "failed to parse critical jitter threshold");
602 }
603
604 result.config.jitter_thresholds =
605 mp_thresholds_set_crit(result.config.jitter_thresholds, tmp.range);
606 } break;
607 case 'm': {
535 result.config.do_truechimers = true; 608 result.config.do_truechimers = true;
536 result.config.twarn = optarg; 609 mp_range_parsed tmp = mp_parse_range_string(optarg);
537 break; 610 if (tmp.error != MP_PARSING_SUCCES) {
538 case 'n': 611 die(STATE_UNKNOWN, "failed to parse warning truechimer threshold");
612 }
613
614 result.config.truechimer_thresholds =
615 mp_thresholds_set_warn(result.config.truechimer_thresholds, tmp.range);
616 } break;
617 case 'n': {
539 result.config.do_truechimers = true; 618 result.config.do_truechimers = true;
540 result.config.tcrit = optarg; 619 mp_range_parsed tmp = mp_parse_range_string(optarg);
541 break; 620 if (tmp.error != MP_PARSING_SUCCES) {
621 die(STATE_UNKNOWN, "failed to parse critical truechimer threshold");
622 }
623
624 result.config.truechimer_thresholds =
625 mp_thresholds_set_crit(result.config.truechimer_thresholds, tmp.range);
626 } break;
542 case 'H': 627 case 'H':
543 if (!is_host(optarg)) { 628 if (!is_host(optarg) && (optarg[0] != '/')) {
544 usage2(_("Invalid hostname/address"), optarg); 629 usage2(_("Invalid hostname/address"), optarg);
545 } 630 }
546 result.config.server_address = strdup(optarg); 631 result.config.server_address = strdup(optarg);
@@ -572,31 +657,28 @@ check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) {
572 usage4(_("Hostname was not supplied")); 657 usage4(_("Hostname was not supplied"));
573 } 658 }
574 659
575 set_thresholds(&result.config.offset_thresholds, result.config.owarn, result.config.ocrit);
576 set_thresholds(&result.config.jitter_thresholds, result.config.jwarn, result.config.jcrit);
577 set_thresholds(&result.config.stratum_thresholds, result.config.swarn, result.config.scrit);
578 set_thresholds(&result.config.truechimer_thresholds, result.config.twarn, result.config.tcrit);
579
580 return result; 660 return result;
581} 661}
582 662
583char *perfd_offset(double offset, thresholds *offset_thresholds) { 663char *perfd_offset(double offset, thresholds *offset_thresholds) {
584 return fperfdata("offset", offset, "s", true, offset_thresholds->warning->end, true, offset_thresholds->critical->end, false, 0, false, 664 return fperfdata("offset", offset, "s", true, offset_thresholds->warning->end, true,
585 0); 665 offset_thresholds->critical->end, false, 0, false, 0);
586} 666}
587 667
588char *perfd_jitter(double jitter, bool do_jitter, thresholds *jitter_thresholds) { 668char *perfd_jitter(double jitter, bool do_jitter, thresholds *jitter_thresholds) {
589 return fperfdata("jitter", jitter, "", do_jitter, jitter_thresholds->warning->end, do_jitter, jitter_thresholds->critical->end, true, 0, 669 return fperfdata("jitter", jitter, "", do_jitter, jitter_thresholds->warning->end, do_jitter,
590 false, 0); 670 jitter_thresholds->critical->end, true, 0, false, 0);
591} 671}
592 672
593char *perfd_stratum(int stratum, bool do_stratum, thresholds *stratum_thresholds) { 673char *perfd_stratum(int stratum, bool do_stratum, thresholds *stratum_thresholds) {
594 return perfdata("stratum", stratum, "", do_stratum, (int)stratum_thresholds->warning->end, do_stratum, 674 return perfdata("stratum", stratum, "", do_stratum, (int)stratum_thresholds->warning->end,
595 (int)stratum_thresholds->critical->end, true, 0, true, 16); 675 do_stratum, (int)stratum_thresholds->critical->end, true, 0, true, 16);
596} 676}
597 677
598char *perfd_truechimers(int num_truechimers, const bool do_truechimers, thresholds *truechimer_thresholds) { 678char *perfd_truechimers(int num_truechimers, const bool do_truechimers,
599 return perfdata("truechimers", num_truechimers, "", do_truechimers, (int)truechimer_thresholds->warning->end, do_truechimers, 679 thresholds *truechimer_thresholds) {
680 return perfdata("truechimers", num_truechimers, "", do_truechimers,
681 (int)truechimer_thresholds->warning->end, do_truechimers,
600 (int)truechimer_thresholds->critical->end, true, 0, false, 0); 682 (int)truechimer_thresholds->critical->end, true, 0, false, 0);
601} 683}
602 684
@@ -616,6 +698,10 @@ int main(int argc, char *argv[]) {
616 698
617 const check_ntp_peer_config config = tmp_config.config; 699 const check_ntp_peer_config config = tmp_config.config;
618 700
701 if (config.output_format_is_set) {
702 mp_set_format(config.output_format);
703 }
704
619 /* initialize alarm signal handling */ 705 /* initialize alarm signal handling */
620 signal(SIGALRM, socket_timeout_alarm_handler); 706 signal(SIGALRM, socket_timeout_alarm_handler);
621 707
@@ -623,118 +709,113 @@ int main(int argc, char *argv[]) {
623 alarm(socket_timeout); 709 alarm(socket_timeout);
624 710
625 /* This returns either OK or WARNING (See comment preceding ntp_request) */ 711 /* This returns either OK or WARNING (See comment preceding ntp_request) */
626 ntp_request_result ntp_res = ntp_request(config); 712 const ntp_request_result ntp_res = ntp_request(config);
627 mp_state_enum result = STATE_UNKNOWN; 713 mp_check overall = mp_check_init();
628 714
715 mp_subcheck sc_offset = mp_subcheck_init();
716 xasprintf(&sc_offset.output, "offset");
629 if (ntp_res.offset_result == STATE_UNKNOWN) { 717 if (ntp_res.offset_result == STATE_UNKNOWN) {
630 /* if there's no sync peer (this overrides ntp_request output): */ 718 /* if there's no sync peer (this overrides ntp_request output): */
631 result = (config.quiet ? STATE_UNKNOWN : STATE_CRITICAL); 719 sc_offset =
720 mp_set_subcheck_state(sc_offset, (config.quiet ? STATE_UNKNOWN : STATE_CRITICAL));
721 xasprintf(&sc_offset.output, "%s unknown", sc_offset.output);
632 } else { 722 } else {
633 /* Be quiet if there's no candidates either */ 723 /* Be quiet if there's no candidates either */
634 if (config.quiet && result == STATE_WARNING) { 724 mp_state_enum tmp = STATE_OK;
635 result = STATE_UNKNOWN; 725 if (config.quiet && ntp_res.state == STATE_WARNING) {
726 tmp = STATE_UNKNOWN;
636 } 727 }
637 result = max_state_alt(result, get_status(fabs(ntp_res.offset), config.offset_thresholds)); 728
729 xasprintf(&sc_offset.output, "%s: %.6fs", sc_offset.output, ntp_res.offset);
730
731 mp_perfdata pd_offset = perfdata_init();
732 pd_offset.value = mp_create_pd_value(fabs(ntp_res.offset));
733 pd_offset = mp_pd_set_thresholds(pd_offset, config.offset_thresholds);
734 pd_offset.label = "offset";
735 pd_offset.uom = "s";
736 mp_add_perfdata_to_subcheck(&sc_offset, pd_offset);
737
738 tmp = max_state_alt(tmp, mp_get_pd_status(pd_offset));
739 sc_offset = mp_set_subcheck_state(sc_offset, tmp);
638 } 740 }
639 741
640 mp_state_enum oresult = result; 742 mp_add_subcheck_to_check(&overall, sc_offset);
641 mp_state_enum tresult = STATE_UNKNOWN;
642 743
744 // truechimers
643 if (config.do_truechimers) { 745 if (config.do_truechimers) {
644 tresult = get_status(ntp_res.num_truechimers, config.truechimer_thresholds); 746 mp_subcheck sc_truechimers = mp_subcheck_init();
645 result = max_state_alt(result, tresult); 747 xasprintf(&sc_truechimers.output, "truechimers: %i", ntp_res.num_truechimers);
646 }
647 748
648 mp_state_enum sresult = STATE_UNKNOWN; 749 mp_perfdata pd_truechimers = perfdata_init();
750 pd_truechimers.value = mp_create_pd_value(ntp_res.num_truechimers);
751 pd_truechimers.label = "truechimers";
752 pd_truechimers = mp_pd_set_thresholds(pd_truechimers, config.truechimer_thresholds);
649 753
650 if (config.do_stratum) { 754 mp_add_perfdata_to_subcheck(&sc_truechimers, pd_truechimers);
651 sresult = get_status((double)ntp_res.stratum, config.stratum_thresholds);
652 result = max_state_alt(result, sresult);
653 }
654 755
655 mp_state_enum jresult = STATE_UNKNOWN; 756 sc_truechimers = mp_set_subcheck_state(sc_truechimers, mp_get_pd_status(pd_truechimers));
656 757
657 if (config.do_jitter) { 758 mp_add_subcheck_to_check(&overall, sc_truechimers);
658 jresult = get_status(ntp_res.jitter, config.jitter_thresholds);
659 result = max_state_alt(result, jresult);
660 } 759 }
661 760
662 char *result_line; 761 if (config.do_stratum) {
663 switch (result) { 762 mp_subcheck sc_stratum = mp_subcheck_init();
664 case STATE_CRITICAL: 763 xasprintf(&sc_stratum.output, "stratum: %li", ntp_res.stratum);
665 xasprintf(&result_line, _("NTP CRITICAL:"));
666 break;
667 case STATE_WARNING:
668 xasprintf(&result_line, _("NTP WARNING:"));
669 break;
670 case STATE_OK:
671 xasprintf(&result_line, _("NTP OK:"));
672 break;
673 default:
674 xasprintf(&result_line, _("NTP UNKNOWN:"));
675 break;
676 }
677 764
678 if (!syncsource_found) { 765 mp_perfdata pd_stratum = perfdata_init();
679 xasprintf(&result_line, "%s %s,", result_line, _("Server not synchronized")); 766 pd_stratum.value = mp_create_pd_value(ntp_res.stratum);
680 } else if (li_alarm) { 767 pd_stratum = mp_pd_set_thresholds(pd_stratum, config.stratum_thresholds);
681 xasprintf(&result_line, "%s %s,", result_line, _("Server has the LI_ALARM bit set")); 768 pd_stratum.label = "stratum";
682 }
683 769
684 char *perfdata_line; 770 mp_add_perfdata_to_subcheck(&sc_stratum, pd_stratum);
685 if (ntp_res.offset_result == STATE_UNKNOWN) { 771
686 xasprintf(&result_line, "%s %s", result_line, _("Offset unknown")); 772 sc_stratum = mp_set_subcheck_state(sc_stratum, mp_get_pd_status(pd_stratum));
687 xasprintf(&perfdata_line, ""); 773
688 } else if (oresult == STATE_WARNING) { 774 mp_add_subcheck_to_check(&overall, sc_stratum);
689 xasprintf(&result_line, "%s %s %.10g secs (WARNING)", result_line, _("Offset"), ntp_res.offset);
690 } else if (oresult == STATE_CRITICAL) {
691 xasprintf(&result_line, "%s %s %.10g secs (CRITICAL)", result_line, _("Offset"), ntp_res.offset);
692 } else {
693 xasprintf(&result_line, "%s %s %.10g secs", result_line, _("Offset"), ntp_res.offset);
694 } 775 }
695 xasprintf(&perfdata_line, "%s", perfd_offset(ntp_res.offset, config.offset_thresholds));
696 776
697 if (config.do_jitter) { 777 if (config.do_jitter) {
698 if (jresult == STATE_WARNING) { 778 mp_subcheck sc_jitter = mp_subcheck_init();
699 xasprintf(&result_line, "%s, jitter=%f (WARNING)", result_line, ntp_res.jitter); 779 xasprintf(&sc_jitter.output, "jitter: %f", ntp_res.jitter);
700 } else if (jresult == STATE_CRITICAL) {
701 xasprintf(&result_line, "%s, jitter=%f (CRITICAL)", result_line, ntp_res.jitter);
702 } else {
703 xasprintf(&result_line, "%s, jitter=%f", result_line, ntp_res.jitter);
704 }
705 xasprintf(&perfdata_line, "%s %s", perfdata_line, perfd_jitter(ntp_res.jitter, config.do_jitter, config.jitter_thresholds));
706 }
707 780
708 if (config.do_stratum) { 781 mp_perfdata pd_jitter = perfdata_init();
709 if (sresult == STATE_WARNING) { 782 pd_jitter.value = mp_create_pd_value(ntp_res.jitter);
710 xasprintf(&result_line, "%s, stratum=%l (WARNING)", result_line, ntp_res.stratum); 783 pd_jitter = mp_pd_set_thresholds(pd_jitter, config.jitter_thresholds);
711 } else if (sresult == STATE_CRITICAL) { 784 pd_jitter.label = "jitter";
712 xasprintf(&result_line, "%s, stratum=%l (CRITICAL)", result_line, ntp_res.stratum); 785
713 } else { 786 mp_add_perfdata_to_subcheck(&sc_jitter, pd_jitter);
714 xasprintf(&result_line, "%s, stratum=%l", result_line, ntp_res.stratum); 787
715 } 788 sc_jitter = mp_set_subcheck_state(sc_jitter, mp_get_pd_status(pd_jitter));
716 xasprintf(&perfdata_line, "%s %s", perfdata_line, perfd_stratum(ntp_res.stratum, config.do_stratum, config.stratum_thresholds)); 789 mp_add_subcheck_to_check(&overall, sc_jitter);
717 } 790 }
718 791
719 if (config.do_truechimers) { 792 mp_subcheck sc_other_info = mp_subcheck_init();
720 if (tresult == STATE_WARNING) { 793 sc_other_info = mp_set_subcheck_default_state(sc_other_info, STATE_OK);
721 xasprintf(&result_line, "%s, truechimers=%i (WARNING)", result_line, ntp_res.num_truechimers); 794 if (!ntp_res.syncsource_found) {
722 } else if (tresult == STATE_CRITICAL) { 795 xasprintf(&sc_other_info.output, "%s", _("Server not synchronized"));
723 xasprintf(&result_line, "%s, truechimers=%i (CRITICAL)", result_line, ntp_res.num_truechimers); 796 mp_add_subcheck_to_check(&overall, sc_other_info);
724 } else { 797 } else if (ntp_res.li_alarm) {
725 xasprintf(&result_line, "%s, truechimers=%i", result_line, ntp_res.num_truechimers); 798 xasprintf(&sc_other_info.output, "%s", _("Server has the LI_ALARM bit set"));
726 } 799 mp_add_subcheck_to_check(&overall, sc_other_info);
727 xasprintf(&perfdata_line, "%s %s", perfdata_line,
728 perfd_truechimers(ntp_res.num_truechimers, config.do_truechimers, config.truechimer_thresholds));
729 } 800 }
730 801
731 printf("%s|%s\n", result_line, perfdata_line); 802 {
803 mp_subcheck sc_offset = mp_subcheck_init();
804 sc_offset = mp_set_subcheck_default_state(sc_offset, STATE_OK);
805 xasprintf(&sc_offset.output, "offset: %.10gs", ntp_res.offset);
806
807 mp_perfdata pd_offset = perfdata_init();
808 pd_offset.value = mp_create_pd_value(ntp_res.offset);
809 pd_offset = mp_pd_set_thresholds(pd_offset, config.offset_thresholds);
810
811 sc_offset = mp_set_subcheck_state(sc_offset, ntp_res.offset_result);
812 }
732 813
733 if (config.server_address != NULL) { 814 if (config.server_address != NULL) {
734 free(config.server_address); 815 free(config.server_address);
735 } 816 }
736 817
737 exit(result); 818 mp_exit(overall);
738} 819}
739 820
740void print_help(void) { 821void print_help(void) {
@@ -753,7 +834,8 @@ void print_help(void) {
753 printf(UT_IPv46); 834 printf(UT_IPv46);
754 printf(UT_HOST_PORT, 'p', "123"); 835 printf(UT_HOST_PORT, 'p', "123");
755 printf(" %s\n", "-q, --quiet"); 836 printf(" %s\n", "-q, --quiet");
756 printf(" %s\n", _("Returns UNKNOWN instead of CRITICAL or WARNING if server isn't synchronized")); 837 printf(" %s\n",
838 _("Returns UNKNOWN instead of CRITICAL or WARNING if server isn't synchronized"));
757 printf(" %s\n", "-w, --warning=THRESHOLD"); 839 printf(" %s\n", "-w, --warning=THRESHOLD");
758 printf(" %s\n", _("Offset to result in warning status (seconds)")); 840 printf(" %s\n", _("Offset to result in warning status (seconds)"));
759 printf(" %s\n", "-c, --critical=THRESHOLD"); 841 printf(" %s\n", "-c, --critical=THRESHOLD");
@@ -772,6 +854,7 @@ void print_help(void) {
772 printf(" %s\n", _("Critical threshold for number of usable time sources (\"truechimers\")")); 854 printf(" %s\n", _("Critical threshold for number of usable time sources (\"truechimers\")"));
773 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 855 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
774 printf(UT_VERBOSE); 856 printf(UT_VERBOSE);
857 printf(UT_OUTPUT_FORMAT);
775 858
776 printf("\n"); 859 printf("\n");
777 printf("%s\n", _("This plugin checks an NTP server independent of any commandline")); 860 printf("%s\n", _("This plugin checks an NTP server independent of any commandline"));
@@ -790,7 +873,8 @@ void print_help(void) {
790 printf(" %s\n", _("Simple NTP server check:")); 873 printf(" %s\n", _("Simple NTP server check:"));
791 printf(" %s\n", ("./check_ntp_peer -H ntpserv -w 0.5 -c 1")); 874 printf(" %s\n", ("./check_ntp_peer -H ntpserv -w 0.5 -c 1"));
792 printf("\n"); 875 printf("\n");
793 printf(" %s\n", _("Check jitter too, avoiding critical notifications if jitter isn't available")); 876 printf(" %s\n",
877 _("Check jitter too, avoiding critical notifications if jitter isn't available"));
794 printf(" %s\n", _("(See Notes above for more details on thresholds formats):")); 878 printf(" %s\n", _("(See Notes above for more details on thresholds formats):"));
795 printf(" %s\n", ("./check_ntp_peer -H ntpserv -w 0.5 -c 1 -j -1:100 -k -1:200")); 879 printf(" %s\n", ("./check_ntp_peer -H ntpserv -w 0.5 -c 1 -j -1:100 -k -1:200"));
796 printf("\n"); 880 printf("\n");