summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--NEWS13
-rw-r--r--plugins/Makefile.am8
-rw-r--r--plugins/check_ntp_peer.c628
-rw-r--r--plugins/check_ntp_time.c323
-rw-r--r--plugins/t/check_ntp.t92
5 files changed, 342 insertions, 722 deletions
diff --git a/NEWS b/NEWS
index 0c26c82..f2de153 100644
--- a/NEWS
+++ b/NEWS
@@ -6,18 +6,21 @@ This file documents the major additions and syntax changes between releases.
6 check_ntp now return UNKNOWN instead of WARNING if jitter is unavailable (jitter=-1.000000) 6 check_ntp now return UNKNOWN instead of WARNING if jitter is unavailable (jitter=-1.000000)
7 as long as the thresholds range inculde -1. If no offset threshold is specified 7 as long as the thresholds range inculde -1. If no offset threshold is specified
8 and the offset is unavailable, will return UNKNOWN as well. 8 and the offset is unavailable, will return UNKNOWN as well.
9 NOTE: If jitter thresholds are specified integers it will return CRITICAL if jitter 9 NOTE: If jitter thresholds are specified as integers it will return CRITICAL if jitter
10 is "-1" as the default range starts at 0. See Examples in --help output. 10 is "-1" as the default range starts at 0. See Examples in --help output.
11 Fix broken usage2 in check_snmp and check_cluster 11 Fix broken usage2 in check_snmp and check_cluster
12 check_cluster now accept all valid characters in its thresholds ("-", "@", "~") 12 check_cluster now accept all valid characters in its thresholds ("-", "@", "~")
13 Merge two new checks that deprecates check_ntp: check_ntp_peer and check_ntp_time.
14 You should read the --help output so see which one is suitable for you. check_ntp_peer
15 implement stratum thresholds support (feature request #1703823).
13 16
141.4.10 28th September 2007 171.4.10 28th September 2007
15 Fix check_http buffer overflow vulnerability when following HTTP redirects 18 Fix check_http buffer overflow vulnerability when following HTTP redirects
16 check_http now explicitly asks HTTP/1.1 servers to close the connection 19 check_http now explicitly asks HTTP/1.1 servers to close the connection
17 after completion of the response 20 after completion of the response
18 Check_ldaps' guessing which secure method to use (starttls vs. ssl on connect) 21 Check_ldaps' guessing which secure method to use (starttls vs. ssl on connect)
19 is now deprecated. See --help for further information. 22 is now deprecated. See --help for further information.
20 Check_disk now calls stat() on all filesystems to check. (Old: only the ones selected using -p) 23 Check_disk now calls stat() on all filesystems to check. (Old: only the ones selected using -p)
21 A meaningful error message (eg "Stale NFS Handle") is printed if stat fails. 24 A meaningful error message (eg "Stale NFS Handle") is printed if stat fails.
22 New check_disk option -L: Only check local filesystems, but call stat() on remote ones, too. 25 New check_disk option -L: Only check local filesystems, but call stat() on remote ones, too.
23 Thus accessibility of remote filesystems can be checked without any threshold comparison. 26 Thus accessibility of remote filesystems can be checked without any threshold comparison.
@@ -36,7 +39,7 @@ This file documents the major additions and syntax changes between releases.
36 Fix possible check_icmp bus errors on some (non-x86/AMD64) platforms 39 Fix possible check_icmp bus errors on some (non-x86/AMD64) platforms
37 Fix check_smtp's handling of multiple-packet server responses 40 Fix check_smtp's handling of multiple-packet server responses
38 WARNING: Fix for negate which may break existing commands: 41 WARNING: Fix for negate which may break existing commands:
39 - stop evaluating command line options through shell twice 42 - stop evaluating command line options through shell twice
40 - enforce a full path for the command to run 43 - enforce a full path for the command to run
41 The "negate" utility can now remap custom states 44 The "negate" utility can now remap custom states
42 Check_radius now supports radiusclient-ng 45 Check_radius now supports radiusclient-ng
@@ -111,7 +114,7 @@ This file documents the major additions and syntax changes between releases.
111 SSL plugins work with gnutls as well as OpenSSL 114 SSL plugins work with gnutls as well as OpenSSL
112 check_mysql_query added to run arbitrary SQL commands, with threshold checking 115 check_mysql_query added to run arbitrary SQL commands, with threshold checking
113 libtool now required for development systems 116 libtool now required for development systems
114 Notice: check_udp (compiled from check_udp.c) will be deprecated in the next release. 117 Notice: check_udp (compiled from check_udp.c) will be deprecated in the next release.
115 check_udp2 should be used instead and will be renamed to check_udp 118 check_udp2 should be used instead and will be renamed to check_udp
116 119
1171.4.2 1201.4.2
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index c0486bc..eafcc5c 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -14,8 +14,8 @@ MATHLIBS = @MATHLIBS@
14#AM_CFLAGS = -Wall 14#AM_CFLAGS = -Wall
15 15
16libexec_PROGRAMS = check_apt check_cluster check_disk check_dummy check_http check_load \ 16libexec_PROGRAMS = check_apt check_cluster check_disk check_dummy check_http check_load \
17 check_mrtg check_mrtgtraf check_ntp check_nwstat check_overcr check_ping \ 17 check_mrtg check_mrtgtraf check_ntp check_ntp_peer check_nwstat check_overcr check_ping \
18 check_real check_smtp check_ssh check_tcp check_time \ 18 check_real check_smtp check_ssh check_tcp check_time check_ntp_time \
19 check_ups check_users negate \ 19 check_ups check_users negate \
20 urlize @EXTRAS@ 20 urlize @EXTRAS@
21 21
@@ -70,6 +70,7 @@ check_mysql_query_LDADD = $(NETLIBS) $(MYSQLLIBS)
70check_nagios_LDADD = $(BASEOBJS) runcmd.o 70check_nagios_LDADD = $(BASEOBJS) runcmd.o
71check_nt_LDADD = $(NETLIBS) 71check_nt_LDADD = $(NETLIBS)
72check_ntp_LDADD = $(NETLIBS) $(MATHLIBS) 72check_ntp_LDADD = $(NETLIBS) $(MATHLIBS)
73check_ntp_peer_LDADD = $(NETLIBS) $(MATHLIBS)
73check_nwstat_LDADD = $(NETLIBS) 74check_nwstat_LDADD = $(NETLIBS)
74check_overcr_LDADD = $(NETLIBS) 75check_overcr_LDADD = $(NETLIBS)
75check_pgsql_LDADD = $(NETLIBS) $(PGLIBS) 76check_pgsql_LDADD = $(NETLIBS) $(PGLIBS)
@@ -83,6 +84,7 @@ check_ssh_LDADD = $(NETLIBS)
83check_swap_LDADD = $(MATHLIBS) $(BASEOBJS) popen.o 84check_swap_LDADD = $(MATHLIBS) $(BASEOBJS) popen.o
84check_tcp_LDADD = $(SSLOBJS) $(NETLIBS) $(SSLLIBS) 85check_tcp_LDADD = $(SSLOBJS) $(NETLIBS) $(SSLLIBS)
85check_time_LDADD = $(NETLIBS) 86check_time_LDADD = $(NETLIBS)
87check_ntp_time_LDADD = $(NETLIBS) $(MATHLIBS)
86check_ups_LDADD = $(NETLIBS) 88check_ups_LDADD = $(NETLIBS)
87check_users_LDADD = $(BASEOBJS) popen.o 89check_users_LDADD = $(BASEOBJS) popen.o
88check_by_ssh_LDADD = $(NETLIBS) runcmd.o 90check_by_ssh_LDADD = $(NETLIBS) runcmd.o
@@ -110,6 +112,7 @@ check_mysql_query_DEPENDENCIES = check_mysql_query.c $(NETOBJS) $(DEPLIBS)
110check_nagios_DEPENDENCIES = check_nagios.c $(BASEOBJS) runcmd.o $(DEPLIBS) 112check_nagios_DEPENDENCIES = check_nagios.c $(BASEOBJS) runcmd.o $(DEPLIBS)
111check_nt_DEPENDENCIES = check_nt.c $(NETOBJS) $(DEPLIBS) 113check_nt_DEPENDENCIES = check_nt.c $(NETOBJS) $(DEPLIBS)
112check_ntp_DEPENDENCIES = check_ntp.c $(NETOBJS) $(DEPLIBS) 114check_ntp_DEPENDENCIES = check_ntp.c $(NETOBJS) $(DEPLIBS)
115check_ntp_peer_DEPENDENCIES = check_ntp_peer.c $(NETOBJS) $(DEPLIBS)
113check_nwstat_DEPENDENCIES = check_nwstat.c $(NETOBJS) $(DEPLIBS) 116check_nwstat_DEPENDENCIES = check_nwstat.c $(NETOBJS) $(DEPLIBS)
114check_overcr_DEPENDENCIES = check_overcr.c $(NETOBJS) $(DEPLIBS) 117check_overcr_DEPENDENCIES = check_overcr.c $(NETOBJS) $(DEPLIBS)
115check_pgsql_DEPENDENCIES = check_pgsql.c $(NETOBJS) $(DEPLIBS) 118check_pgsql_DEPENDENCIES = check_pgsql.c $(NETOBJS) $(DEPLIBS)
@@ -123,6 +126,7 @@ check_ssh_DEPENDENCIES = check_ssh.c $(NETOBJS) $(DEPLIBS)
123check_swap_DEPENDENCIES = check_swap.c $(BASEOBJS) popen.o $(DEPLIBS) 126check_swap_DEPENDENCIES = check_swap.c $(BASEOBJS) popen.o $(DEPLIBS)
124check_tcp_DEPENDENCIES = check_tcp.c $(SSLOBJS) $(NETOBJS) $(DEPLIBS) 127check_tcp_DEPENDENCIES = check_tcp.c $(SSLOBJS) $(NETOBJS) $(DEPLIBS)
125check_time_DEPENDENCIES = check_time.c $(NETOBJS) $(DEPLIBS) 128check_time_DEPENDENCIES = check_time.c $(NETOBJS) $(DEPLIBS)
129check_ntp_time_DEPENDENCIES = check_ntp_time.c $(NETOBJS) $(DEPLIBS)
126check_ups_DEPENDENCIES = check_ups.c $(NETOBJS) $(DEPLIBS) 130check_ups_DEPENDENCIES = check_ups.c $(NETOBJS) $(DEPLIBS)
127check_users_DEPENDENCIES = check_users.c $(BASEOBJS) popen.o $(DEPLIBS) 131check_users_DEPENDENCIES = check_users.c $(BASEOBJS) popen.o $(DEPLIBS)
128check_by_ssh_DEPENDENCIES = check_by_ssh.c $(NETOBJS) runcmd.o $(DEPLIBS) 132check_by_ssh_DEPENDENCIES = check_by_ssh.c $(NETOBJS) runcmd.o $(DEPLIBS)
diff --git a/plugins/check_ntp_peer.c b/plugins/check_ntp_peer.c
index 164d519..1c4702c 100644
--- a/plugins/check_ntp_peer.c
+++ b/plugins/check_ntp_peer.c
@@ -1,6 +1,6 @@
1/****************************************************************************** 1/******************************************************************************
2* 2*
3* Nagios check_ntp plugin 3* Nagios check_ntp_peer plugin
4* 4*
5* License: GPL 5* License: GPL
6* Copyright (c) 2006 sean finney <seanius@seanius.net> 6* Copyright (c) 2006 sean finney <seanius@seanius.net>
@@ -10,11 +10,16 @@
10* 10*
11* Description: 11* Description:
12* 12*
13* This file contains the check_ntp plugin 13* This file contains the check_ntp_peer plugin
14* 14*
15* This plugin to check ntp servers independant of any commandline 15* This plugin checks an NTP server independent of any commandline
16* programs or external libraries. 16* programs or external libraries.
17* 17*
18* Use this plugin to check the health of an NTP server. It supports
19* checking the offset with the sync peer, the jitter and stratum. This
20* plugin will not check the clock offset between the local host and NTP
21* server; please use check_ntp_time for that purpose.
22*
18* 23*
19* License Information: 24* License Information:
20* 25*
@@ -36,7 +41,7 @@
36 41
37*****************************************************************************/ 42*****************************************************************************/
38 43
39const char *progname = "check_ntp"; 44const char *progname = "check_ntp_peer";
40const char *revision = "$Revision$"; 45const char *revision = "$Revision$";
41const char *copyright = "2007"; 46const char *copyright = "2007";
42const char *email = "nagiosplug-devel@lists.sourceforge.net"; 47const char *email = "nagiosplug-devel@lists.sourceforge.net";
@@ -47,51 +52,28 @@ const char *email = "nagiosplug-devel@lists.sourceforge.net";
47 52
48static char *server_address=NULL; 53static char *server_address=NULL;
49static int verbose=0; 54static int verbose=0;
55static int quiet=0;
50static short do_offset=0; 56static short do_offset=0;
51static char *owarn="60"; 57static char *owarn="60";
52static char *ocrit="120"; 58static char *ocrit="120";
59static short do_stratum=0;
60static char *swarn="-1:16";
61static char *scrit="-1:16";
53static short do_jitter=0; 62static short do_jitter=0;
54static char *jwarn="5000"; 63static char *jwarn="-1:5000";
55static char *jcrit="10000"; 64static char *jcrit="-1:10000";
65static int syncsource_found=0;
56 66
57int process_arguments (int, char **); 67int process_arguments (int, char **);
58thresholds *offset_thresholds = NULL; 68thresholds *offset_thresholds = NULL;
59thresholds *jitter_thresholds = NULL; 69thresholds *jitter_thresholds = NULL;
70thresholds *stratum_thresholds = NULL;
60void print_help (void); 71void print_help (void);
61void print_usage (void); 72void print_usage (void);
62 73
63/* number of times to perform each request to get a good average. */
64#define AVG_NUM 4
65
66/* max size of control message data */ 74/* max size of control message data */
67#define MAX_CM_SIZE 468 75#define MAX_CM_SIZE 468
68 76
69/* this structure holds everything in an ntp request/response as per rfc1305 */
70typedef struct {
71 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */
72 uint8_t stratum; /* clock stratum */
73 int8_t poll; /* polling interval */
74 int8_t precision; /* precision of the local clock */
75 int32_t rtdelay; /* total rt delay, as a fixed point num. see macros */
76 uint32_t rtdisp; /* like above, but for max err to primary src */
77 uint32_t refid; /* ref clock identifier */
78 uint64_t refts; /* reference timestamp. local time local clock */
79 uint64_t origts; /* time at which request departed client */
80 uint64_t rxts; /* time at which request arrived at server */
81 uint64_t txts; /* time at which request departed server */
82} ntp_message;
83
84/* this structure holds data about results from querying offset from a peer */
85typedef struct {
86 time_t waiting; /* ts set when we started waiting for a response */
87 int num_responses; /* number of successfully recieved responses */
88 uint8_t stratum; /* copied verbatim from the ntp_message */
89 double rtdelay; /* converted from the ntp_message */
90 double rtdisp; /* converted from the ntp_message */
91 double offset[AVG_NUM]; /* offsets from each response */
92 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */
93} ntp_server_results;
94
95/* this structure holds everything in an ntp control message as per rfc1305 */ 77/* this structure holds everything in an ntp control message as per rfc1305 */
96typedef struct { 78typedef struct {
97 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */ 79 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */
@@ -147,57 +129,6 @@ typedef struct {
147#define PEER_INCLUDED 0x04 129#define PEER_INCLUDED 0x04
148#define PEER_SYNCSOURCE 0x06 130#define PEER_SYNCSOURCE 0x06
149 131
150/**
151 ** a note about the 32-bit "fixed point" numbers:
152 **
153 they are divided into halves, each being a 16-bit int in network byte order:
154 - the first 16 bits are an int on the left side of a decimal point.
155 - the second 16 bits represent a fraction n/(2^16)
156 likewise for the 64-bit "fixed point" numbers with everything doubled :)
157 **/
158
159/* macros to access the left/right 16 bits of a 32-bit ntp "fixed point"
160 number. note that these can be used as lvalues too */
161#define L16(x) (((uint16_t*)&x)[0])
162#define R16(x) (((uint16_t*)&x)[1])
163/* macros to access the left/right 32 bits of a 64-bit ntp "fixed point"
164 number. these too can be used as lvalues */
165#define L32(x) (((uint32_t*)&x)[0])
166#define R32(x) (((uint32_t*)&x)[1])
167
168/* ntp wants seconds since 1/1/00, epoch is 1/1/70. this is the difference */
169#define EPOCHDIFF 0x83aa7e80UL
170
171/* extract a 32-bit ntp fixed point number into a double */
172#define NTP32asDOUBLE(x) (ntohs(L16(x)) + (double)ntohs(R16(x))/65536.0)
173
174/* likewise for a 64-bit ntp fp number */
175#define NTP64asDOUBLE(n) (double)(((uint64_t)n)?\
176 (ntohl(L32(n))-EPOCHDIFF) + \
177 (.00000001*(0.5+(double)(ntohl(R32(n))/42.94967296))):\
178 0)
179
180/* convert a struct timeval to a double */
181#define TVasDOUBLE(x) (double)(x.tv_sec+(0.000001*x.tv_usec))
182
183/* convert an ntp 64-bit fp number to a struct timeval */
184#define NTP64toTV(n,t) \
185 do{ if(!n) t.tv_sec = t.tv_usec = 0; \
186 else { \
187 t.tv_sec=ntohl(L32(n))-EPOCHDIFF; \
188 t.tv_usec=(int)(0.5+(double)(ntohl(R32(n))/4294.967296)); \
189 } \
190 }while(0)
191
192/* convert a struct timeval to an ntp 64-bit fp number */
193#define TVtoNTP64(t,n) \
194 do{ if(!t.tv_usec && !t.tv_sec) n=0x0UL; \
195 else { \
196 L32(n)=htonl(t.tv_sec + EPOCHDIFF); \
197 R32(n)=htonl((uint64_t)((4294.967296*t.tv_usec)+.5)); \
198 } \
199 } while(0)
200
201/* NTP control message header is 12 bytes, plus any data in the data 132/* NTP control message header is 12 bytes, plus any data in the data
202 * field, plus null padding to the nearest 32-bit boundary per rfc. 133 * field, plus null padding to the nearest 32-bit boundary per rfc.
203 */ 134 */
@@ -210,42 +141,6 @@ typedef struct {
210 printf("%u.%u.%u.%u", (x>>24)&0xff, (x>>16)&0xff, (x>>8)&0xff, x&0xff);\ 141 printf("%u.%u.%u.%u", (x>>24)&0xff, (x>>16)&0xff, (x>>8)&0xff, x&0xff);\
211 }while(0); 142 }while(0);
212 143
213/* calculate the offset of the local clock */
214static inline double calc_offset(const ntp_message *m, const struct timeval *t){
215 double client_tx, peer_rx, peer_tx, client_rx;
216 client_tx = NTP64asDOUBLE(m->origts);
217 peer_rx = NTP64asDOUBLE(m->rxts);
218 peer_tx = NTP64asDOUBLE(m->txts);
219 client_rx=TVasDOUBLE((*t));
220 return (.5*((peer_tx-client_rx)+(peer_rx-client_tx)));
221}
222
223/* print out a ntp packet in human readable/debuggable format */
224void print_ntp_message(const ntp_message *p){
225 struct timeval ref, orig, rx, tx;
226
227 NTP64toTV(p->refts,ref);
228 NTP64toTV(p->origts,orig);
229 NTP64toTV(p->rxts,rx);
230 NTP64toTV(p->txts,tx);
231
232 printf("packet contents:\n");
233 printf("\tflags: 0x%.2x\n", p->flags);
234 printf("\t li=%d (0x%.2x)\n", LI(p->flags), p->flags&LI_MASK);
235 printf("\t vn=%d (0x%.2x)\n", VN(p->flags), p->flags&VN_MASK);
236 printf("\t mode=%d (0x%.2x)\n", MODE(p->flags), p->flags&MODE_MASK);
237 printf("\tstratum = %d\n", p->stratum);
238 printf("\tpoll = %g\n", pow(2, p->poll));
239 printf("\tprecision = %g\n", pow(2, p->precision));
240 printf("\trtdelay = %-.16g\n", NTP32asDOUBLE(p->rtdelay));
241 printf("\trtdisp = %-.16g\n", NTP32asDOUBLE(p->rtdisp));
242 printf("\trefid = %x\n", p->refid);
243 printf("\trefts = %-.16g\n", NTP64asDOUBLE(p->refts));
244 printf("\torigts = %-.16g\n", NTP64asDOUBLE(p->origts));
245 printf("\trxts = %-.16g\n", NTP64asDOUBLE(p->rxts));
246 printf("\ttxts = %-.16g\n", NTP64asDOUBLE(p->txts));
247}
248
249void print_ntp_control_message(const ntp_control_message *p){ 144void print_ntp_control_message(const ntp_control_message *p){
250 int i=0, numpeers=0; 145 int i=0, numpeers=0;
251 const ntp_assoc_status_pair *peer=NULL; 146 const ntp_assoc_status_pair *peer=NULL;
@@ -282,222 +177,25 @@ void print_ntp_control_message(const ntp_control_message *p){
282 } 177 }
283} 178}
284 179
285void setup_request(ntp_message *p){ 180char *extract_value(const char *varlist, const char *name){
286 struct timeval t; 181 char *tmpvarlist=NULL, *tmpkey=NULL, *value=NULL;
287 182 int last=0;
288 memset(p, 0, sizeof(ntp_message));
289 LI_SET(p->flags, LI_ALARM);
290 VN_SET(p->flags, 4);
291 MODE_SET(p->flags, MODE_CLIENT);
292 p->poll=4;
293 p->precision=(int8_t)0xfa;
294 L16(p->rtdelay)=htons(1);
295 L16(p->rtdisp)=htons(1);
296
297 gettimeofday(&t, NULL);
298 TVtoNTP64(t,p->txts);
299}
300
301/* select the "best" server from a list of servers, and return its index.
302 * this is done by filtering servers based on stratum, dispersion, and
303 * finally round-trip delay. */
304int best_offset_server(const ntp_server_results *slist, int nservers){
305 int i=0, j=0, cserver=0, candidates[5], csize=0;
306
307 /* for each server */
308 for(cserver=0; cserver<nservers; cserver++){
309 /* sort out servers with error flags */
310 if ( LI(slist[cserver].flags) != LI_NOWARNING ){
311 if (verbose) printf("discarding peer id %d: flags=%d\n", cserver, LI(slist[cserver].flags));
312 break;
313 }
314
315 /* compare it to each of the servers already in the candidate list */
316 for(i=0; i<csize; i++){
317 /* does it have an equal or better stratum? */
318 if(slist[cserver].stratum <= slist[i].stratum){
319 /* does it have an equal or better dispersion? */
320 if(slist[cserver].rtdisp <= slist[i].rtdisp){
321 /* does it have a better rtdelay? */
322 if(slist[cserver].rtdelay < slist[i].rtdelay){
323 break;
324 }
325 }
326 }
327 }
328
329 /* if we haven't reached the current list's end, move everyone
330 * over one to the right, and insert the new candidate */
331 if(i<csize){
332 for(j=5; j>i; j--){
333 candidates[j]=candidates[j-1];
334 }
335 }
336 /* regardless, if they should be on the list... */
337 if(i<5) {
338 candidates[i]=cserver;
339 if(csize<5) csize++;
340 /* otherwise discard the server */
341 } else {
342 DBG(printf("discarding peer id %d\n", cserver));
343 }
344 }
345
346 if(csize>0) {
347 DBG(printf("best server selected: peer %d\n", candidates[0]));
348 return candidates[0];
349 } else {
350 DBG(printf("no peers meeting synchronization criteria :(\n"));
351 return -1;
352 }
353}
354
355/* do everything we need to get the total average offset
356 * - we use a certain amount of parallelization with poll() to ensure
357 * we don't waste time sitting around waiting for single packets.
358 * - we also "manually" handle resolving host names and connecting, because
359 * we have to do it in a way that our lazy macros don't handle currently :( */
360double offset_request(const char *host, int *status){
361 int i=0, j=0, ga_result=0, num_hosts=0, *socklist=NULL, respnum=0;
362 int servers_completed=0, one_written=0, one_read=0, servers_readable=0, best_index=-1;
363 time_t now_time=0, start_ts=0;
364 ntp_message *req=NULL;
365 double avg_offset=0.;
366 struct timeval recv_time;
367 struct addrinfo *ai=NULL, *ai_tmp=NULL, hints;
368 struct pollfd *ufds=NULL;
369 ntp_server_results *servers=NULL;
370
371 /* setup hints to only return results from getaddrinfo that we'd like */
372 memset(&hints, 0, sizeof(struct addrinfo));
373 hints.ai_family = address_family;
374 hints.ai_protocol = IPPROTO_UDP;
375 hints.ai_socktype = SOCK_DGRAM;
376
377 /* fill in ai with the list of hosts resolved by the host name */
378 ga_result = getaddrinfo(host, "123", &hints, &ai);
379 if(ga_result!=0){
380 die(STATE_UNKNOWN, "error getting address for %s: %s\n",
381 host, gai_strerror(ga_result));
382 }
383
384 /* count the number of returned hosts, and allocate stuff accordingly */
385 for(ai_tmp=ai; ai_tmp!=NULL; ai_tmp=ai_tmp->ai_next){ num_hosts++; }
386 req=(ntp_message*)malloc(sizeof(ntp_message)*num_hosts);
387 if(req==NULL) die(STATE_UNKNOWN, "can not allocate ntp message array");
388 socklist=(int*)malloc(sizeof(int)*num_hosts);
389 if(socklist==NULL) die(STATE_UNKNOWN, "can not allocate socket array");
390 ufds=(struct pollfd*)malloc(sizeof(struct pollfd)*num_hosts);
391 if(ufds==NULL) die(STATE_UNKNOWN, "can not allocate socket array");
392 servers=(ntp_server_results*)malloc(sizeof(ntp_server_results)*num_hosts);
393 if(servers==NULL) die(STATE_UNKNOWN, "can not allocate server array");
394 memset(servers, 0, sizeof(ntp_server_results)*num_hosts);
395
396 /* setup each socket for writing, and the corresponding struct pollfd */
397 ai_tmp=ai;
398 for(i=0;ai_tmp;i++){
399 socklist[i]=socket(ai_tmp->ai_family, SOCK_DGRAM, IPPROTO_UDP);
400 if(socklist[i] == -1) {
401 perror(NULL);
402 die(STATE_UNKNOWN, "can not create new socket");
403 }
404 if(connect(socklist[i], ai_tmp->ai_addr, ai_tmp->ai_addrlen)){
405 die(STATE_UNKNOWN, "can't create socket connection");
406 } else {
407 ufds[i].fd=socklist[i];
408 ufds[i].events=POLLIN;
409 ufds[i].revents=0;
410 }
411 ai_tmp = ai_tmp->ai_next;
412 }
413
414 /* now do AVG_NUM checks to each host. we stop before timeout/2 seconds
415 * have passed in order to ensure post-processing and jitter time. */
416 now_time=start_ts=time(NULL);
417 while(servers_completed<num_hosts && now_time-start_ts <= socket_timeout/2){
418 /* loop through each server and find each one which hasn't
419 * been touched in the past second or so and is still lacking
420 * some responses. for each of these servers, send a new request,
421 * and update the "waiting" timestamp with the current time. */
422 one_written=0;
423 now_time=time(NULL);
424
425 for(i=0; i<num_hosts; i++){
426 if(servers[i].waiting<now_time && servers[i].num_responses<AVG_NUM){
427 if(verbose && servers[i].waiting != 0) printf("re-");
428 if(verbose) printf("sending request to peer %d\n", i);
429 setup_request(&req[i]);
430 write(socklist[i], &req[i], sizeof(ntp_message));
431 servers[i].waiting=now_time;
432 one_written=1;
433 break;
434 }
435 }
436
437 /* quickly poll for any sockets with pending data */
438 servers_readable=poll(ufds, num_hosts, 100);
439 if(servers_readable==-1){
440 perror("polling ntp sockets");
441 die(STATE_UNKNOWN, "communication errors");
442 }
443
444 /* read from any sockets with pending data */
445 for(i=0; servers_readable && i<num_hosts; i++){
446 if(ufds[i].revents&POLLIN && servers[i].num_responses < AVG_NUM){
447 if(verbose) {
448 printf("response from peer %d: ", i);
449 }
450 183
451 read(ufds[i].fd, &req[i], sizeof(ntp_message)); 184 /* The following code require a non-empty varlist */
452 gettimeofday(&recv_time, NULL); 185 if(strlen(varlist) == 0)
453 DBG(print_ntp_message(&req[i])); 186 return NULL;
454 respnum=servers[i].num_responses++;
455 servers[i].offset[respnum]=calc_offset(&req[i], &recv_time);
456 if(verbose) {
457 printf("offset %.10g\n", servers[i].offset[respnum]);
458 }
459 servers[i].stratum=req[i].stratum;
460 servers[i].rtdisp=NTP32asDOUBLE(req[i].rtdisp);
461 servers[i].rtdelay=NTP32asDOUBLE(req[i].rtdelay);
462 servers[i].waiting=0;
463 servers[i].flags=req[i].flags;
464 servers_readable--;
465 one_read = 1;
466 if(servers[i].num_responses==AVG_NUM) servers_completed++;
467 }
468 }
469 /* lather, rinse, repeat. */
470 }
471 187
472 if (one_read == 0) { 188 tmpvarlist = strdup(varlist);
473 die(STATE_CRITICAL, "NTP CRITICAL: No response from NTP server\n"); 189 tmpkey = strtok(tmpvarlist, "=");
474 }
475 190
476 /* now, pick the best server from the list */ 191 do {
477 best_index=best_offset_server(servers, num_hosts); 192 if(strstr(tmpkey, name) != NULL) {
478 if(best_index < 0){ 193 value = strtok(NULL, ",");
479 *status=STATE_UNKNOWN; 194 last = 1;
480 } else {
481 /* finally, calculate the average offset */
482 for(i=0; i<servers[best_index].num_responses;i++){
483 avg_offset+=servers[best_index].offset[j];
484 } 195 }
485 avg_offset/=servers[best_index].num_responses; 196 } while (last == 0 && (tmpkey = strtok(NULL, "=")));
486 }
487 197
488 /* cleanup */ 198 return value;
489 /* FIXME: Not closing the socket to avoid re-use of the local port
490 * which can cause old NTP packets to be read instead of NTP control
491 * pactets in jitter_request(). THERE MUST BE ANOTHER WAY...
492 * for(j=0; j<num_hosts; j++){ close(socklist[j]); } */
493 free(socklist);
494 free(ufds);
495 free(servers);
496 free(req);
497 freeaddrinfo(ai);
498
499 if(verbose) printf("overall average offset: %.10g\n", avg_offset);
500 return avg_offset;
501} 199}
502 200
503void 201void
@@ -511,20 +209,36 @@ setup_control_request(ntp_control_message *p, uint8_t opcode, uint16_t seq){
511 /* Remaining fields are zero for requests */ 209 /* Remaining fields are zero for requests */
512} 210}
513 211
514/* XXX handle responses with the error bit set */ 212/* This function does all the actual work; roughly here's what it does
515double jitter_request(const char *host, int *status){ 213 * beside setting the offest, jitter and stratum passed as argument:
516 int conn=-1, i, npeers=0, num_candidates=0, syncsource_found=0; 214 * - offset can be negative, so if it cannot get the offset, offset_result
517 int run=0, min_peer_sel=PEER_INCLUDED, num_selected=0, num_valid=0; 215 * is set to UNKNOWN, otherwise OK.
216 * - jitter and stratum are set to -1 if they cannot be retrieved so any
217 * positive value means a success retrieving the value.
218 * - status is set to WARNING if there's no sync.peer (otherwise OK) and is
219 * the return value of the function.
220 * status is pretty much useless as syncsource_found is a global variable
221 * used later in main to check is the server was synchronized. It works
222 * so I left it alone */
223int ntp_request(const char *host, double *offset, int *offset_result, double *jitter, int *stratum){
224 int conn=-1, i, npeers=0, num_candidates=0;
225 double tmp_offset = 0;
226 int min_peer_sel=PEER_INCLUDED;
518 int peers_size=0, peer_offset=0; 227 int peers_size=0, peer_offset=0;
228 int status;
519 ntp_assoc_status_pair *peers=NULL; 229 ntp_assoc_status_pair *peers=NULL;
520 ntp_control_message req; 230 ntp_control_message req;
521 const char *getvar = "jitter"; 231 const char *getvar = "stratum,offset,jitter";
522 double rval = 0.0, jitter = -1.0; 232 char *data, *value, *nptr;
523 char *startofvalue=NULL, *nptr=NULL;
524 void *tmp; 233 void *tmp;
525 234
235 status = STATE_OK;
236 *offset_result = STATE_UNKNOWN;
237 *jitter = *stratum = -1;
238
526 /* Long-winded explanation: 239 /* Long-winded explanation:
527 * Getting the jitter requires a number of steps: 240 * Getting the sync peer offset, jitter and stratum requires a number of
241 * steps:
528 * 1) Send a READSTAT request. 242 * 1) Send a READSTAT request.
529 * 2) Interpret the READSTAT reply 243 * 2) Interpret the READSTAT reply
530 * a) The data section contains a list of peer identifiers (16 bits) 244 * a) The data section contains a list of peer identifiers (16 bits)
@@ -535,7 +249,8 @@ double jitter_request(const char *host, int *status){
535 * set a minimum of warning. 249 * set a minimum of warning.
536 * 3) Send a READVAR request for information on each peer identified 250 * 3) Send a READVAR request for information on each peer identified
537 * in 2b greater than the minimum selection value. 251 * in 2b greater than the minimum selection value.
538 * 4) Extract the jitter value from the data[] (it's ASCII) 252 * 4) Extract the offset, jitter and stratum value from the data[]
253 * (it's ASCII)
539 */ 254 */
540 my_udp_connect(server_address, 123, &conn); 255 my_udp_connect(server_address, 123, &conn);
541 256
@@ -564,7 +279,7 @@ double jitter_request(const char *host, int *status){
564 } while(req.op&REM_MORE); 279 } while(req.op&REM_MORE);
565 280
566 /* first, let's find out if we have a sync source, or if there are 281 /* first, let's find out if we have a sync source, or if there are
567 * at least some candidates. in the case of the latter we'll issue 282 * at least some candidates. In the latter case we'll issue
568 * a warning but go ahead with the check on them. */ 283 * a warning but go ahead with the check on them. */
569 for (i = 0; i < npeers; i++){ 284 for (i = 0; i < npeers; i++){
570 if (PEER_SEL(peers[i].status) >= PEER_INCLUDED){ 285 if (PEER_SEL(peers[i].status) >= PEER_INCLUDED){
@@ -578,21 +293,22 @@ double jitter_request(const char *host, int *status){
578 if(verbose) printf("%d candiate peers available\n", num_candidates); 293 if(verbose) printf("%d candiate peers available\n", num_candidates);
579 if(verbose && syncsource_found) printf("synchronization source found\n"); 294 if(verbose && syncsource_found) printf("synchronization source found\n");
580 if(! syncsource_found){ 295 if(! syncsource_found){
581 *status = STATE_UNKNOWN; 296 status = STATE_WARNING;
582 if(verbose) printf("warning: no synchronization source found\n"); 297 if(verbose) printf("warning: no synchronization source found\n");
583 } 298 }
584 299
585 300
586 for (run=0; run<AVG_NUM; run++){ 301 for (i = 0; i < npeers; i++){
587 if(verbose) printf("jitter run %d of %d\n", run+1, AVG_NUM); 302 /* Only query this server if it is the current sync source */
588 for (i = 0; i < npeers; i++){ 303 /* If there's no sync.peer, query all candidates and use the best one */
589 /* Only query this server if it is the current sync source */ 304 if (PEER_SEL(peers[i].status) >= min_peer_sel){
590 if (PEER_SEL(peers[i].status) >= min_peer_sel){ 305 if(verbose) printf("Getting offset, jitter and stratum for peer %.2x\n", ntohs(peers[i].assoc));
591 num_selected++; 306 asprintf(&data, "");
307 do{
592 setup_control_request(&req, OP_READVAR, 2); 308 setup_control_request(&req, OP_READVAR, 2);
593 req.assoc = peers[i].assoc; 309 req.assoc = peers[i].assoc;
594 /* By spec, putting the variable name "jitter" in the request 310 /* Putting the wanted variable names in the request
595 * should cause the server to provide _only_ the jitter value. 311 * cause the server to provide _only_ the requested values.
596 * thus reducing net traffic, guaranteeing us only a single 312 * thus reducing net traffic, guaranteeing us only a single
597 * datagram in reply, and making intepretation much simpler 313 * datagram in reply, and making intepretation much simpler
598 */ 314 */
@@ -605,48 +321,97 @@ double jitter_request(const char *host, int *status){
605 DBG(print_ntp_control_message(&req)); 321 DBG(print_ntp_control_message(&req));
606 322
607 req.count = htons(MAX_CM_SIZE); 323 req.count = htons(MAX_CM_SIZE);
608 DBG(printf("recieving READVAR response...\n")); 324 DBG(printf("receiving READVAR response...\n"));
609 read(conn, &req, SIZEOF_NTPCM(req)); 325 read(conn, &req, SIZEOF_NTPCM(req));
610 DBG(print_ntp_control_message(&req)); 326 DBG(print_ntp_control_message(&req));
611 327
612 if(req.op&REM_ERROR && strstr(getvar, "jitter")) { 328 if(!(req.op&REM_ERROR))
613 if(verbose) printf("The 'jitter' command failed (old ntp server?)\nRestarting with 'dispersion'...\n"); 329 asprintf(&data, "%s%s", data, req.data);
614 getvar = "dispersion"; 330 } while(req.op&REM_MORE);
615 num_selected--; 331
332 if(req.op&REM_ERROR) {
333 if(strstr(getvar, "jitter")) {
334 if(verbose) printf("The command failed. This is usually caused by servers refusing the 'jitter'\nvariable. Restarting with 'dispersion'...\n");
335 getvar = "stratum,offset,dispersion";
336 i--;
337 continue;
338 } else if(strlen(getvar)) {
339 if(verbose) printf("Server didn't like dispersion either; will retrieve everything\n");
340 getvar = "";
616 i--; 341 i--;
617 continue; 342 continue;
618 } 343 }
344 }
345
346 if(verbose > 1)
347 printf("Server responded: >>>%s<<<\n", data);
348
349 /* get the offset */
350 if(verbose)
351 printf("parsing offset from peer %.2x: ", ntohs(peers[i].assoc));
352
353 value = extract_value(data, "offset");
354 nptr=NULL;
355 /* Convert the value if we have one */
356 if(value != NULL)
357 tmp_offset = strtod(value, &nptr) / 1000;
358 /* If value is null or no conversion was performed */
359 if(value == NULL || value==nptr) {
360 if(verbose) printf("error: unable to read server offset response.\n");
361 } else {
362 if(verbose) printf("%.10g\n", tmp_offset);
363 if(*offset_result == STATE_UNKNOWN || fabs(tmp_offset) < fabs(*offset)) {
364 *offset = tmp_offset;
365 *offset_result = STATE_OK;
366 } else {
367 /* Skip this one; move to the next */
368 continue;
369 }
370 }
619 371
620 /* get to the float value */ 372 if(do_jitter) {
373 /* get the jitter */
621 if(verbose) { 374 if(verbose) {
622 printf("parsing jitter from peer %.2x: ", ntohs(peers[i].assoc)); 375 printf("parsing %s from peer %.2x: ", strstr(getvar, "dispersion") != NULL ? "dispersion" : "jitter", ntohs(peers[i].assoc));
623 } 376 }
624 startofvalue = strchr(req.data, '='); 377 value = extract_value(data, strstr(getvar, "dispersion") != NULL ? "dispersion" : "jitter");
625 if(startofvalue != NULL) { 378 nptr=NULL;
626 startofvalue++; 379 /* Convert the value if we have one */
627 jitter = strtod(startofvalue, &nptr); 380 if(value != NULL)
381 *jitter = strtod(value, &nptr);
382 /* If value is null or no conversion was performed */
383 if(value == NULL || value==nptr) {
384 if(verbose) printf("error: unable to read server jitter/dispersion response.\n");
385 *jitter = -1;
386 } else if(verbose) {
387 printf("%.10g\n", *jitter);
388 }
389 }
390
391 if(do_stratum) {
392 /* get the stratum */
393 if(verbose) {
394 printf("parsing stratum from peer %.2x: ", ntohs(peers[i].assoc));
628 } 395 }
629 if(startofvalue == NULL || startofvalue==nptr){ 396 value = extract_value(data, "stratum");
630 printf("warning: unable to read server jitter response.\n"); 397 nptr=NULL;
631 *status = STATE_UNKNOWN; 398 /* Convert the value if we have one */
399 if(value != NULL)
400 *stratum = strtol(value, &nptr, 10);
401 if(value == NULL || value==nptr) {
402 if(verbose) printf("error: unable to read server stratum response.\n");
403 *stratum = -1;
632 } else { 404 } else {
633 if(verbose) printf("%g\n", jitter); 405 if(verbose) printf("%i\n", *stratum);
634 num_valid++;
635 rval += jitter;
636 } 406 }
637 } 407 }
638 } 408 } /* if (PEER_SEL(peers[i].status) >= min_peer_sel) */
639 if(verbose){ 409 } /* for (i = 0; i < npeers; i++) */
640 printf("jitter parsed from %d/%d peers\n", num_valid, num_selected);
641 }
642 }
643
644 rval = num_valid ? rval / num_valid : -1.0;
645 410
646 close(conn); 411 close(conn);
647 if(peers!=NULL) free(peers); 412 if(peers!=NULL) free(peers);
648 /* If we return -1.0, it means no synchronization source was found */ 413
649 return rval; 414 return status;
650} 415}
651 416
652int process_arguments(int argc, char **argv){ 417int process_arguments(int argc, char **argv){
@@ -658,8 +423,11 @@ int process_arguments(int argc, char **argv){
658 {"verbose", no_argument, 0, 'v'}, 423 {"verbose", no_argument, 0, 'v'},
659 {"use-ipv4", no_argument, 0, '4'}, 424 {"use-ipv4", no_argument, 0, '4'},
660 {"use-ipv6", no_argument, 0, '6'}, 425 {"use-ipv6", no_argument, 0, '6'},
426 {"quiet", no_argument, 0, 'q'},
661 {"warning", required_argument, 0, 'w'}, 427 {"warning", required_argument, 0, 'w'},
662 {"critical", required_argument, 0, 'c'}, 428 {"critical", required_argument, 0, 'c'},
429 {"swarn", required_argument, 0, 'W'},
430 {"scrit", required_argument, 0, 'C'},
663 {"jwarn", required_argument, 0, 'j'}, 431 {"jwarn", required_argument, 0, 'j'},
664 {"jcrit", required_argument, 0, 'k'}, 432 {"jcrit", required_argument, 0, 'k'},
665 {"timeout", required_argument, 0, 't'}, 433 {"timeout", required_argument, 0, 't'},
@@ -672,7 +440,7 @@ int process_arguments(int argc, char **argv){
672 usage ("\n"); 440 usage ("\n");
673 441
674 while (1) { 442 while (1) {
675 c = getopt_long (argc, argv, "Vhv46w:c:j:k:t:H:", longopts, &option); 443 c = getopt_long (argc, argv, "Vhv46qw:c:W:C:j:k:t:H:", longopts, &option);
676 if (c == -1 || c == EOF || c == 1) 444 if (c == -1 || c == EOF || c == 1)
677 break; 445 break;
678 446
@@ -688,6 +456,9 @@ int process_arguments(int argc, char **argv){
688 case 'v': 456 case 'v':
689 verbose++; 457 verbose++;
690 break; 458 break;
459 case 'q':
460 quiet = 1;
461 break;
691 case 'w': 462 case 'w':
692 do_offset=1; 463 do_offset=1;
693 owarn = optarg; 464 owarn = optarg;
@@ -696,6 +467,14 @@ int process_arguments(int argc, char **argv){
696 do_offset=1; 467 do_offset=1;
697 ocrit = optarg; 468 ocrit = optarg;
698 break; 469 break;
470 case 'W':
471 do_stratum=1;
472 swarn = optarg;
473 break;
474 case 'C':
475 do_stratum=1;
476 scrit = optarg;
477 break;
699 case 'j': 478 case 'j':
700 do_jitter=1; 479 do_jitter=1;
701 jwarn = optarg; 480 jwarn = optarg;
@@ -746,24 +525,31 @@ char *perfd_offset (double offset)
746 525
747char *perfd_jitter (double jitter) 526char *perfd_jitter (double jitter)
748{ 527{
749 return fperfdata ("jitter", jitter, "s", 528 return fperfdata ("jitter", jitter, "",
750 do_jitter, jitter_thresholds->warning->end, 529 do_jitter, jitter_thresholds->warning->end,
751 do_jitter, jitter_thresholds->critical->end, 530 do_jitter, jitter_thresholds->critical->end,
752 TRUE, 0, FALSE, 0); 531 TRUE, 0, FALSE, 0);
753} 532}
754 533
534char *perfd_stratum (int stratum)
535{
536 return perfdata ("stratum", stratum, "",
537 do_stratum, (int)stratum_thresholds->warning->end,
538 do_stratum, (int)stratum_thresholds->critical->end,
539 TRUE, 0, TRUE, 16);
540}
541
755int main(int argc, char *argv[]){ 542int main(int argc, char *argv[]){
756 int result, offset_result, jitter_result; 543 int result, offset_result, stratum;
757 double offset=0, jitter=0; 544 double offset=0, jitter=0;
758 char *result_line, *perfdata_line; 545 char *result_line, *perfdata_line;
759 546
760 result = offset_result = jitter_result = STATE_OK;
761
762 if (process_arguments (argc, argv) == ERROR) 547 if (process_arguments (argc, argv) == ERROR)
763 usage4 (_("Could not parse arguments")); 548 usage4 (_("Could not parse arguments"));
764 549
765 set_thresholds(&offset_thresholds, owarn, ocrit); 550 set_thresholds(&offset_thresholds, owarn, ocrit);
766 set_thresholds(&jitter_thresholds, jwarn, jcrit); 551 set_thresholds(&jitter_thresholds, jwarn, jcrit);
552 set_thresholds(&stratum_thresholds, swarn, scrit);
767 553
768 /* initialize alarm signal handling */ 554 /* initialize alarm signal handling */
769 signal (SIGALRM, socket_timeout_alarm_handler); 555 signal (SIGALRM, socket_timeout_alarm_handler);
@@ -771,29 +557,24 @@ int main(int argc, char *argv[]){
771 /* set socket timeout */ 557 /* set socket timeout */
772 alarm (socket_timeout); 558 alarm (socket_timeout);
773 559
774 offset = offset_request(server_address, &offset_result); 560 /* This returns either OK or WARNING (See comment preceeding ntp_request) */
775 /* check_ntp used to always return CRITICAL if offset_result == STATE_UNKNOWN. 561 result = ntp_request(server_address, &offset, &offset_result, &jitter, &stratum);
776 * Now we'll only do that is the offset thresholds were set */ 562
777 if (do_offset && offset_result == STATE_UNKNOWN) { 563 if(offset_result == STATE_UNKNOWN) {
778 result = STATE_CRITICAL; 564 /* if there's no sync peer (this overrides ntp_request output): */
565 result = (quiet == 1 ? STATE_UNKNOWN : STATE_CRITICAL);
779 } else { 566 } else {
780 result = get_status(fabs(offset), offset_thresholds); 567 /* Be quiet if there's no candidates either */
568 if (quiet == 1 && result == STATE_WARNING)
569 result = STATE_UNKNOWN;
570 result = max_state_alt(result, get_status(fabs(offset), offset_thresholds));
781 } 571 }
782 572
783 /* If not told to check the jitter, we don't even send packets. 573 if(do_stratum)
784 * jitter is checked using NTP control packets, which not all 574 result = max_state_alt(result, get_status(stratum, stratum_thresholds));
785 * servers recognize. Trying to check the jitter on OpenNTPD 575
786 * (for example) will result in an error 576 if(do_jitter)
787 */
788 if(do_jitter){
789 jitter=jitter_request(server_address, &jitter_result);
790 result = max_state_alt(result, get_status(jitter, jitter_thresholds)); 577 result = max_state_alt(result, get_status(jitter, jitter_thresholds));
791 /* -1 indicates that we couldn't calculate the jitter
792 * Only overrides STATE_OK from the offset */
793 if(jitter == -1.0 && result == STATE_OK)
794 result = STATE_UNKNOWN;
795 }
796 result = max_state_alt(result, jitter_result);
797 578
798 switch (result) { 579 switch (result) {
799 case STATE_CRITICAL : 580 case STATE_CRITICAL :
@@ -809,6 +590,9 @@ int main(int argc, char *argv[]){
809 asprintf(&result_line, "NTP UNKNOWN:"); 590 asprintf(&result_line, "NTP UNKNOWN:");
810 break; 591 break;
811 } 592 }
593 if(!syncsource_found)
594 asprintf(&result_line, "%s %s,", result_line, _("Server not synchronized"));
595
812 if(offset_result == STATE_UNKNOWN){ 596 if(offset_result == STATE_UNKNOWN){
813 asprintf(&result_line, "%s %s", result_line, _("Offset unknown")); 597 asprintf(&result_line, "%s %s", result_line, _("Offset unknown"));
814 asprintf(&perfdata_line, ""); 598 asprintf(&perfdata_line, "");
@@ -818,7 +602,11 @@ int main(int argc, char *argv[]){
818 } 602 }
819 if (do_jitter) { 603 if (do_jitter) {
820 asprintf(&result_line, "%s, jitter=%f", result_line, jitter); 604 asprintf(&result_line, "%s, jitter=%f", result_line, jitter);
821 asprintf(&perfdata_line, "%s %s", perfdata_line, perfd_jitter(jitter)); 605 asprintf(&perfdata_line, "%s %s", perfdata_line, perfd_jitter(jitter));
606 }
607 if (do_stratum) {
608 asprintf(&result_line, "%s, stratum=%i", result_line, stratum);
609 asprintf(&perfdata_line, "%s %s", perfdata_line, perfd_stratum(stratum));
822 } 610 }
823 printf("%s|%s\n", result_line, perfdata_line); 611 printf("%s|%s\n", result_line, perfdata_line);
824 612
@@ -833,18 +621,24 @@ void print_help(void){
833 621
834 printf ("Copyright (c) 2006 Sean Finney\n"); 622 printf ("Copyright (c) 2006 Sean Finney\n");
835 printf (COPYRIGHT, copyright, email); 623 printf (COPYRIGHT, copyright, email);
836
837 printf ("%s\n", _("This plugin checks the selected ntp server"));
838 624
839 printf ("\n\n"); 625 printf ("%s\n", _("This plugin checks the selected ntp server"));
840 626
627 printf ("\n\n");
628
841 print_usage(); 629 print_usage();
842 printf (_(UT_HELP_VRSN)); 630 printf (_(UT_HELP_VRSN));
843 printf (_(UT_HOST_PORT), 'p', "123"); 631 printf (_(UT_HOST_PORT), 'p', "123");
632 printf (" %s\n", "-q, --quiet");
633 printf (" %s\n", _("Returns UNKNOWN instead of CRITICAL or WARNING if server isn't synchronized"));
844 printf (" %s\n", "-w, --warning=THRESHOLD"); 634 printf (" %s\n", "-w, --warning=THRESHOLD");
845 printf (" %s\n", _("Offset to result in warning status (seconds)")); 635 printf (" %s\n", _("Offset to result in warning status (seconds)"));
846 printf (" %s\n", "-c, --critical=THRESHOLD"); 636 printf (" %s\n", "-c, --critical=THRESHOLD");
847 printf (" %s\n", _("Offset to result in critical status (seconds)")); 637 printf (" %s\n", _("Offset to result in critical status (seconds)"));
638 printf (" %s\n", "-W, --warning=THRESHOLD");
639 printf (" %s\n", _("Warning threshold for stratum"));
640 printf (" %s\n", "-W, --critical=THRESHOLD");
641 printf (" %s\n", _("Critical threshold for stratum"));
848 printf (" %s\n", "-j, --warning=THRESHOLD"); 642 printf (" %s\n", "-j, --warning=THRESHOLD");
849 printf (" %s\n", _("Warning threshold for jitter")); 643 printf (" %s\n", _("Warning threshold for jitter"));
850 printf (" %s\n", "-k, --critical=THRESHOLD"); 644 printf (" %s\n", "-k, --critical=THRESHOLD");
@@ -854,17 +648,26 @@ void print_help(void){
854 648
855 printf("\n"); 649 printf("\n");
856 printf("%s\n", _("Notes:")); 650 printf("%s\n", _("Notes:"));
651 printf(" %s\n", _("This plugin checks an NTP server independent of any commandline"));
652 printf(" %s\n\n", _("programs or external libraries."));
653 printf(" %s\n", _("Use this plugin to check the health of an NTP server. It supports"));
654 printf(" %s\n", _("checking the offset with the sync peer, the jitter and stratum. This"));
655 printf(" %s\n", _("plugin will not check the clock offset between the local host and NTP"));
656 printf(" %s\n\n", _("server; please use check_ntp_time for that purpose."));
657
857 printf(" %s\n", _("See:")); 658 printf(" %s\n", _("See:"));
858 printf(" %s\n", ("http://nagiosplug.sourceforge.net/developer-guidelines.html#THRESHOLDFORMAT")); 659 printf(" %s\n", ("http://nagiosplug.sourceforge.net/developer-guidelines.html#THRESHOLDFORMAT"));
859 printf(" %s\n", _("for THRESHOLD format and examples.")); 660 printf(" %s\n", _("for THRESHOLD format and examples."));
860 661
861 printf("\n"); 662 printf("\n");
862 printf("%s\n", _("Examples:")); 663 printf("%s\n", _("Examples:"));
863 printf(" %s\n", _("Normal offset check:")); 664 printf(" %s\n", _("Normal NTP server check:"));
864 printf(" %s\n", ("./check_ntp -H ntpserv -w 0.5 -c 1")); 665 printf(" %s\n", ("./check_ntp_peer -H ntpserv -w 0.5 -c 1"));
865 printf(" %s\n", _("Check jitter too, avoiding critical notifications if jitter isn't available")); 666 printf(" %s\n", _("Check jitter too, avoiding critical notifications if jitter isn't available"));
866 printf(" %s\n", _("(See Notes above for more details on thresholds formats):")); 667 printf(" %s\n", _("(See Notes above for more details on thresholds formats):"));
867 printf(" %s\n", ("./check_ntp -H ntpserv -w 0.5 -c 1 -j -1:100 -k -1:200")); 668 printf(" %s\n", ("./check_ntp_peer -H ntpserv -w 0.5 -c 1 -j -1:100 -k -1:200"));
669 printf(" %s\n", _("Check only stratum:"));
670 printf(" %s\n", ("./check_ntp_peer -H ntpserv -W 4 -C 6"));
868 671
869 printf (_(UT_SUPPORT)); 672 printf (_(UT_SUPPORT));
870} 673}
@@ -872,6 +675,7 @@ void print_help(void){
872void 675void
873print_usage(void) 676print_usage(void)
874{ 677{
875 printf (_("Usage:")); 678 printf (_("Usage:"));
876 printf(" %s -H <host> [-w <warn>] [-c <crit>] [-j <warn>] [-k <crit>] [-v verbose]\n", progname); 679 printf(" %s -H <host> [-w <warn>] [-c <crit>] [-W <warn>] [-C <crit>]\n", progname);
680 printf(" [-j <warn>] [-k <crit>] [-v verbose]\n");
877} 681}
diff --git a/plugins/check_ntp_time.c b/plugins/check_ntp_time.c
index 164d519..22e78fb 100644
--- a/plugins/check_ntp_time.c
+++ b/plugins/check_ntp_time.c
@@ -1,6 +1,6 @@
1/****************************************************************************** 1/******************************************************************************
2* 2*
3* Nagios check_ntp plugin 3* Nagios check_ntp_time plugin
4* 4*
5* License: GPL 5* License: GPL
6* Copyright (c) 2006 sean finney <seanius@seanius.net> 6* Copyright (c) 2006 sean finney <seanius@seanius.net>
@@ -10,10 +10,14 @@
10* 10*
11* Description: 11* Description:
12* 12*
13* This file contains the check_ntp plugin 13* This file contains the check_ntp_time plugin
14* 14*
15* This plugin to check ntp servers independant of any commandline 15* This plugin checks the clock offset between the local host and a
16* programs or external libraries. 16* remote NTP server. It is independent of any commandline programs or
17* external libraries.
18*
19* If you'd rather want to monitor an NTP server, please use
20* check_ntp_peer.
17* 21*
18* 22*
19* License Information: 23* License Information:
@@ -47,16 +51,12 @@ const char *email = "nagiosplug-devel@lists.sourceforge.net";
47 51
48static char *server_address=NULL; 52static char *server_address=NULL;
49static int verbose=0; 53static int verbose=0;
50static short do_offset=0; 54static int quiet=0;
51static char *owarn="60"; 55static char *owarn="60";
52static char *ocrit="120"; 56static char *ocrit="120";
53static short do_jitter=0;
54static char *jwarn="5000";
55static char *jcrit="10000";
56 57
57int process_arguments (int, char **); 58int process_arguments (int, char **);
58thresholds *offset_thresholds = NULL; 59thresholds *offset_thresholds = NULL;
59thresholds *jitter_thresholds = NULL;
60void print_help (void); 60void print_help (void);
61void print_usage (void); 61void print_usage (void);
62 62
@@ -92,25 +92,6 @@ typedef struct {
92 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */ 92 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */
93} ntp_server_results; 93} ntp_server_results;
94 94
95/* this structure holds everything in an ntp control message as per rfc1305 */
96typedef struct {
97 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */
98 uint8_t op; /* R,E,M bits and Opcode */
99 uint16_t seq; /* Packet sequence */
100 uint16_t status; /* Clock status */
101 uint16_t assoc; /* Association */
102 uint16_t offset; /* Similar to TCP sequence # */
103 uint16_t count; /* # bytes of data */
104 char data[MAX_CM_SIZE]; /* ASCII data of the request */
105 /* NB: not necessarily NULL terminated! */
106} ntp_control_message;
107
108/* this is an association/status-word pair found in control packet reponses */
109typedef struct {
110 uint16_t assoc;
111 uint16_t status;
112} ntp_assoc_status_pair;
113
114/* bits 1,2 are the leap indicator */ 95/* bits 1,2 are the leap indicator */
115#define LI_MASK 0xc0 96#define LI_MASK 0xc0
116#define LI(x) ((x&LI_MASK)>>6) 97#define LI(x) ((x&LI_MASK)>>6)
@@ -246,42 +227,6 @@ void print_ntp_message(const ntp_message *p){
246 printf("\ttxts = %-.16g\n", NTP64asDOUBLE(p->txts)); 227 printf("\ttxts = %-.16g\n", NTP64asDOUBLE(p->txts));
247} 228}
248 229
249void print_ntp_control_message(const ntp_control_message *p){
250 int i=0, numpeers=0;
251 const ntp_assoc_status_pair *peer=NULL;
252
253 printf("control packet contents:\n");
254 printf("\tflags: 0x%.2x , 0x%.2x\n", p->flags, p->op);
255 printf("\t li=%d (0x%.2x)\n", LI(p->flags), p->flags&LI_MASK);
256 printf("\t vn=%d (0x%.2x)\n", VN(p->flags), p->flags&VN_MASK);
257 printf("\t mode=%d (0x%.2x)\n", MODE(p->flags), p->flags&MODE_MASK);
258 printf("\t response=%d (0x%.2x)\n", (p->op&REM_RESP)>0, p->op&REM_RESP);
259 printf("\t more=%d (0x%.2x)\n", (p->op&REM_MORE)>0, p->op&REM_MORE);
260 printf("\t error=%d (0x%.2x)\n", (p->op&REM_ERROR)>0, p->op&REM_ERROR);
261 printf("\t op=%d (0x%.2x)\n", p->op&OP_MASK, p->op&OP_MASK);
262 printf("\tsequence: %d (0x%.2x)\n", ntohs(p->seq), ntohs(p->seq));
263 printf("\tstatus: %d (0x%.2x)\n", ntohs(p->status), ntohs(p->status));
264 printf("\tassoc: %d (0x%.2x)\n", ntohs(p->assoc), ntohs(p->assoc));
265 printf("\toffset: %d (0x%.2x)\n", ntohs(p->offset), ntohs(p->offset));
266 printf("\tcount: %d (0x%.2x)\n", ntohs(p->count), ntohs(p->count));
267 numpeers=ntohs(p->count)/(sizeof(ntp_assoc_status_pair));
268 if(p->op&REM_RESP && p->op&OP_READSTAT){
269 peer=(ntp_assoc_status_pair*)p->data;
270 for(i=0;i<numpeers;i++){
271 printf("\tpeer id %.2x status %.2x",
272 ntohs(peer[i].assoc), ntohs(peer[i].status));
273 if (PEER_SEL(peer[i].status) >= PEER_INCLUDED){
274 if(PEER_SEL(peer[i].status) >= PEER_SYNCSOURCE){
275 printf(" <-- current sync source");
276 } else {
277 printf(" <-- current sync candidate");
278 }
279 }
280 printf("\n");
281 }
282 }
283}
284
285void setup_request(ntp_message *p){ 230void setup_request(ntp_message *p){
286 struct timeval t; 231 struct timeval t;
287 232
@@ -411,13 +356,13 @@ double offset_request(const char *host, int *status){
411 ai_tmp = ai_tmp->ai_next; 356 ai_tmp = ai_tmp->ai_next;
412 } 357 }
413 358
414 /* now do AVG_NUM checks to each host. we stop before timeout/2 seconds 359 /* now do AVG_NUM checks to each host. We stop before timeout/2 seconds
415 * have passed in order to ensure post-processing and jitter time. */ 360 * have passed in order to ensure post-processing and jitter time. */
416 now_time=start_ts=time(NULL); 361 now_time=start_ts=time(NULL);
417 while(servers_completed<num_hosts && now_time-start_ts <= socket_timeout/2){ 362 while(servers_completed<num_hosts && now_time-start_ts <= socket_timeout/2){
418 /* loop through each server and find each one which hasn't 363 /* loop through each server and find each one which hasn't
419 * been touched in the past second or so and is still lacking 364 * been touched in the past second or so and is still lacking
420 * some responses. for each of these servers, send a new request, 365 * some responses. For each of these servers, send a new request,
421 * and update the "waiting" timestamp with the current time. */ 366 * and update the "waiting" timestamp with the current time. */
422 one_written=0; 367 one_written=0;
423 now_time=time(NULL); 368 now_time=time(NULL);
@@ -486,10 +431,7 @@ double offset_request(const char *host, int *status){
486 } 431 }
487 432
488 /* cleanup */ 433 /* cleanup */
489 /* FIXME: Not closing the socket to avoid re-use of the local port 434 for(j=0; j<num_hosts; j++){ close(socklist[j]); }
490 * which can cause old NTP packets to be read instead of NTP control
491 * pactets in jitter_request(). THERE MUST BE ANOTHER WAY...
492 * for(j=0; j<num_hosts; j++){ close(socklist[j]); } */
493 free(socklist); 435 free(socklist);
494 free(ufds); 436 free(ufds);
495 free(servers); 437 free(servers);
@@ -500,155 +442,6 @@ double offset_request(const char *host, int *status){
500 return avg_offset; 442 return avg_offset;
501} 443}
502 444
503void
504setup_control_request(ntp_control_message *p, uint8_t opcode, uint16_t seq){
505 memset(p, 0, sizeof(ntp_control_message));
506 LI_SET(p->flags, LI_NOWARNING);
507 VN_SET(p->flags, VN_RESERVED);
508 MODE_SET(p->flags, MODE_CONTROLMSG);
509 OP_SET(p->op, opcode);
510 p->seq = htons(seq);
511 /* Remaining fields are zero for requests */
512}
513
514/* XXX handle responses with the error bit set */
515double jitter_request(const char *host, int *status){
516 int conn=-1, i, npeers=0, num_candidates=0, syncsource_found=0;
517 int run=0, min_peer_sel=PEER_INCLUDED, num_selected=0, num_valid=0;
518 int peers_size=0, peer_offset=0;
519 ntp_assoc_status_pair *peers=NULL;
520 ntp_control_message req;
521 const char *getvar = "jitter";
522 double rval = 0.0, jitter = -1.0;
523 char *startofvalue=NULL, *nptr=NULL;
524 void *tmp;
525
526 /* Long-winded explanation:
527 * Getting the jitter requires a number of steps:
528 * 1) Send a READSTAT request.
529 * 2) Interpret the READSTAT reply
530 * a) The data section contains a list of peer identifiers (16 bits)
531 * and associated status words (16 bits)
532 * b) We want the value of 0x06 in the SEL (peer selection) value,
533 * which means "current synchronizatin source". If that's missing,
534 * we take anything better than 0x04 (see the rfc for details) but
535 * set a minimum of warning.
536 * 3) Send a READVAR request for information on each peer identified
537 * in 2b greater than the minimum selection value.
538 * 4) Extract the jitter value from the data[] (it's ASCII)
539 */
540 my_udp_connect(server_address, 123, &conn);
541
542 /* keep sending requests until the server stops setting the
543 * REM_MORE bit, though usually this is only 1 packet. */
544 do{
545 setup_control_request(&req, OP_READSTAT, 1);
546 DBG(printf("sending READSTAT request"));
547 write(conn, &req, SIZEOF_NTPCM(req));
548 DBG(print_ntp_control_message(&req));
549 /* Attempt to read the largest size packet possible */
550 req.count=htons(MAX_CM_SIZE);
551 DBG(printf("recieving READSTAT response"))
552 read(conn, &req, SIZEOF_NTPCM(req));
553 DBG(print_ntp_control_message(&req));
554 /* Each peer identifier is 4 bytes in the data section, which
555 * we represent as a ntp_assoc_status_pair datatype.
556 */
557 peers_size+=ntohs(req.count);
558 if((tmp=realloc(peers, peers_size)) == NULL)
559 free(peers), die(STATE_UNKNOWN, "can not (re)allocate 'peers' buffer\n");
560 peers=tmp;
561 memcpy((void*)((ptrdiff_t)peers+peer_offset), (void*)req.data, ntohs(req.count));
562 npeers=peers_size/sizeof(ntp_assoc_status_pair);
563 peer_offset+=ntohs(req.count);
564 } while(req.op&REM_MORE);
565
566 /* first, let's find out if we have a sync source, or if there are
567 * at least some candidates. in the case of the latter we'll issue
568 * a warning but go ahead with the check on them. */
569 for (i = 0; i < npeers; i++){
570 if (PEER_SEL(peers[i].status) >= PEER_INCLUDED){
571 num_candidates++;
572 if(PEER_SEL(peers[i].status) >= PEER_SYNCSOURCE){
573 syncsource_found=1;
574 min_peer_sel=PEER_SYNCSOURCE;
575 }
576 }
577 }
578 if(verbose) printf("%d candiate peers available\n", num_candidates);
579 if(verbose && syncsource_found) printf("synchronization source found\n");
580 if(! syncsource_found){
581 *status = STATE_UNKNOWN;
582 if(verbose) printf("warning: no synchronization source found\n");
583 }
584
585
586 for (run=0; run<AVG_NUM; run++){
587 if(verbose) printf("jitter run %d of %d\n", run+1, AVG_NUM);
588 for (i = 0; i < npeers; i++){
589 /* Only query this server if it is the current sync source */
590 if (PEER_SEL(peers[i].status) >= min_peer_sel){
591 num_selected++;
592 setup_control_request(&req, OP_READVAR, 2);
593 req.assoc = peers[i].assoc;
594 /* By spec, putting the variable name "jitter" in the request
595 * should cause the server to provide _only_ the jitter value.
596 * thus reducing net traffic, guaranteeing us only a single
597 * datagram in reply, and making intepretation much simpler
598 */
599 /* Older servers doesn't know what jitter is, so if we get an
600 * error on the first pass we redo it with "dispersion" */
601 strncpy(req.data, getvar, MAX_CM_SIZE-1);
602 req.count = htons(strlen(getvar));
603 DBG(printf("sending READVAR request...\n"));
604 write(conn, &req, SIZEOF_NTPCM(req));
605 DBG(print_ntp_control_message(&req));
606
607 req.count = htons(MAX_CM_SIZE);
608 DBG(printf("recieving READVAR response...\n"));
609 read(conn, &req, SIZEOF_NTPCM(req));
610 DBG(print_ntp_control_message(&req));
611
612 if(req.op&REM_ERROR && strstr(getvar, "jitter")) {
613 if(verbose) printf("The 'jitter' command failed (old ntp server?)\nRestarting with 'dispersion'...\n");
614 getvar = "dispersion";
615 num_selected--;
616 i--;
617 continue;
618 }
619
620 /* get to the float value */
621 if(verbose) {
622 printf("parsing jitter from peer %.2x: ", ntohs(peers[i].assoc));
623 }
624 startofvalue = strchr(req.data, '=');
625 if(startofvalue != NULL) {
626 startofvalue++;
627 jitter = strtod(startofvalue, &nptr);
628 }
629 if(startofvalue == NULL || startofvalue==nptr){
630 printf("warning: unable to read server jitter response.\n");
631 *status = STATE_UNKNOWN;
632 } else {
633 if(verbose) printf("%g\n", jitter);
634 num_valid++;
635 rval += jitter;
636 }
637 }
638 }
639 if(verbose){
640 printf("jitter parsed from %d/%d peers\n", num_valid, num_selected);
641 }
642 }
643
644 rval = num_valid ? rval / num_valid : -1.0;
645
646 close(conn);
647 if(peers!=NULL) free(peers);
648 /* If we return -1.0, it means no synchronization source was found */
649 return rval;
650}
651
652int process_arguments(int argc, char **argv){ 445int process_arguments(int argc, char **argv){
653 int c; 446 int c;
654 int option=0; 447 int option=0;
@@ -658,10 +451,9 @@ int process_arguments(int argc, char **argv){
658 {"verbose", no_argument, 0, 'v'}, 451 {"verbose", no_argument, 0, 'v'},
659 {"use-ipv4", no_argument, 0, '4'}, 452 {"use-ipv4", no_argument, 0, '4'},
660 {"use-ipv6", no_argument, 0, '6'}, 453 {"use-ipv6", no_argument, 0, '6'},
454 {"quiet", no_argument, 0, 'q'},
661 {"warning", required_argument, 0, 'w'}, 455 {"warning", required_argument, 0, 'w'},
662 {"critical", required_argument, 0, 'c'}, 456 {"critical", required_argument, 0, 'c'},
663 {"jwarn", required_argument, 0, 'j'},
664 {"jcrit", required_argument, 0, 'k'},
665 {"timeout", required_argument, 0, 't'}, 457 {"timeout", required_argument, 0, 't'},
666 {"hostname", required_argument, 0, 'H'}, 458 {"hostname", required_argument, 0, 'H'},
667 {0, 0, 0, 0} 459 {0, 0, 0, 0}
@@ -672,7 +464,7 @@ int process_arguments(int argc, char **argv){
672 usage ("\n"); 464 usage ("\n");
673 465
674 while (1) { 466 while (1) {
675 c = getopt_long (argc, argv, "Vhv46w:c:j:k:t:H:", longopts, &option); 467 c = getopt_long (argc, argv, "Vhv46qw:c:t:H:", longopts, &option);
676 if (c == -1 || c == EOF || c == 1) 468 if (c == -1 || c == EOF || c == 1)
677 break; 469 break;
678 470
@@ -688,22 +480,15 @@ int process_arguments(int argc, char **argv){
688 case 'v': 480 case 'v':
689 verbose++; 481 verbose++;
690 break; 482 break;
483 case 'q':
484 quiet = 1;
485 break;
691 case 'w': 486 case 'w':
692 do_offset=1;
693 owarn = optarg; 487 owarn = optarg;
694 break; 488 break;
695 case 'c': 489 case 'c':
696 do_offset=1;
697 ocrit = optarg; 490 ocrit = optarg;
698 break; 491 break;
699 case 'j':
700 do_jitter=1;
701 jwarn = optarg;
702 break;
703 case 'k':
704 do_jitter=1;
705 jcrit = optarg;
706 break;
707 case 'H': 492 case 'H':
708 if(is_host(optarg) == FALSE) 493 if(is_host(optarg) == FALSE)
709 usage2(_("Invalid hostname/address"), optarg); 494 usage2(_("Invalid hostname/address"), optarg);
@@ -744,26 +529,17 @@ char *perfd_offset (double offset)
744 FALSE, 0, FALSE, 0); 529 FALSE, 0, FALSE, 0);
745} 530}
746 531
747char *perfd_jitter (double jitter)
748{
749 return fperfdata ("jitter", jitter, "s",
750 do_jitter, jitter_thresholds->warning->end,
751 do_jitter, jitter_thresholds->critical->end,
752 TRUE, 0, FALSE, 0);
753}
754
755int main(int argc, char *argv[]){ 532int main(int argc, char *argv[]){
756 int result, offset_result, jitter_result; 533 int result, offset_result;
757 double offset=0, jitter=0; 534 double offset=0;
758 char *result_line, *perfdata_line; 535 char *result_line, *perfdata_line;
759 536
760 result = offset_result = jitter_result = STATE_OK; 537 result = offset_result = STATE_OK;
761 538
762 if (process_arguments (argc, argv) == ERROR) 539 if (process_arguments (argc, argv) == ERROR)
763 usage4 (_("Could not parse arguments")); 540 usage4 (_("Could not parse arguments"));
764 541
765 set_thresholds(&offset_thresholds, owarn, ocrit); 542 set_thresholds(&offset_thresholds, owarn, ocrit);
766 set_thresholds(&jitter_thresholds, jwarn, jcrit);
767 543
768 /* initialize alarm signal handling */ 544 /* initialize alarm signal handling */
769 signal (SIGALRM, socket_timeout_alarm_handler); 545 signal (SIGALRM, socket_timeout_alarm_handler);
@@ -772,29 +548,12 @@ int main(int argc, char *argv[]){
772 alarm (socket_timeout); 548 alarm (socket_timeout);
773 549
774 offset = offset_request(server_address, &offset_result); 550 offset = offset_request(server_address, &offset_result);
775 /* check_ntp used to always return CRITICAL if offset_result == STATE_UNKNOWN. 551 if (offset_result == STATE_UNKNOWN) {
776 * Now we'll only do that is the offset thresholds were set */ 552 result = (quiet == 1 ? STATE_UNKNOWN : STATE_CRITICAL);
777 if (do_offset && offset_result == STATE_UNKNOWN) {
778 result = STATE_CRITICAL;
779 } else { 553 } else {
780 result = get_status(fabs(offset), offset_thresholds); 554 result = get_status(fabs(offset), offset_thresholds);
781 } 555 }
782 556
783 /* If not told to check the jitter, we don't even send packets.
784 * jitter is checked using NTP control packets, which not all
785 * servers recognize. Trying to check the jitter on OpenNTPD
786 * (for example) will result in an error
787 */
788 if(do_jitter){
789 jitter=jitter_request(server_address, &jitter_result);
790 result = max_state_alt(result, get_status(jitter, jitter_thresholds));
791 /* -1 indicates that we couldn't calculate the jitter
792 * Only overrides STATE_OK from the offset */
793 if(jitter == -1.0 && result == STATE_OK)
794 result = STATE_UNKNOWN;
795 }
796 result = max_state_alt(result, jitter_result);
797
798 switch (result) { 557 switch (result) {
799 case STATE_CRITICAL : 558 case STATE_CRITICAL :
800 asprintf(&result_line, "NTP CRITICAL:"); 559 asprintf(&result_line, "NTP CRITICAL:");
@@ -816,55 +575,49 @@ int main(int argc, char *argv[]){
816 asprintf(&result_line, "%s Offset %.10g secs", result_line, offset); 575 asprintf(&result_line, "%s Offset %.10g secs", result_line, offset);
817 asprintf(&perfdata_line, "%s", perfd_offset(offset)); 576 asprintf(&perfdata_line, "%s", perfd_offset(offset));
818 } 577 }
819 if (do_jitter) {
820 asprintf(&result_line, "%s, jitter=%f", result_line, jitter);
821 asprintf(&perfdata_line, "%s %s", perfdata_line, perfd_jitter(jitter));
822 }
823 printf("%s|%s\n", result_line, perfdata_line); 578 printf("%s|%s\n", result_line, perfdata_line);
824 579
825 if(server_address!=NULL) free(server_address); 580 if(server_address!=NULL) free(server_address);
826 return result; 581 return result;
827} 582}
828 583
829
830
831void print_help(void){ 584void print_help(void){
832 print_revision(progname, revision); 585 print_revision(progname, revision);
833 586
834 printf ("Copyright (c) 2006 Sean Finney\n"); 587 printf ("Copyright (c) 2006 Sean Finney\n");
835 printf (COPYRIGHT, copyright, email); 588 printf (COPYRIGHT, copyright, email);
836
837 printf ("%s\n", _("This plugin checks the selected ntp server"));
838 589
839 printf ("\n\n"); 590 printf ("%s\n", _("This plugin checks the clock offset with the ntp server"));
840 591
592 printf ("\n\n");
593
841 print_usage(); 594 print_usage();
842 printf (_(UT_HELP_VRSN)); 595 printf (_(UT_HELP_VRSN));
843 printf (_(UT_HOST_PORT), 'p', "123"); 596 printf (_(UT_HOST_PORT), 'p', "123");
597 printf (" %s\n", "-q, --quiet");
598 printf (" %s\n", _("Returns UNKNOWN instead of CRITICAL if offset cannot be found"));
844 printf (" %s\n", "-w, --warning=THRESHOLD"); 599 printf (" %s\n", "-w, --warning=THRESHOLD");
845 printf (" %s\n", _("Offset to result in warning status (seconds)")); 600 printf (" %s\n", _("Offset to result in warning status (seconds)"));
846 printf (" %s\n", "-c, --critical=THRESHOLD"); 601 printf (" %s\n", "-c, --critical=THRESHOLD");
847 printf (" %s\n", _("Offset to result in critical status (seconds)")); 602 printf (" %s\n", _("Offset to result in critical status (seconds)"));
848 printf (" %s\n", "-j, --warning=THRESHOLD");
849 printf (" %s\n", _("Warning threshold for jitter"));
850 printf (" %s\n", "-k, --critical=THRESHOLD");
851 printf (" %s\n", _("Critical threshold for jitter"));
852 printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT); 603 printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
853 printf (_(UT_VERBOSE)); 604 printf (_(UT_VERBOSE));
854 605
855 printf("\n"); 606 printf("\n");
856 printf("%s\n", _("Notes:")); 607 printf("%s\n", _("Notes:"));
608 printf(" %s\n", _("This plugin checks the clock offset between the local host and a"));
609 printf(" %s\n", _("remote NTP server. It is independent of any commandline programs or"));
610 printf(" %s\n\n", _("external libraries."));
611 printf(" %s\n", _("If you'd rather want to monitor an NTP server, please use"));
612 printf(" %s\n\n", _("check_ntp_peer."));
613
857 printf(" %s\n", _("See:")); 614 printf(" %s\n", _("See:"));
858 printf(" %s\n", ("http://nagiosplug.sourceforge.net/developer-guidelines.html#THRESHOLDFORMAT")); 615 printf(" %s\n", ("http://nagiosplug.sourceforge.net/developer-guidelines.html#THRESHOLDFORMAT"));
859 printf(" %s\n", _("for THRESHOLD format and examples.")); 616 printf(" %s\n", _("for THRESHOLD format and examples."));
860 617
861 printf("\n"); 618 printf("\n");
862 printf("%s\n", _("Examples:")); 619 printf("%s\n", _("Examples:"));
863 printf(" %s\n", _("Normal offset check:")); 620 printf(" %s\n", ("./check_ntp_time -H ntpserv -w 0.5 -c 1"));
864 printf(" %s\n", ("./check_ntp -H ntpserv -w 0.5 -c 1"));
865 printf(" %s\n", _("Check jitter too, avoiding critical notifications if jitter isn't available"));
866 printf(" %s\n", _("(See Notes above for more details on thresholds formats):"));
867 printf(" %s\n", ("./check_ntp -H ntpserv -w 0.5 -c 1 -j -1:100 -k -1:200"));
868 621
869 printf (_(UT_SUPPORT)); 622 printf (_(UT_SUPPORT));
870} 623}
@@ -872,6 +625,8 @@ void print_help(void){
872void 625void
873print_usage(void) 626print_usage(void)
874{ 627{
875 printf (_("Usage:")); 628 printf (_("Usage:"));
876 printf(" %s -H <host> [-w <warn>] [-c <crit>] [-j <warn>] [-k <crit>] [-v verbose]\n", progname); 629 printf(" %s -H <host> [-w <warn>] [-c <crit>] [-W <warn>] [-C <crit>]\n", progname);
630 printf(" [-j <warn>] [-k <crit>] [-v verbose]\n");
877} 631}
632
diff --git a/plugins/t/check_ntp.t b/plugins/t/check_ntp.t
index 6e222a3..ae7f036 100644
--- a/plugins/t/check_ntp.t
+++ b/plugins/t/check_ntp.t
@@ -9,7 +9,10 @@ use strict;
9use Test::More; 9use Test::More;
10use NPTest; 10use NPTest;
11 11
12plan tests => 4; 12my @PLUGINS1 = ('check_ntp', 'check_ntp_peer', 'check_ntp_time');
13my @PLUGINS2 = ('check_ntp_peer');
14
15plan tests => (12 * scalar(@PLUGINS1)) + (6 * scalar(@PLUGINS2));
13 16
14my $res; 17my $res;
15 18
@@ -25,33 +28,84 @@ my $host_nonresponsive = getTestParameter( "NP_HOST_NONRESPONSIVE",
25 "The hostname of system not responsive to network requests", 28 "The hostname of system not responsive to network requests",
26 "10.0.0.1" ); 29 "10.0.0.1" );
27 30
28my $hostname_invalid = getTestParameter( "NP_HOSTNAME_INVALID", 31my $hostname_invalid = getTestParameter( "NP_HOSTNAME_INVALID",
29 "An invalid (not known to DNS) hostname", 32 "An invalid (not known to DNS) hostname",
30 "nosuchhost"); 33 "nosuchhost");
31 34
32SKIP: { 35my $ntp_okmatch1 = '/^NTP\sOK:\sOffset\s-?[0-9]+(\.[0-9]+)?(e-[0-9]{2})?\ssecs/';
33 skip "No NTP server defined", 1 unless $ntp_service; 36my $ntp_warnmatch1 = '/^NTP\sWARNING:\sOffset\s-?[0-9]+(\.[0-9]+)?(e-[0-9]{2})?\ssecs/';
37my $ntp_critmatch1 = '/^NTP\sCRITICAL:\sOffset\s-?[0-9]+(\.[0-9]+)?(e-[0-9]{2})?\ssecs/';
38my $ntp_okmatch2 = '/^NTP\sOK:\sOffset\s-?[0-9]+(\.[0-9]+)?(e-[0-9]{2})?\ssecs,\sjitter=[0-9]+\.[0-9]+,\sstratum=[0-9]{1,2}/';
39my $ntp_warnmatch2 = '/^NTP\sWARNING:\sOffset\s-?[0-9]+(\.[0-9]+)?(e-[0-9]{2})?\ssecs,\sjitter=[0-9]+\.[0-9]+,\sstratum=[0-9]{1,2}/';
40my $ntp_critmatch2 = '/^NTP\sCRITICAL:\sOffset\s-?[0-9]+(\.[0-9]+)?(e-[0-9]{2})?\ssecs,\sjitter=[0-9]+\.[0-9]+,\sstratum=[0-9]{1,2}/';
41my $ntp_noresponse = '/^(CRITICAL - Socket timeout after 3 seconds)|(NTP CRITICAL: No response from NTP server)$/';
42my $ntp_nosuchhost = '/^check_ntp.*: Invalid hostname/address - ' . $hostname_invalid . '/';
43
44
45foreach my $plugin (@PLUGINS1) {
46 SKIP: {
47 skip "No NTP server defined", 1 unless $ntp_service;
48 $res = NPTest->testCmd(
49 "./$plugin -H $ntp_service -w 1000 -c 2000"
50 );
51 cmp_ok( $res->return_code, '==', 0, "$plugin: Good NTP result (simple check)" );
52 like( $res->output, $ntp_okmatch1, "$plugin: Output match OK (simple check)" );
53
54 $res = NPTest->testCmd(
55 "./$plugin -H $ntp_service -w 1000: -c 2000"
56 );
57 cmp_ok( $res->return_code, '==', 1, "$plugin: Warning NTP result (simple check)" );
58 like( $res->output, $ntp_warnmatch1, "$plugin: Output match WARNING (simple check)" );
59
60 $res = NPTest->testCmd(
61 "./$plugin -H $ntp_service -w 1000 -c 2000:"
62 );
63 cmp_ok( $res->return_code, '==', 2, "$plugin: Critical NTP result (simple check)" );
64 like( $res->output, $ntp_critmatch1, "$plugin: Output match CRITICAL (simple check)" );
65 }
66
67 SKIP: {
68 skip "No bad NTP server defined", 1 unless $no_ntp_service;
69 $res = NPTest->testCmd(
70 "./$plugin -H $no_ntp_service -t 3"
71 );
72 cmp_ok( $res->return_code, '==', 2, "$plugin: No NTP service" );
73 like( $res->output, $ntp_noresponse, "$plugin: Output match no NTP service" );
74 }
75
34 $res = NPTest->testCmd( 76 $res = NPTest->testCmd(
35 "./check_ntp -H $ntp_service" 77 "./$plugin -H $host_nonresponsive -t 3"
36 ); 78 );
37 cmp_ok( $res->return_code, '==', 0, "Got good NTP result"); 79 cmp_ok( $res->return_code, '==', 2, "$plugin: Server not responding" );
38} 80 like( $res->output, $ntp_noresponse, "$plugin: Output match non-responsive" );
39 81
40SKIP: {
41 skip "No bad NTP server defined", 1 unless $no_ntp_service;
42 $res = NPTest->testCmd( 82 $res = NPTest->testCmd(
43 "./check_ntp -H $no_ntp_service" 83 "./$plugin -H $hostname_invalid"
44 ); 84 );
45 cmp_ok( $res->return_code, '==', 2, "Got bad NTP result"); 85 cmp_ok( $res->return_code, '==', 3, "$plugin: Invalid hostname/address" );
86 like( $res->output, $ntp_nosuchhost, "$plugin: Output match invalid hostname/address" );
87
46} 88}
47 89
48$res = NPTest->testCmd( 90foreach my $plugin (@PLUGINS2) {
49 "./check_ntp -H $host_nonresponsive" 91 SKIP: {
50 ); 92 skip "No NTP server defined", 1 unless $ntp_service;
51cmp_ok( $res->return_code, '==', 2, "Got critical if server not responding"); 93 $res = NPTest->testCmd(
94 "./$plugin -H $ntp_service -w 1000 -c 2000 -W 20 -C 21 -j 100000 -k 200000"
95 );
96 cmp_ok( $res->return_code, '==', 0, "$plugin: Good NTP result with jitter and stratum check" );
97 like( $res->output, $ntp_okmatch2, "$plugin: Output match OK with jitter and stratum" );
52 98
53$res = NPTest->testCmd( 99 $res = NPTest->testCmd(
54 "./check_ntp -H $hostname_invalid" 100 "./$plugin -H $ntp_service -w 1000 -c 2000 -W ~:-1 -C 21 -j 100000 -k 200000"
55 ); 101 );
56cmp_ok( $res->return_code, '==', 3, "Got critical if server hostname invalid"); 102 cmp_ok( $res->return_code, '==', 1, "$plugin: Warning NTP result with jitter and stratum check" );
103 like( $res->output, $ntp_warnmatch2, "$plugin: Output match WARNING with jitter and stratum" );
57 104
105 $res = NPTest->testCmd(
106 "./$plugin -H $ntp_service -w 1000 -c 2000 -W 20 -C 21 -j 100000 -k ~:-1"
107 );
108 cmp_ok( $res->return_code, '==', 2, "$plugin: Critical NTP result with jitter and stratum check" );
109 like( $res->output, $ntp_critmatch2, "$plugin: Output match CRITICAL with jitter and stratum" );
110 }
111}