X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=plugins%2Fcheck_ntp.c;h=ac89def77624392e136b473f6d30cb7559149551;hb=1a5a83bb82c35d888229fe9f815fbc663c0f4d3c;hp=2954aa888e9719c35a68267e649353cee291a64e;hpb=077a1f6a5149a297f60675516f21be9630eaf25e;p=nagiosplug.git diff --git a/plugins/check_ntp.c b/plugins/check_ntp.c index 2954aa8..ac89def 100644 --- a/plugins/check_ntp.c +++ b/plugins/check_ntp.c @@ -1,29 +1,37 @@ -/****************************************************************************** - check_ntp.c: utility to check ntp servers independant of any commandline - programs or external libraries. - original author: sean finney - ****************************************************************************** - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - $Id$ - +/***************************************************************************** +* +* Nagios check_ntp plugin +* +* License: GPL +* Copyright (c) 2006 Sean Finney +* Copyright (c) 2006-2008 Nagios Plugins Development Team +* +* Description: +* +* This file contains the check_ntp plugin +* +* This plugin to check ntp servers independant of any commandline +* programs or external libraries. +* +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* *****************************************************************************/ const char *progname = "check_ntp"; -const char *revision = "$Revision$"; -const char *copyright = "2006"; +const char *copyright = "2006-2008"; const char *email = "nagiosplug-devel@lists.sourceforge.net"; #include "common.h" @@ -32,14 +40,16 @@ const char *email = "nagiosplug-devel@lists.sourceforge.net"; static char *server_address=NULL; static int verbose=0; -static int zero_offset_bad=0; -static double owarn=0; -static double ocrit=0; +static short do_offset=0; +static char *owarn="60"; +static char *ocrit="120"; static short do_jitter=0; -static double jwarn=0; -static double jcrit=0; +static char *jwarn="5000"; +static char *jcrit="10000"; int process_arguments (int, char **); +thresholds *offset_thresholds = NULL; +thresholds *jitter_thresholds = NULL; void print_help (void); void print_usage (void); @@ -64,6 +74,17 @@ typedef struct { uint64_t txts; /* time at which request departed server */ } ntp_message; +/* this structure holds data about results from querying offset from a peer */ +typedef struct { + time_t waiting; /* ts set when we started waiting for a response */ + int num_responses; /* number of successfully recieved responses */ + uint8_t stratum; /* copied verbatim from the ntp_message */ + double rtdelay; /* converted from the ntp_message */ + double rtdisp; /* converted from the ntp_message */ + double offset[AVG_NUM]; /* offsets from each response */ + uint8_t flags; /* byte with leapindicator,vers,mode. see macros */ +} ntp_server_results; + /* this structure holds everything in an ntp control message as per rfc1305 */ typedef struct { uint8_t flags; /* byte with leapindicator,vers,mode. see macros */ @@ -114,8 +135,8 @@ typedef struct { #define OP_SET(x,y) do{ x |= (y&OP_MASK); }while(0) #define OP_READSTAT 0x01 #define OP_READVAR 0x02 -/* In peer status bytes, bytes 6,7,8 determine clock selection status */ -#define PEER_SEL(x) (x&0x07) +/* In peer status bytes, bits 6,7,8 determine clock selection status */ +#define PEER_SEL(x) ((ntohs(x)>>8)&0x07) #define PEER_INCLUDED 0x04 #define PEER_SYNCSOURCE 0x06 @@ -125,7 +146,7 @@ typedef struct { they are divided into halves, each being a 16-bit int in network byte order: - the first 16 bits are an int on the left side of a decimal point. - the second 16 bits represent a fraction n/(2^16) - likewise for the 64-bit "fixed point" numbers with everything doubled :) + likewise for the 64-bit "fixed point" numbers with everything doubled :) **/ /* macros to access the left/right 16 bits of a 32-bit ntp "fixed point" @@ -166,14 +187,14 @@ typedef struct { do{ if(!t.tv_usec && !t.tv_sec) n=0x0UL; \ else { \ L32(n)=htonl(t.tv_sec + EPOCHDIFF); \ - R32(n)=htonl((4294.967296*t.tv_usec)+.5); \ + R32(n)=htonl((uint64_t)((4294.967296*t.tv_usec)+.5)); \ } \ } while(0) /* NTP control message header is 12 bytes, plus any data in the data * field, plus null padding to the nearest 32-bit boundary per rfc. */ -#define SIZEOF_NTPCM(m) (12+ntohs(m.count)+((m.count)?4-(ntohs(m.count)%4):0)) +#define SIZEOF_NTPCM(m) (12+ntohs(m.count)+((ntohs(m.count)%4)?4-(ntohs(m.count)%4):0)) /* finally, a little helper or two for debugging: */ #define DBG(x) do{if(verbose>1){ x; }}while(0); @@ -184,13 +205,12 @@ typedef struct { /* calculate the offset of the local clock */ static inline double calc_offset(const ntp_message *m, const struct timeval *t){ - double client_tx, peer_rx, peer_tx, client_rx, rtdelay; + double client_tx, peer_rx, peer_tx, client_rx; client_tx = NTP64asDOUBLE(m->origts); peer_rx = NTP64asDOUBLE(m->rxts); peer_tx = NTP64asDOUBLE(m->txts); client_rx=TVasDOUBLE((*t)); - rtdelay=NTP32asDOUBLE(m->rtdelay); - return (.5*((peer_tx-client_rx)+(peer_rx-client_tx)))-rtdelay; + return (.5*((peer_tx-client_rx)+(peer_rx-client_tx))); } /* print out a ntp packet in human readable/debuggable format */ @@ -241,7 +261,7 @@ void print_ntp_control_message(const ntp_control_message *p){ if(p->op&REM_RESP && p->op&OP_READSTAT){ peer=(ntp_assoc_status_pair*)p->data; for(i=0;i= PEER_INCLUDED){ if(PEER_SEL(peer[i].status) >= PEER_SYNCSOURCE){ @@ -263,7 +283,7 @@ void setup_request(ntp_message *p){ VN_SET(p->flags, 4); MODE_SET(p->flags, MODE_CLIENT); p->poll=4; - p->precision=0xfa; + p->precision=(int8_t)0xfa; L16(p->rtdelay)=htons(1); L16(p->rtdisp)=htons(1); @@ -271,27 +291,208 @@ void setup_request(ntp_message *p){ TVtoNTP64(t,p->txts); } -double offset_request(const char *host){ - int i=0, conn=-1; - ntp_message req; - double next_offset=0., avg_offset=0.; +/* select the "best" server from a list of servers, and return its index. + * this is done by filtering servers based on stratum, dispersion, and + * finally round-trip delay. */ +int best_offset_server(const ntp_server_results *slist, int nservers){ + int i=0, cserver=0, best_server=-1; + + /* for each server */ + for(cserver=0; cserver= 0) { + DBG(printf("best server selected: peer %d\n", best_server)); + return best_server; + } else { + DBG(printf("no peers meeting synchronization criteria :(\n")); + return -1; + } +} + +/* do everything we need to get the total average offset + * - we use a certain amount of parallelization with poll() to ensure + * we don't waste time sitting around waiting for single packets. + * - we also "manually" handle resolving host names and connecting, because + * we have to do it in a way that our lazy macros don't handle currently :( */ +double offset_request(const char *host, int *status){ + int i=0, j=0, ga_result=0, num_hosts=0, *socklist=NULL, respnum=0; + int servers_completed=0, one_written=0, one_read=0, servers_readable=0, best_index=-1; + time_t now_time=0, start_ts=0; + ntp_message *req=NULL; + double avg_offset=0.; struct timeval recv_time; + struct addrinfo *ai=NULL, *ai_tmp=NULL, hints; + struct pollfd *ufds=NULL; + ntp_server_results *servers=NULL; + + /* setup hints to only return results from getaddrinfo that we'd like */ + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = address_family; + hints.ai_protocol = IPPROTO_UDP; + hints.ai_socktype = SOCK_DGRAM; + + /* fill in ai with the list of hosts resolved by the host name */ + ga_result = getaddrinfo(host, "123", &hints, &ai); + if(ga_result!=0){ + die(STATE_UNKNOWN, "error getting address for %s: %s\n", + host, gai_strerror(ga_result)); + } + + /* count the number of returned hosts, and allocate stuff accordingly */ + for(ai_tmp=ai; ai_tmp!=NULL; ai_tmp=ai_tmp->ai_next){ num_hosts++; } + req=(ntp_message*)malloc(sizeof(ntp_message)*num_hosts); + if(req==NULL) die(STATE_UNKNOWN, "can not allocate ntp message array"); + socklist=(int*)malloc(sizeof(int)*num_hosts); + if(socklist==NULL) die(STATE_UNKNOWN, "can not allocate socket array"); + ufds=(struct pollfd*)malloc(sizeof(struct pollfd)*num_hosts); + if(ufds==NULL) die(STATE_UNKNOWN, "can not allocate socket array"); + servers=(ntp_server_results*)malloc(sizeof(ntp_server_results)*num_hosts); + if(servers==NULL) die(STATE_UNKNOWN, "can not allocate server array"); + memset(servers, 0, sizeof(ntp_server_results)*num_hosts); + DBG(printf("Found %d peers to check\n", num_hosts)); + + /* setup each socket for writing, and the corresponding struct pollfd */ + ai_tmp=ai; + for(i=0;ai_tmp;i++){ + socklist[i]=socket(ai_tmp->ai_family, SOCK_DGRAM, IPPROTO_UDP); + if(socklist[i] == -1) { + perror(NULL); + die(STATE_UNKNOWN, "can not create new socket"); + } + if(connect(socklist[i], ai_tmp->ai_addr, ai_tmp->ai_addrlen)){ + die(STATE_UNKNOWN, "can't create socket connection"); + } else { + ufds[i].fd=socklist[i]; + ufds[i].events=POLLIN; + ufds[i].revents=0; + } + ai_tmp = ai_tmp->ai_next; + } + + /* now do AVG_NUM checks to each host. we stop before timeout/2 seconds + * have passed in order to ensure post-processing and jitter time. */ + now_time=start_ts=time(NULL); + while(servers_completed= min_peer_sel){ + num_selected++; setup_control_request(&req, OP_READVAR, 2); req.assoc = peers[i].assoc; /* By spec, putting the variable name "jitter" in the request @@ -379,8 +592,10 @@ double jitter_request(const char *host){ * thus reducing net traffic, guaranteeing us only a single * datagram in reply, and making intepretation much simpler */ - strncpy(req.data, "jitter", 6); - req.count = htons(6); + /* Older servers doesn't know what jitter is, so if we get an + * error on the first pass we redo it with "dispersion" */ + strncpy(req.data, getvar, MAX_CM_SIZE-1); + req.count = htons(strlen(getvar)); DBG(printf("sending READVAR request...\n")); write(conn, &req, SIZEOF_NTPCM(req)); DBG(print_ntp_control_message(&req)); @@ -390,16 +605,26 @@ double jitter_request(const char *host){ read(conn, &req, SIZEOF_NTPCM(req)); DBG(print_ntp_control_message(&req)); + if(req.op&REM_ERROR && strstr(getvar, "jitter")) { + if(verbose) printf("The 'jitter' command failed (old ntp server?)\nRestarting with 'dispersion'...\n"); + getvar = "dispersion"; + num_selected--; + i--; + continue; + } + /* get to the float value */ if(verbose) { - printf("parsing jitter from peer %.2x: ", peers[i].assoc); + printf("parsing jitter from peer %.2x: ", ntohs(peers[i].assoc)); } - startofvalue = strchr(req.data, '=') + 1; - jitter = strtod(startofvalue, &nptr); - num_selected++; - if(jitter == 0 && startofvalue==nptr){ - printf("warning: unable to parse server response.\n"); - /* XXX errors value ... */ + startofvalue = strchr(req.data, '='); + if(startofvalue != NULL) { + startofvalue++; + jitter = strtod(startofvalue, &nptr); + } + if(startofvalue == NULL || startofvalue==nptr){ + printf("warning: unable to read server jitter response.\n"); + *status = STATE_UNKNOWN; } else { if(verbose) printf("%g\n", jitter); num_valid++; @@ -408,14 +633,14 @@ double jitter_request(const char *host){ } } if(verbose){ - printf("jitter parsed from %d/%d peers\n", num_selected, num_valid); + printf("jitter parsed from %d/%d peers\n", num_valid, num_selected); } } - rval /= num_valid; + rval = num_valid ? rval / num_valid : -1.0; close(conn); - free(peers); + if(peers!=NULL) free(peers); /* If we return -1.0, it means no synchronization source was found */ return rval; } @@ -431,7 +656,6 @@ int process_arguments(int argc, char **argv){ {"use-ipv6", no_argument, 0, '6'}, {"warning", required_argument, 0, 'w'}, {"critical", required_argument, 0, 'c'}, - {"zero-offset", no_argument, 0, 'O'}, {"jwarn", required_argument, 0, 'j'}, {"jcrit", required_argument, 0, 'k'}, {"timeout", required_argument, 0, 't'}, @@ -439,12 +663,12 @@ int process_arguments(int argc, char **argv){ {0, 0, 0, 0} }; - + if (argc < 2) usage ("\n"); while (1) { - c = getopt_long (argc, argv, "Vhv46w:c:Oj:k:t:H:", longopts, &option); + c = getopt_long (argc, argv, "Vhv46w:c:j:k:t:H:", longopts, &option); if (c == -1 || c == EOF || c == 1) break; @@ -454,25 +678,27 @@ int process_arguments(int argc, char **argv){ exit(STATE_OK); break; case 'V': - print_revision(progname, revision); + print_revision(progname, NP_VERSION); exit(STATE_OK); break; case 'v': verbose++; break; case 'w': - owarn = atof(optarg); + do_offset=1; + owarn = optarg; break; case 'c': - ocrit = atof(optarg); + do_offset=1; + ocrit = optarg; break; case 'j': do_jitter=1; - jwarn = atof(optarg); + jwarn = optarg; break; case 'k': do_jitter=1; - jcrit = atof(optarg); + jcrit = optarg; break; case 'H': if(is_host(optarg) == FALSE) @@ -482,9 +708,6 @@ int process_arguments(int argc, char **argv){ case 't': socket_timeout=atoi(optarg); break; - case 'O': - zero_offset_bad=1; - break; case '4': address_family = AF_INET; break; @@ -497,19 +720,11 @@ int process_arguments(int argc, char **argv){ break; case '?': /* print short usage statement if args not parsable */ - usage2 (_("Unknown argument"), optarg); + usage5 (); break; } } - if (ocrit < owarn){ - usage4(_("Critical offset should be larger than warning offset")); - } - - if (ocrit < owarn){ - usage4(_("Critical jitter should be larger than warning jitter")); - } - if(server_address == NULL){ usage4(_("Hostname was not supplied")); } @@ -517,26 +732,55 @@ int process_arguments(int argc, char **argv){ return 0; } +char *perfd_offset (double offset) +{ + return fperfdata ("offset", offset, "s", + TRUE, offset_thresholds->warning->end, + TRUE, offset_thresholds->critical->end, + FALSE, 0, FALSE, 0); +} + +char *perfd_jitter (double jitter) +{ + return fperfdata ("jitter", jitter, "s", + do_jitter, jitter_thresholds->warning->end, + do_jitter, jitter_thresholds->critical->end, + TRUE, 0, FALSE, 0); +} + int main(int argc, char *argv[]){ - int result = STATE_UNKNOWN; + int result, offset_result, jitter_result; double offset=0, jitter=0; + char *result_line, *perfdata_line; + + setlocale (LC_ALL, ""); + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); + + result = offset_result = jitter_result = STATE_OK; + + /* Parse extra opts if any */ + argv=np_extra_opts (&argc, argv, progname); if (process_arguments (argc, argv) == ERROR) usage4 (_("Could not parse arguments")); + set_thresholds(&offset_thresholds, owarn, ocrit); + set_thresholds(&jitter_thresholds, jwarn, jcrit); + /* initialize alarm signal handling */ signal (SIGALRM, socket_timeout_alarm_handler); /* set socket timeout */ alarm (socket_timeout); - offset = offset_request(server_address); - if(offset > ocrit){ + offset = offset_request(server_address, &offset_result); + /* check_ntp used to always return CRITICAL if offset_result == STATE_UNKNOWN. + * Now we'll only do that is the offset thresholds were set */ + if (do_offset && offset_result == STATE_UNKNOWN) { result = STATE_CRITICAL; - } else if(offset > owarn) { - result = STATE_WARNING; } else { - result = STATE_OK; + result = get_status(fabs(offset), offset_thresholds); } /* If not told to check the jitter, we don't even send packets. @@ -545,59 +789,97 @@ int main(int argc, char *argv[]){ * (for example) will result in an error */ if(do_jitter){ - jitter=jitter_request(server_address); - if(jitter > jcrit){ - result = max_state(result, STATE_CRITICAL); - } else if(jitter > jwarn) { - result = max_state(result, STATE_WARNING); - } else if(jitter == -1.0 && result == STATE_OK){ - /* -1 indicates that we couldn't calculate the jitter - * Only overrides STATE_OK from the offset */ + jitter=jitter_request(server_address, &jitter_result); + result = max_state_alt(result, get_status(jitter, jitter_thresholds)); + /* -1 indicates that we couldn't calculate the jitter + * Only overrides STATE_OK from the offset */ + if(jitter == -1.0 && result == STATE_OK) result = STATE_UNKNOWN; - } } + result = max_state_alt(result, jitter_result); switch (result) { case STATE_CRITICAL : - printf("NTP CRITICAL: "); + asprintf(&result_line, _("NTP CRITICAL:")); break; case STATE_WARNING : - printf("NTP WARNING: "); + asprintf(&result_line, _("NTP WARNING:")); break; case STATE_OK : - printf("NTP OK: "); + asprintf(&result_line, _("NTP OK:")); break; default : - printf("NTP UNKNOWN: "); + asprintf(&result_line, _("NTP UNKNOWN:")); break; } - - printf("Offset %g secs|offset=%g", offset, offset); - if (do_jitter) printf("|jitter=%f", jitter); - printf("\n"); + if(offset_result == STATE_UNKNOWN){ + asprintf(&result_line, "%s %s", result_line, _("Offset unknown")); + asprintf(&perfdata_line, ""); + } else { + asprintf(&result_line, "%s %s %.10g secs", result_line, _("Offset"), offset); + asprintf(&perfdata_line, "%s", perfd_offset(offset)); + } + if (do_jitter) { + asprintf(&result_line, "%s, jitter=%f", result_line, jitter); + asprintf(&perfdata_line, "%s %s", perfdata_line, perfd_jitter(jitter)); + } + printf("%s|%s\n", result_line, perfdata_line); if(server_address!=NULL) free(server_address); return result; } -void print_usage(void){ - printf("\ -Usage: %s -H [-O] [-w ] [-c ] [-j ] [-k ] [-v verbose]\ -\n", progname); -} void print_help(void){ - print_revision(progname, revision); + print_revision(progname, NP_VERSION); - printf ("Copyright (c) 1999 Ethan Galstad\n"); + printf ("Copyright (c) 2006 Sean Finney\n"); printf (COPYRIGHT, copyright, email); + printf ("%s\n", _("This plugin checks the selected ntp server")); + + printf ("\n\n"); + print_usage(); - printf (_(UT_HELP_VRSN)); - printf (_(UT_HOST_PORT), 'p', "123"); - printf (_(UT_WARN_CRIT)); - printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT); - printf (_(UT_VERBOSE)); - printf(_(UT_SUPPORT)); + printf (UT_HELP_VRSN); + printf (UT_EXTRA_OPTS); + printf (UT_HOST_PORT, 'p', "123"); + printf (" %s\n", "-w, --warning=THRESHOLD"); + printf (" %s\n", _("Offset to result in warning status (seconds)")); + printf (" %s\n", "-c, --critical=THRESHOLD"); + printf (" %s\n", _("Offset to result in critical status (seconds)")); + printf (" %s\n", "-j, --jwarn=THRESHOLD"); + printf (" %s\n", _("Warning threshold for jitter")); + printf (" %s\n", "-k, --jcrit=THRESHOLD"); + printf (" %s\n", _("Critical threshold for jitter")); + printf (UT_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); + printf (UT_VERBOSE); + + printf("\n"); + printf("%s\n", _("Notes:")); + printf(UT_THRESHOLDS_NOTES); + + printf("\n"); + printf("%s\n", _("Examples:")); + printf(" %s\n", _("Normal offset check:")); + printf(" %s\n", ("./check_ntp -H ntpserv -w 0.5 -c 1")); + printf("\n"); + printf(" %s\n", _("Check jitter too, avoiding critical notifications if jitter isn't available")); + printf(" %s\n", _("(See Notes above for more details on thresholds formats):")); + printf(" %s\n", ("./check_ntp -H ntpserv -w 0.5 -c 1 -j -1:100 -k -1:200")); + + printf (UT_SUPPORT); + + printf ("%s\n", _("WARNING: check_ntp is deprecated. Please use check_ntp_peer or")); + printf ("%s\n\n", _("check_ntp_time instead.")); +} + +void +print_usage(void) +{ + printf ("%s\n", _("WARNING: check_ntp is deprecated. Please use check_ntp_peer or")); + printf ("%s\n\n", _("check_ntp_time instead.")); + printf ("%s\n", _("Usage:")); + printf(" %s -H [-w ] [-c ] [-j ] [-k ] [-v verbose]\n", progname); }