diff options
| author | M. Sean Finney <seanius@users.sourceforge.net> | 2006-04-11 23:24:07 +0000 |
|---|---|---|
| committer | M. Sean Finney <seanius@users.sourceforge.net> | 2006-04-11 23:24:07 +0000 |
| commit | 077a1f6a5149a297f60675516f21be9630eaf25e (patch) | |
| tree | b76f51c81bae3b12b8d7083ed16a48ee0572fbd7 | |
| parent | 2e7a39ae55f93eaddc917d47de6dc8bee51ee401 (diff) | |
| download | monitoring-plugins-077a1f6a5149a297f60675516f21be9630eaf25e.tar.gz | |
another big code-commit to check_ntp. jitter calculations now work, and
the program is becoming much closer on the packet-for-packet level to
how check_ntp.pl behaves. i'll send an email in the morning :)
git-svn-id: https://nagiosplug.svn.sourceforge.net/svnroot/nagiosplug/nagiosplug/trunk@1371 f882894a-f735-0410-b71e-b25c423dba1c
| -rw-r--r-- | plugins/check_ntp.c | 269 |
1 files changed, 247 insertions, 22 deletions
diff --git a/plugins/check_ntp.c b/plugins/check_ntp.c index 5037786a..2954aa88 100644 --- a/plugins/check_ntp.c +++ b/plugins/check_ntp.c | |||
| @@ -43,6 +43,12 @@ int process_arguments (int, char **); | |||
| 43 | void print_help (void); | 43 | void print_help (void); |
| 44 | void print_usage (void); | 44 | void print_usage (void); |
| 45 | 45 | ||
| 46 | /* number of times to perform each request to get a good average. */ | ||
| 47 | #define AVG_NUM 4 | ||
| 48 | |||
| 49 | /* max size of control message data */ | ||
| 50 | #define MAX_CM_SIZE 468 | ||
| 51 | |||
| 46 | /* this structure holds everything in an ntp request/response as per rfc1305 */ | 52 | /* this structure holds everything in an ntp request/response as per rfc1305 */ |
| 47 | typedef struct { | 53 | typedef struct { |
| 48 | uint8_t flags; /* byte with leapindicator,vers,mode. see macros */ | 54 | uint8_t flags; /* byte with leapindicator,vers,mode. see macros */ |
| @@ -58,6 +64,25 @@ typedef struct { | |||
| 58 | uint64_t txts; /* time at which request departed server */ | 64 | uint64_t txts; /* time at which request departed server */ |
| 59 | } ntp_message; | 65 | } ntp_message; |
| 60 | 66 | ||
| 67 | /* this structure holds everything in an ntp control message as per rfc1305 */ | ||
| 68 | typedef struct { | ||
| 69 | uint8_t flags; /* byte with leapindicator,vers,mode. see macros */ | ||
| 70 | uint8_t op; /* R,E,M bits and Opcode */ | ||
| 71 | uint16_t seq; /* Packet sequence */ | ||
| 72 | uint16_t status; /* Clock status */ | ||
| 73 | uint16_t assoc; /* Association */ | ||
| 74 | uint16_t offset; /* Similar to TCP sequence # */ | ||
| 75 | uint16_t count; /* # bytes of data */ | ||
| 76 | char data[MAX_CM_SIZE]; /* ASCII data of the request */ | ||
| 77 | /* NB: not necessarily NULL terminated! */ | ||
| 78 | } ntp_control_message; | ||
| 79 | |||
| 80 | /* this is an association/status-word pair found in control packet reponses */ | ||
| 81 | typedef struct { | ||
| 82 | uint16_t assoc; | ||
| 83 | uint16_t status; | ||
| 84 | } ntp_assoc_status_pair; | ||
| 85 | |||
| 61 | /* bits 1,2 are the leap indicator */ | 86 | /* bits 1,2 are the leap indicator */ |
| 62 | #define LI_MASK 0xc0 | 87 | #define LI_MASK 0xc0 |
| 63 | #define LI(x) ((x&LI_MASK)>>6) | 88 | #define LI(x) ((x&LI_MASK)>>6) |
| @@ -71,12 +96,28 @@ typedef struct { | |||
| 71 | #define VN_MASK 0x38 | 96 | #define VN_MASK 0x38 |
| 72 | #define VN(x) ((x&VN_MASK)>>3) | 97 | #define VN(x) ((x&VN_MASK)>>3) |
| 73 | #define VN_SET(x,y) do{ x |= ((y<<3)&VN_MASK); }while(0) | 98 | #define VN_SET(x,y) do{ x |= ((y<<3)&VN_MASK); }while(0) |
| 99 | #define VN_RESERVED 0x02 | ||
| 74 | /* bits 6,7,8 are the ntp mode */ | 100 | /* bits 6,7,8 are the ntp mode */ |
| 75 | #define MODE_MASK 0x07 | 101 | #define MODE_MASK 0x07 |
| 76 | #define MODE(x) (x&MODE_MASK) | 102 | #define MODE(x) (x&MODE_MASK) |
| 77 | #define MODE_SET(x,y) do{ x |= (y&MODE_MASK); }while(0) | 103 | #define MODE_SET(x,y) do{ x |= (y&MODE_MASK); }while(0) |
| 78 | /* here are some values */ | 104 | /* here are some values */ |
| 79 | #define MODE_CLIENT 0x03 | 105 | #define MODE_CLIENT 0x03 |
| 106 | #define MODE_CONTROLMSG 0x06 | ||
| 107 | /* In control message, bits 8-10 are R,E,M bits */ | ||
| 108 | #define REM_MASK 0xe0 | ||
| 109 | #define REM_RESP 0x80 | ||
| 110 | #define REM_ERROR 0x40 | ||
| 111 | #define REM_MORE 0x20 | ||
| 112 | /* In control message, bits 11 - 15 are opcode */ | ||
| 113 | #define OP_MASK 0x1f | ||
| 114 | #define OP_SET(x,y) do{ x |= (y&OP_MASK); }while(0) | ||
| 115 | #define OP_READSTAT 0x01 | ||
| 116 | #define OP_READVAR 0x02 | ||
| 117 | /* In peer status bytes, bytes 6,7,8 determine clock selection status */ | ||
| 118 | #define PEER_SEL(x) (x&0x07) | ||
| 119 | #define PEER_INCLUDED 0x04 | ||
| 120 | #define PEER_SYNCSOURCE 0x06 | ||
| 80 | 121 | ||
| 81 | /** | 122 | /** |
| 82 | ** a note about the 32-bit "fixed point" numbers: | 123 | ** a note about the 32-bit "fixed point" numbers: |
| @@ -116,7 +157,7 @@ typedef struct { | |||
| 116 | do{ if(!n) t.tv_sec = t.tv_usec = 0; \ | 157 | do{ if(!n) t.tv_sec = t.tv_usec = 0; \ |
| 117 | else { \ | 158 | else { \ |
| 118 | t.tv_sec=ntohl(L32(n))-EPOCHDIFF; \ | 159 | t.tv_sec=ntohl(L32(n))-EPOCHDIFF; \ |
| 119 | t.tv_usec=(int)(0.5+(double)(ntohl(R32(n))/4294.967296)); \ | 160 | t.tv_usec=(int)(0.5+(double)(ntohl(R32(n))/4294.967296)); \ |
| 120 | } \ | 161 | } \ |
| 121 | }while(0) | 162 | }while(0) |
| 122 | 163 | ||
| @@ -129,6 +170,17 @@ typedef struct { | |||
| 129 | } \ | 170 | } \ |
| 130 | } while(0) | 171 | } while(0) |
| 131 | 172 | ||
| 173 | /* NTP control message header is 12 bytes, plus any data in the data | ||
| 174 | * field, plus null padding to the nearest 32-bit boundary per rfc. | ||
| 175 | */ | ||
| 176 | #define SIZEOF_NTPCM(m) (12+ntohs(m.count)+((m.count)?4-(ntohs(m.count)%4):0)) | ||
| 177 | |||
| 178 | /* finally, a little helper or two for debugging: */ | ||
| 179 | #define DBG(x) do{if(verbose>1){ x; }}while(0); | ||
| 180 | #define PRINTSOCKADDR(x) \ | ||
| 181 | do{ \ | ||
| 182 | printf("%u.%u.%u.%u", (x>>24)&0xff, (x>>16)&0xff, (x>>8)&0xff, x&0xff);\ | ||
| 183 | }while(0); | ||
| 132 | 184 | ||
| 133 | /* calculate the offset of the local clock */ | 185 | /* calculate the offset of the local clock */ |
| 134 | static inline double calc_offset(const ntp_message *m, const struct timeval *t){ | 186 | static inline double calc_offset(const ntp_message *m, const struct timeval *t){ |
| @@ -142,7 +194,7 @@ static inline double calc_offset(const ntp_message *m, const struct timeval *t){ | |||
| 142 | } | 194 | } |
| 143 | 195 | ||
| 144 | /* print out a ntp packet in human readable/debuggable format */ | 196 | /* print out a ntp packet in human readable/debuggable format */ |
| 145 | void print_packet(const ntp_message *p){ | 197 | void print_ntp_message(const ntp_message *p){ |
| 146 | struct timeval ref, orig, rx, tx; | 198 | struct timeval ref, orig, rx, tx; |
| 147 | 199 | ||
| 148 | NTP64toTV(p->refts,ref); | 200 | NTP64toTV(p->refts,ref); |
| @@ -167,6 +219,42 @@ void print_packet(const ntp_message *p){ | |||
| 167 | printf("\ttxts = %-.16g\n", NTP64asDOUBLE(p->txts)); | 219 | printf("\ttxts = %-.16g\n", NTP64asDOUBLE(p->txts)); |
| 168 | } | 220 | } |
| 169 | 221 | ||
| 222 | void print_ntp_control_message(const ntp_control_message *p){ | ||
| 223 | int i=0, numpeers=0; | ||
| 224 | const ntp_assoc_status_pair *peer=NULL; | ||
| 225 | |||
| 226 | printf("control packet contents:\n"); | ||
| 227 | printf("\tflags: 0x%.2x , 0x%.2x\n", p->flags, p->op); | ||
| 228 | printf("\t li=%d (0x%.2x)\n", LI(p->flags), p->flags&LI_MASK); | ||
| 229 | printf("\t vn=%d (0x%.2x)\n", VN(p->flags), p->flags&VN_MASK); | ||
| 230 | printf("\t mode=%d (0x%.2x)\n", MODE(p->flags), p->flags&MODE_MASK); | ||
| 231 | printf("\t response=%d (0x%.2x)\n", (p->op&REM_RESP)>0, p->op&REM_RESP); | ||
| 232 | printf("\t more=%d (0x%.2x)\n", (p->op&REM_MORE)>0, p->op&REM_MORE); | ||
| 233 | printf("\t error=%d (0x%.2x)\n", (p->op&REM_ERROR)>0, p->op&REM_ERROR); | ||
| 234 | printf("\t op=%d (0x%.2x)\n", p->op&OP_MASK, p->op&OP_MASK); | ||
| 235 | printf("\tsequence: %d (0x%.2x)\n", ntohs(p->seq), ntohs(p->seq)); | ||
| 236 | printf("\tstatus: %d (0x%.2x)\n", ntohs(p->status), ntohs(p->status)); | ||
| 237 | printf("\tassoc: %d (0x%.2x)\n", ntohs(p->assoc), ntohs(p->assoc)); | ||
| 238 | printf("\toffset: %d (0x%.2x)\n", ntohs(p->offset), ntohs(p->offset)); | ||
| 239 | printf("\tcount: %d (0x%.2x)\n", ntohs(p->count), ntohs(p->count)); | ||
| 240 | numpeers=ntohs(p->count)/(sizeof(ntp_assoc_status_pair)); | ||
| 241 | if(p->op&REM_RESP && p->op&OP_READSTAT){ | ||
| 242 | peer=(ntp_assoc_status_pair*)p->data; | ||
| 243 | for(i=0;i<numpeers;i++){ | ||
| 244 | printf("\tpeer id %.2x status %.2x", | ||
| 245 | ntohs(peer[i].assoc), ntohs(peer[i].status)); | ||
| 246 | if (PEER_SEL(peer[i].status) >= PEER_INCLUDED){ | ||
| 247 | if(PEER_SEL(peer[i].status) >= PEER_SYNCSOURCE){ | ||
| 248 | printf(" <-- current sync source"); | ||
| 249 | } else { | ||
| 250 | printf(" <-- current sync candidate"); | ||
| 251 | } | ||
| 252 | } | ||
| 253 | printf("\n"); | ||
| 254 | } | ||
| 255 | } | ||
| 256 | } | ||
| 257 | |||
| 170 | void setup_request(ntp_message *p){ | 258 | void setup_request(ntp_message *p){ |
| 171 | struct timeval t; | 259 | struct timeval t; |
| 172 | 260 | ||
| @@ -189,7 +277,8 @@ double offset_request(const char *host){ | |||
| 189 | double next_offset=0., avg_offset=0.; | 277 | double next_offset=0., avg_offset=0.; |
| 190 | struct timeval recv_time; | 278 | struct timeval recv_time; |
| 191 | 279 | ||
| 192 | for(i=0; i<4; i++){ | 280 | for(i=0; i<AVG_NUM; i++){ |
| 281 | if(verbose) printf("offset run: %d/%d\n", i+1, AVG_NUM); | ||
| 193 | setup_request(&req); | 282 | setup_request(&req); |
| 194 | my_udp_connect(server_address, 123, &conn); | 283 | my_udp_connect(server_address, 123, &conn); |
| 195 | write(conn, &req, sizeof(ntp_message)); | 284 | write(conn, &req, sizeof(ntp_message)); |
| @@ -201,12 +290,134 @@ double offset_request(const char *host){ | |||
| 201 | if(verbose) printf("offset: %g\n", next_offset); | 290 | if(verbose) printf("offset: %g\n", next_offset); |
| 202 | avg_offset+=next_offset; | 291 | avg_offset+=next_offset; |
| 203 | } | 292 | } |
| 204 | return avg_offset/4.; | 293 | avg_offset/=AVG_NUM; |
| 294 | if(verbose) printf("average offset: %g\n", avg_offset); | ||
| 295 | return avg_offset; | ||
| 296 | } | ||
| 297 | |||
| 298 | void | ||
| 299 | setup_control_request(ntp_control_message *p, uint8_t opcode, uint16_t seq){ | ||
| 300 | memset(p, 0, sizeof(ntp_control_message)); | ||
| 301 | LI_SET(p->flags, LI_NOWARNING); | ||
| 302 | VN_SET(p->flags, VN_RESERVED); | ||
| 303 | MODE_SET(p->flags, MODE_CONTROLMSG); | ||
| 304 | OP_SET(p->op, opcode); | ||
| 305 | p->seq = htons(seq); | ||
| 306 | /* Remaining fields are zero for requests */ | ||
| 205 | } | 307 | } |
| 206 | 308 | ||
| 207 | /* not yet implemented yet */ | 309 | /* XXX handle responses with the error bit set */ |
| 208 | double jitter_request(const char *host){ | 310 | double jitter_request(const char *host){ |
| 209 | return 0.; | 311 | int conn=-1, i, npeers=0, num_candidates=0, syncsource_found=0; |
| 312 | int run=0, min_peer_sel=PEER_INCLUDED, num_selected=0, num_valid=0; | ||
| 313 | ntp_assoc_status_pair *peers; | ||
| 314 | ntp_control_message req; | ||
| 315 | double rval = 0.0, jitter = -1.0; | ||
| 316 | char *startofvalue=NULL, *nptr=NULL; | ||
| 317 | |||
| 318 | /* Long-winded explanation: | ||
| 319 | * Getting the jitter requires a number of steps: | ||
| 320 | * 1) Send a READSTAT request. | ||
| 321 | * 2) Interpret the READSTAT reply | ||
| 322 | * a) The data section contains a list of peer identifiers (16 bits) | ||
| 323 | * and associated status words (16 bits) | ||
| 324 | * b) We want the value of 0x06 in the SEL (peer selection) value, | ||
| 325 | * which means "current synchronizatin source". If that's missing, | ||
| 326 | * we take anything better than 0x04 (see the rfc for details) but | ||
| 327 | * set a minimum of warning. | ||
| 328 | * 3) Send a READVAR request for information on each peer identified | ||
| 329 | * in 2b greater than the minimum selection value. | ||
| 330 | * 4) Extract the jitter value from the data[] (it's ASCII) | ||
| 331 | */ | ||
| 332 | my_udp_connect(server_address, 123, &conn); | ||
| 333 | setup_control_request(&req, OP_READSTAT, 1); | ||
| 334 | |||
| 335 | DBG(printf("sending READSTAT request")); | ||
| 336 | write(conn, &req, SIZEOF_NTPCM(req)); | ||
| 337 | DBG(print_ntp_control_message(&req)); | ||
| 338 | /* Attempt to read the largest size packet possible | ||
| 339 | * Is it possible for an NTP server to have more than 117 synchronization | ||
| 340 | * sources? If so, we will receive a second datagram with additional | ||
| 341 | * peers listed, since 117 is the maximum number that can fit in a | ||
| 342 | * single NTP control datagram. This code doesn't handle that case */ | ||
| 343 | /* XXX check the REM_MORE bit */ | ||
| 344 | req.count=htons(MAX_CM_SIZE); | ||
| 345 | DBG(printf("recieving READSTAT response")) | ||
| 346 | read(conn, &req, SIZEOF_NTPCM(req)); | ||
| 347 | DBG(print_ntp_control_message(&req)); | ||
| 348 | /* Each peer identifier is 4 bytes in the data section, which | ||
| 349 | * we represent as a ntp_assoc_status_pair datatype. | ||
| 350 | */ | ||
| 351 | npeers=ntohs(req.count)/sizeof(ntp_assoc_status_pair); | ||
| 352 | peers=(ntp_assoc_status_pair*)malloc(sizeof(ntp_assoc_status_pair)*npeers); | ||
| 353 | memcpy((void*)peers, (void*)req.data, sizeof(ntp_assoc_status_pair)*npeers); | ||
| 354 | /* first, let's find out if we have a sync source, or if there are | ||
| 355 | * at least some candidates. in the case of the latter we'll issue | ||
| 356 | * a warning but go ahead with the check on them. */ | ||
| 357 | for (i = 0; i < npeers; i++){ | ||
| 358 | if (PEER_SEL(peers[i].status) >= PEER_INCLUDED){ | ||
| 359 | num_candidates++; | ||
| 360 | if(PEER_SEL(peers[i].status) >= PEER_SYNCSOURCE){ | ||
| 361 | syncsource_found=1; | ||
| 362 | min_peer_sel=PEER_SYNCSOURCE; | ||
| 363 | } | ||
| 364 | } | ||
| 365 | } | ||
| 366 | if(verbose) printf("%d candiate peers available\n", num_candidates); | ||
| 367 | if(verbose && syncsource_found) printf("synchronization source found\n"); | ||
| 368 | /* XXX if ! syncsource_found set status to warning */ | ||
| 369 | |||
| 370 | for (run=0; run<AVG_NUM; run++){ | ||
| 371 | if(verbose) printf("jitter run %d of %d\n", run+1, AVG_NUM); | ||
| 372 | for (i = 0; i < npeers; i++){ | ||
| 373 | /* Only query this server if it is the current sync source */ | ||
| 374 | if (PEER_SEL(peers[i].status) >= min_peer_sel){ | ||
| 375 | setup_control_request(&req, OP_READVAR, 2); | ||
| 376 | req.assoc = peers[i].assoc; | ||
| 377 | /* By spec, putting the variable name "jitter" in the request | ||
| 378 | * should cause the server to provide _only_ the jitter value. | ||
| 379 | * thus reducing net traffic, guaranteeing us only a single | ||
| 380 | * datagram in reply, and making intepretation much simpler | ||
| 381 | */ | ||
| 382 | strncpy(req.data, "jitter", 6); | ||
| 383 | req.count = htons(6); | ||
| 384 | DBG(printf("sending READVAR request...\n")); | ||
| 385 | write(conn, &req, SIZEOF_NTPCM(req)); | ||
| 386 | DBG(print_ntp_control_message(&req)); | ||
| 387 | |||
| 388 | req.count = htons(MAX_CM_SIZE); | ||
| 389 | DBG(printf("recieving READVAR response...\n")); | ||
| 390 | read(conn, &req, SIZEOF_NTPCM(req)); | ||
| 391 | DBG(print_ntp_control_message(&req)); | ||
| 392 | |||
| 393 | /* get to the float value */ | ||
| 394 | if(verbose) { | ||
| 395 | printf("parsing jitter from peer %.2x: ", peers[i].assoc); | ||
| 396 | } | ||
| 397 | startofvalue = strchr(req.data, '=') + 1; | ||
| 398 | jitter = strtod(startofvalue, &nptr); | ||
| 399 | num_selected++; | ||
| 400 | if(jitter == 0 && startofvalue==nptr){ | ||
| 401 | printf("warning: unable to parse server response.\n"); | ||
| 402 | /* XXX errors value ... */ | ||
| 403 | } else { | ||
| 404 | if(verbose) printf("%g\n", jitter); | ||
| 405 | num_valid++; | ||
| 406 | rval += jitter; | ||
| 407 | } | ||
| 408 | } | ||
| 409 | } | ||
| 410 | if(verbose){ | ||
| 411 | printf("jitter parsed from %d/%d peers\n", num_selected, num_valid); | ||
| 412 | } | ||
| 413 | } | ||
| 414 | |||
| 415 | rval /= num_valid; | ||
| 416 | |||
| 417 | close(conn); | ||
| 418 | free(peers); | ||
| 419 | /* If we return -1.0, it means no synchronization source was found */ | ||
| 420 | return rval; | ||
| 210 | } | 421 | } |
| 211 | 422 | ||
| 212 | int process_arguments(int argc, char **argv){ | 423 | int process_arguments(int argc, char **argv){ |
| @@ -247,7 +458,7 @@ int process_arguments(int argc, char **argv){ | |||
| 247 | exit(STATE_OK); | 458 | exit(STATE_OK); |
| 248 | break; | 459 | break; |
| 249 | case 'v': | 460 | case 'v': |
| 250 | verbose = 1; | 461 | verbose++; |
| 251 | break; | 462 | break; |
| 252 | case 'w': | 463 | case 'w': |
| 253 | owarn = atof(optarg); | 464 | owarn = atof(optarg); |
| @@ -321,35 +532,49 @@ int main(int argc, char *argv[]){ | |||
| 321 | 532 | ||
| 322 | offset = offset_request(server_address); | 533 | offset = offset_request(server_address); |
| 323 | if(offset > ocrit){ | 534 | if(offset > ocrit){ |
| 324 | printf("NTP CRITICAL: "); | ||
| 325 | result = STATE_CRITICAL; | 535 | result = STATE_CRITICAL; |
| 326 | } else if(offset > owarn) { | 536 | } else if(offset > owarn) { |
| 327 | printf("NTP WARNING: "); | ||
| 328 | result = STATE_WARNING; | 537 | result = STATE_WARNING; |
| 329 | } else { | 538 | } else { |
| 330 | printf("NTP OK: "); | ||
| 331 | result = STATE_OK; | 539 | result = STATE_OK; |
| 332 | } | 540 | } |
| 333 | 541 | ||
| 334 | /* not implemented yet: */ | 542 | /* If not told to check the jitter, we don't even send packets. |
| 335 | jitter=jitter_request(server_address); | 543 | * jitter is checked using NTP control packets, which not all |
| 336 | 544 | * servers recognize. Trying to check the jitter on OpenNTPD | |
| 337 | /* not implemented yet: | 545 | * (for example) will result in an error |
| 546 | */ | ||
| 338 | if(do_jitter){ | 547 | if(do_jitter){ |
| 548 | jitter=jitter_request(server_address); | ||
| 339 | if(jitter > jcrit){ | 549 | if(jitter > jcrit){ |
| 340 | printf("NTP CRITICAL: "); | 550 | result = max_state(result, STATE_CRITICAL); |
| 341 | result = STATE_CRITICAL; | ||
| 342 | } else if(jitter > jwarn) { | 551 | } else if(jitter > jwarn) { |
| 552 | result = max_state(result, STATE_WARNING); | ||
| 553 | } else if(jitter == -1.0 && result == STATE_OK){ | ||
| 554 | /* -1 indicates that we couldn't calculate the jitter | ||
| 555 | * Only overrides STATE_OK from the offset */ | ||
| 556 | result = STATE_UNKNOWN; | ||
| 557 | } | ||
| 558 | } | ||
| 559 | |||
| 560 | switch (result) { | ||
| 561 | case STATE_CRITICAL : | ||
| 562 | printf("NTP CRITICAL: "); | ||
| 563 | break; | ||
| 564 | case STATE_WARNING : | ||
| 343 | printf("NTP WARNING: "); | 565 | printf("NTP WARNING: "); |
| 344 | result = STATE_WARNING; | 566 | break; |
| 345 | } else { | 567 | case STATE_OK : |
| 346 | printf("NTP OK: "); | 568 | printf("NTP OK: "); |
| 347 | result = STATE_OK; | 569 | break; |
| 348 | } | 570 | default : |
| 571 | printf("NTP UNKNOWN: "); | ||
| 572 | break; | ||
| 349 | } | 573 | } |
| 350 | */ | ||
| 351 | 574 | ||
| 352 | printf("Offset %g secs|offset=%g\n", offset, offset); | 575 | printf("Offset %g secs|offset=%g", offset, offset); |
| 576 | if (do_jitter) printf("|jitter=%f", jitter); | ||
| 577 | printf("\n"); | ||
| 353 | 578 | ||
| 354 | if(server_address!=NULL) free(server_address); | 579 | if(server_address!=NULL) free(server_address); |
| 355 | return result; | 580 | return result; |
