From 3da3e4bde33cf3eafe2158f358a47cbdd69e2c98 Mon Sep 17 00:00:00 2001 From: Poul-Henning Kamp Date: Sun, 11 Jan 2015 19:44:54 +0000 Subject: [PATCH] Add signal detection to TODO and time_unix.c and use this to let SIGHUP restart processing in ntimed-client. Have the NTP_filter clear state and the PLL start over when TB_generation changes. Simplify the NTP_PeerSet code with respect to real/simulated peers, and add some preliminary scaffolding for the server maintenance code. --- main_client.c | 32 +++++-- main_sim_client.c | 7 +- ntimed.h | 5 +- ntp.h | 21 +++-- ntp_filter.c | 8 ++ ntp_peerset.c | 224 +++++++++++++++++++++++++++++++--------------- ntp_tbl.h | 14 ++- pll_std.c | 6 ++ time_sim.c | 3 +- time_stuff.c | 10 ++- time_unix.c | 9 +- todo.c | 6 +- 12 files changed, 239 insertions(+), 106 deletions(-) diff --git a/main_client.c b/main_client.c index 37932e6..d87834f 100644 --- a/main_client.c +++ b/main_client.c @@ -31,6 +31,7 @@ */ #include +#include #include #include @@ -44,12 +45,22 @@ #undef PARAM_TABLE_NAME #undef PARAM_CLIENT +static volatile sig_atomic_t restart = 1; + +static void __match_proto__() +sig_hup(int siginfo) +{ + (void)signal(SIGHUP, sig_hup); + (void)siginfo; + restart = 1; +} + int main_client(int argc, char *const *argv) { int ch; struct ntp_peer *np; - struct ntp_peerset *npl; + struct ntp_peerset *nps; struct todolist *tdl; struct combine_delta *cd; struct udp_socket *usc; @@ -63,7 +74,7 @@ main_client(int argc, char *const *argv) PLL_Init(); - npl = NTP_PeerSet_New(NULL); + nps = NTP_PeerSet_New(NULL); Param_Register(client_param_table); NF_Init(); @@ -87,7 +98,7 @@ main_client(int argc, char *const *argv) argv += optind; for (ch = 0; ch < argc; ch++) - npeer += NTP_PeerSet_Add(NULL, npl, argv[ch]); + npeer += NTP_PeerSet_Add(NULL, nps, argv[ch]); if (npeer == 0) Fail(NULL, 0, "No NTP peers found"); @@ -102,14 +113,21 @@ main_client(int argc, char *const *argv) cd = CD_New(); - NTP_PeerSet_Foreach(np, npl) { + NTP_PeerSet_Foreach(np, nps) { NF_New(np); np->combiner = CD_AddSource(cd, np->hostname, np->ip); } - NTP_PeerSet_Poll(NULL, npl, usc, tdl); - - (void)TODO_Run(NULL, tdl); + do { + if (restart) { + Debug(NULL, "RESTART\n"); + TB_generation++; + NTP_PeerSet_Poll(NULL, nps, usc, tdl); + restart = 0; + } + (void)signal(SIGHUP, sig_hup); + (void)TODO_Run(NULL, tdl); + } while (restart); return (0); } diff --git a/main_sim_client.c b/main_sim_client.c index d60bd6b..dde8ddf 100644 --- a/main_sim_client.c +++ b/main_sim_client.c @@ -156,7 +156,6 @@ SimFile_Open(struct ocx *ocx, const char *fn, struct todolist *tdl, struct ntp_peerset *npl) { struct sim_file *sf; - struct ntp_peer *np; char buf[BUFSIZ]; char buf2[BUFSIZ]; char buf3[BUFSIZ]; @@ -205,11 +204,7 @@ SimFile_Open(struct ocx *ocx, const char *fn, struct todolist *tdl, if (sscanf(buf, "# Peer %s %s", buf2, buf3) != 2) Fail(ocx, 0, "Expected '# Peer' line"); - np = NTP_Peer_NewLookup(ocx, buf3); - AN(np); - np->hostname = strdup(buf2); - AN(np->hostname); - NTP_PeerSet_AddPeer(ocx, npl, np); + NTP_PeerSet_AddSim(ocx, npl, buf2, buf3); if (++fpeer == sf->n_peer) s++; break; diff --git a/ntimed.h b/ntimed.h index cbf2c2d..cf2eba3 100644 --- a/ntimed.h +++ b/ntimed.h @@ -113,7 +113,7 @@ struct timestamp { uint64_t frac; }; -typedef void tb_sleep_f(double dur); +typedef int tb_sleep_f(double dur); typedef struct timestamp *tb_now_f(struct timestamp *); typedef void tb_step_f(struct ocx *, double offset); typedef void tb_adjust_f(struct ocx *, double offset, double duration, @@ -131,7 +131,7 @@ struct timestamp *TS_Nanosec(struct timestamp *storage, struct timestamp *TS_Double(struct timestamp *storage, double); double TS_Diff(const struct timestamp *t1, const struct timestamp *t2); -void TS_SleepUntil(const struct timestamp *); +int TS_SleepUntil(const struct timestamp *); void TS_Format(char *buf, ssize_t len, const struct timestamp *ts); void TS_RunTest(struct ocx *ocx); @@ -140,6 +140,7 @@ void TS_RunTest(struct ocx *ocx); enum todo_e { + TODO_INTR = -2, // Signal received TODO_FAIL = -1, // Break out of TODO_Run() TODO_OK = 0, TODO_DONE = 1, // Stop repeating me diff --git a/ntp.h b/ntp.h index e2f75ac..a253577 100644 --- a/ntp.h +++ b/ntp.h @@ -47,6 +47,12 @@ enum ntp_leap { #undef NTP_LEAP }; +enum ntp_state { +#define NTP_STATE(n, l, u, d) NTP_STATE_##u = n, +#include "ntp_tbl.h" +#undef NTP_STATE +}; + /* ntp_packet.c -- [De]Serialisation **********************************/ struct ntp_packet { @@ -108,6 +114,8 @@ struct ntp_peer { // For ntp_peerset.c TAILQ_ENTRY(ntp_peer) list; struct ntp_group *group; + enum ntp_state state; + const struct ntp_peer *other; }; struct ntp_peer *NTP_Peer_New(const char *name, const void *, unsigned); @@ -119,10 +127,9 @@ int NTP_Peer_Poll(struct ocx *, const struct udp_socket *, /* ntp_peerset.c -- Peer set management ****************************/ struct ntp_peerset *NTP_PeerSet_New(struct ocx *); -void NTP_PeerSet_AddPeer(struct ocx *ocx, struct ntp_peerset *npl, - struct ntp_peer *np); -int NTP_PeerSet_Add(struct ocx *, struct ntp_peerset *, - const char *hostname); +void NTP_PeerSet_AddSim(struct ocx *, struct ntp_peerset *, + const char *hostname, const char *ip); +int NTP_PeerSet_Add(struct ocx *, struct ntp_peerset *, const char *hostname); void NTP_PeerSet_Poll(struct ocx *, struct ntp_peerset *, struct udp_socket *, struct todolist *); @@ -130,7 +137,7 @@ struct ntp_peer *NTP_PeerSet_Iter0(const struct ntp_peerset *); struct ntp_peer *NTP_PeerSet_IterN(const struct ntp_peerset *, const struct ntp_peer *); -#define NTP_PeerSet_Foreach(var, npl) \ - for(var = NTP_PeerSet_Iter0(npl); \ +#define NTP_PeerSet_Foreach(var, nps) \ + for(var = NTP_PeerSet_Iter0(nps); \ var != NULL; \ - var = NTP_PeerSet_IterN(npl, var)) + var = NTP_PeerSet_IterN(nps, var)) diff --git a/ntp_filter.c b/ntp_filter.c index 5f99d55..bd9cb9e 100644 --- a/ntp_filter.c +++ b/ntp_filter.c @@ -49,6 +49,8 @@ struct ntp_filter { double alolo, ahihi; double navg; double trust; + + int generation; }; static void __match_proto__(ntp_filter_f) @@ -64,6 +66,12 @@ nf_filter(struct ocx *ocx, const struct ntp_peer *np) CAST_OBJ_NOTNULL(nf, np->filter_priv, NTP_FILTER_MAGIC); + if (nf->generation != TB_generation) { + nf->navg = 0; + nf->alo = nf->amid = nf->ahi = 0.0; + nf->alolo = nf->ahihi = 0.0; + } + rxp = np->rx_pkt; CHECK_OBJ_NOTNULL(rxp, NTP_PACKET_MAGIC); diff --git a/ntp_peerset.c b/ntp_peerset.c index 07f9936..a9fe456 100644 --- a/ntp_peerset.c +++ b/ntp_peerset.c @@ -23,6 +23,19 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * + * A peer-set is the total set of NTP servers we keep track of. + * + * The set is composed of groups, each of which is all the IP# we + * get from resolving a single argument. + * + * Within each group we poll a maximum of one ${param} servers, + * picking the best. + * + * Across the set we keep an hairy eyeball for servers appearing + * more than once, because it would deceive our quorum. Spotting + * the same IP# is trivial, multihomed servers can be spotted on + * {stratum,refid,reftime} triplet. + * */ #include @@ -65,15 +78,15 @@ struct ntp_peerset { struct ntp_peerset * NTP_PeerSet_New(struct ocx *ocx) { - struct ntp_peerset *npl; + struct ntp_peerset *nps; (void)ocx; - ALLOC_OBJ(npl, NTP_PEERSET_MAGIC); - AN(npl); + ALLOC_OBJ(nps, NTP_PEERSET_MAGIC); + AN(nps); - TAILQ_INIT(&npl->head); - TAILQ_INIT(&npl->group); - return (npl); + TAILQ_INIT(&nps->head); + TAILQ_INIT(&nps->group); + return (nps); } /********************************************************************** @@ -81,112 +94,164 @@ NTP_PeerSet_New(struct ocx *ocx) */ struct ntp_peer * -NTP_PeerSet_Iter0(const struct ntp_peerset *npl) +NTP_PeerSet_Iter0(const struct ntp_peerset *nps) { - CHECK_OBJ_NOTNULL(npl, NTP_PEERSET_MAGIC); - return (TAILQ_FIRST(&npl->head)); + CHECK_OBJ_NOTNULL(nps, NTP_PEERSET_MAGIC); + return (TAILQ_FIRST(&nps->head)); } struct ntp_peer * -NTP_PeerSet_IterN(const struct ntp_peerset *npl, const struct ntp_peer *np) +NTP_PeerSet_IterN(const struct ntp_peerset *nps, const struct ntp_peer *np) { - CHECK_OBJ_NOTNULL(npl, NTP_PEERSET_MAGIC); + CHECK_OBJ_NOTNULL(nps, NTP_PEERSET_MAGIC); CHECK_OBJ_NOTNULL(np, NTP_PEER_MAGIC); return (TAILQ_NEXT(np, list)); } /**********************************************************************/ -void -NTP_PeerSet_AddPeer(struct ocx *ocx, struct ntp_peerset *npl, - struct ntp_peer *np) -{ - - (void)ocx; - CHECK_OBJ_NOTNULL(npl, NTP_PEERSET_MAGIC); - CHECK_OBJ_NOTNULL(np, NTP_PEER_MAGIC); - TAILQ_INSERT_TAIL(&npl->head, np, list); - npl->npeer++; -} - -/**********************************************************************/ - -int -NTP_PeerSet_Add(struct ocx *ocx, struct ntp_peerset *npl, - const char *hostname) +static int +ntp_peerset_fillgroup(struct ocx *ocx, struct ntp_peerset *nps, + struct ntp_group *ng, const char *lookup) { struct addrinfo hints, *res, *res0; - int error, n; - struct ntp_peer *np; - struct ntp_group *ng; + int error, n = 0; + struct ntp_peer *np, *np2; + + CHECK_OBJ_NOTNULL(nps, NTP_PEERSET_MAGIC); + CHECK_OBJ_NOTNULL(ng, NTP_GROUP_MAGIC); - CHECK_OBJ_NOTNULL(npl, NTP_PEERSET_MAGIC); memset(&hints, 0, sizeof hints); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; - error = getaddrinfo(hostname, "ntp", &hints, &res0); + error = getaddrinfo(lookup, "ntp", &hints, &res0); if (error) Fail(ocx, 1, "hostname '%s', port 'ntp': %s\n", - hostname, gai_strerror(error)); - ALLOC_OBJ(ng, NTP_GROUP_MAGIC); - AN(ng); - n = 0; + lookup, gai_strerror(error)); for (res = res0; res; res = res->ai_next) { - np = NTP_Peer_New(hostname, res->ai_addr, res->ai_addrlen); - // XXX: duplicate check - TAILQ_INSERT_TAIL(&npl->head, np, list); - npl->npeer++; + if (res->ai_family != AF_INET && res->ai_family != AF_INET6) + continue; + np = NTP_Peer_New(ng->hostname, res->ai_addr, res->ai_addrlen); + AN(np); + TAILQ_FOREACH(np2, &nps->head, list) + if (SA_Equal(np->sa, np->sa_len, np2->sa, np2->sa_len)) + break; + if (np2 != NULL) { + /* All duplicates point to the same "master" */ + np->state = NTP_STATE_DUPLICATE; + np->other = np2->other; + if (np->other == NULL) + np->other = np2; + TAILQ_INSERT_TAIL(&nps->head, np, list); + Debug(ocx, "Peer {%s %s} is duplicate of {%s %s}\n", + np->hostname, np->ip, np2->hostname, np2->ip); + } else { + np->state = NTP_STATE_NEW; + TAILQ_INSERT_HEAD(&nps->head, np, list); + } + nps->npeer++; np->group = ng; ng->npeer++; n++; } freeaddrinfo(res0); - if (ng->npeer == 0) { - FREE_OBJ(ng); - } else { - ng->hostname = strdup(hostname); - AN(ng->hostname); - TAILQ_INSERT_TAIL(&npl->group, ng, list); - npl->ngroup++; - } return (n); } +/**********************************************************************/ + +static struct ntp_group * +ntp_peerset_add_group(struct ntp_peerset *nps, const char *name) +{ + struct ntp_group *ng; + + ALLOC_OBJ(ng, NTP_GROUP_MAGIC); + AN(ng); + ng->hostname = strdup(name); + AN(ng->hostname); + TAILQ_INSERT_TAIL(&nps->group, ng, list); + nps->ngroup++; + return (ng); +} + +/********************************************************************** + * Add a peer with a specific hostname+ip combination without actually + * resolving the hostname. + */ + +void +NTP_PeerSet_AddSim(struct ocx *ocx, struct ntp_peerset *nps, + const char *hostname, const char *ip) +{ + struct ntp_group *ng; + + CHECK_OBJ_NOTNULL(nps, NTP_PEERSET_MAGIC); + TAILQ_FOREACH(ng, &nps->group, list) + if (!strcasecmp(ng->hostname, hostname)) + break; + if (ng == NULL) + ng = ntp_peerset_add_group(nps, hostname); + assert(ntp_peerset_fillgroup(ocx, nps, ng, ip) == 1); +} + +/********************************************************************** + * Create a new group and add whatever peers its hostname resolves to + */ + +int +NTP_PeerSet_Add(struct ocx *ocx, struct ntp_peerset *nps, const char *hostname) +{ + struct ntp_group *ng; + + CHECK_OBJ_NOTNULL(nps, NTP_PEERSET_MAGIC); + + TAILQ_FOREACH(ng, &nps->group, list) + if (!strcasecmp(ng->hostname, hostname)) + Fail(ocx, 0, "hostname %s is duplicated\n", hostname); + + ng = ntp_peerset_add_group(nps, hostname); + + if (ntp_peerset_fillgroup(ocx, nps, ng, hostname) == 0) + Fail(ocx, 0, "hostname %s no IP# found.\n", hostname); + + return (ng->npeer); +} + /********************************************************************** * This function is responsible for polling the peers in the set. */ -static enum todo_e +static enum todo_e __match_proto__(todo_f) ntp_peerset_poll(struct ocx *ocx, struct todolist *tdl, void *priv) { - struct ntp_peerset *npl; + struct ntp_peerset *nps; struct ntp_peer *np; double d, dt; (void)ocx; - CAST_OBJ_NOTNULL(npl, priv, NTP_PEERSET_MAGIC); + CAST_OBJ_NOTNULL(nps, priv, NTP_PEERSET_MAGIC); AN(tdl); - np = TAILQ_FIRST(&npl->head); + np = TAILQ_FIRST(&nps->head); if (np == NULL) return(TODO_DONE); CHECK_OBJ_NOTNULL(np, NTP_PEER_MAGIC); - TAILQ_REMOVE(&npl->head, np, list); - TAILQ_INSERT_TAIL(&npl->head, np, list); + TAILQ_REMOVE(&nps->head, np, list); + TAILQ_INSERT_TAIL(&nps->head, np, list); - d = npl->poll_period / npl->npeer; - if (npl->t0 < npl->init_duration) { + d = nps->poll_period / nps->npeer; + if (nps->t0 < nps->init_duration) { dt = exp( - log(npl->init_duration) / (npl->init_packets * npl->npeer)); - if (npl->t0 * dt < npl->init_duration) - d = npl->t0 * dt - npl->t0; + log(nps->init_duration) / (nps->init_packets * nps->npeer)); + if (nps->t0 * dt < nps->init_duration) + d = nps->t0 * dt - nps->t0; } - npl->t0 += d; - TODO_ScheduleRel(tdl, ntp_peerset_poll, npl, d, 0.0, "NTP_PeerSet"); - if (NTP_Peer_Poll(ocx, npl->usc, np, 0.8)) { + nps->t0 += d; + TODO_ScheduleRel(tdl, ntp_peerset_poll, nps, d, 0.0, "NTP_PeerSet"); + if (NTP_Peer_Poll(ocx, nps->usc, np, 0.8)) { if (np->filter_func != NULL) np->filter_func(ocx, np); } @@ -213,26 +278,37 @@ ntp_peerset_herd(struct ocx *ocx, struct todolist *tdl, void *priv) /**********************************************************************/ +static uintptr_t poll_hdl; +static uintptr_t herd_hdl; + void -NTP_PeerSet_Poll(struct ocx *ocx, struct ntp_peerset *npl, +NTP_PeerSet_Poll(struct ocx *ocx, struct ntp_peerset *nps, struct udp_socket *usc, struct todolist *tdl) { + struct ntp_peer *np; (void)ocx; - CHECK_OBJ_NOTNULL(npl, NTP_PEERSET_MAGIC); + CHECK_OBJ_NOTNULL(nps, NTP_PEERSET_MAGIC); AN(usc); AN(tdl); - npl->usc = usc; - npl->t0 = 1.0; - npl->init_duration = 64.; - npl->init_packets = 6.; - npl->poll_period = 64.; - TODO_ScheduleRel(tdl, ntp_peerset_poll, npl, 0.0, 0.0, + TAILQ_FOREACH(np, &nps->head, list) + np->state = NTP_STATE_NEW; + nps->usc = usc; + nps->t0 = 1.0; + nps->init_duration = 64.; + nps->init_packets = 6.; + nps->poll_period = 64.; + + if (poll_hdl != 0) + TODO_Cancel(tdl, &poll_hdl); + poll_hdl = TODO_ScheduleRel(tdl, ntp_peerset_poll, nps, 0.0, 0.0, "NTP_PeerSet Poll"); - if (npl->ngroup > 0) - TODO_ScheduleRel(tdl, ntp_peerset_herd, npl, - 15. * 60. / npl->ngroup, 0.0, "NTP_PeerSet Herd"); + + if (herd_hdl != 0) + TODO_Cancel(tdl, &herd_hdl); + herd_hdl = TODO_ScheduleRel(tdl, ntp_peerset_herd, nps, + 15. * 60. / nps->ngroup, 0.0, "NTP_PeerSet Herd"); } diff --git a/ntp_tbl.h b/ntp_tbl.h index afd4633..deb9915 100644 --- a/ntp_tbl.h +++ b/ntp_tbl.h @@ -37,13 +37,23 @@ NTP_MODE(5, bcast, BCAST) NTP_MODE(6, ctrl, CTRL) NTP_MODE(7, mode7, MODE7) #endif -/*lint -restore */ -/*lint -save -e525 -e539 */ #ifdef NTP_LEAP NTP_LEAP(0, none, NONE) NTP_LEAP(1, ins, INS) NTP_LEAP(2, del, DEL) NTP_LEAP(3, unknown, UNKNOWN) #endif + +#ifdef NTP_STATE +NTP_STATE(0, new, NEW, "Newly configured peer.") +NTP_STATE(1, active, ACTIVE, "Good (and used) peer.") +NTP_STATE(2, available, AVAILABLE, "Not good enough peer.") +NTP_STATE(3, unsynchronized, UNSYNCHRONIZED, "Bad peer.") +NTP_STATE(4, unresponsive, UNRESPONSIVE, "Peer does not respond.") +NTP_STATE(5, unreachable, UNREACHABLE, "Peer cannot be reached.") +NTP_STATE(6, multihome, MULTIHOME, "Copy of multihomed peer.") +NTP_STATE(7, duplicate, DUPLICATE, "Duplicate peer.") +#endif + /*lint -restore */ diff --git a/pll_std.c b/pll_std.c index 6344963..5223e31 100644 --- a/pll_std.c +++ b/pll_std.c @@ -44,6 +44,7 @@ static struct timestamp pll_last_time; static int pll_mode; static double pll_a, pll_b; static struct timestamp pll_t0; +static int pll_generation; static void __match_proto__(pll_f) pll_std(struct ocx *ocx, double offset, double weight) @@ -58,6 +59,11 @@ pll_std(struct ocx *ocx, double offset, double weight) dt = 0; used_a = used_b = 0; + if (pll_generation != TB_generation) { + pll_mode = 0; + pll_generation = TB_generation; + } + switch (pll_mode) { case 0: /* Startup */ diff --git a/time_sim.c b/time_sim.c index f3d2dbe..e63d666 100644 --- a/time_sim.c +++ b/time_sim.c @@ -69,12 +69,13 @@ st_Now(struct timestamp *storage) /**********************************************************************/ -static void +static int st_Sleep(double dur) { TS_Add(&st_now, dur); Time_Sim_delta += dur * freq; + return (0); } /**********************************************************************/ diff --git a/time_stuff.c b/time_stuff.c index dd5a6bc..d003738 100644 --- a/time_stuff.c +++ b/time_stuff.c @@ -124,7 +124,7 @@ TS_Diff(const struct timestamp *t1, const struct timestamp *t2) /**********************************************************************/ -void +int TS_SleepUntil(const struct timestamp *t) { struct timestamp now; @@ -132,8 +132,9 @@ TS_SleepUntil(const struct timestamp *t) TB_Now(&now); dt = TS_Diff(t, &now); - if (dt > 0.) - TB_Sleep(dt); + if (dt <= 0.) + return (0); + return (TB_Sleep(dt)); } /**********************************************************************/ @@ -171,11 +172,12 @@ tb_now_f *TB_Now = tb_Now; /**********************************************************************/ -static void +static int tb_Sleep(double dur) { (void)dur; WRONG("No TB_Sleep"); + return (-1); } tb_sleep_f *TB_Sleep = tb_Sleep; diff --git a/time_unix.c b/time_unix.c index 6590c03..d34b0c5 100644 --- a/time_unix.c +++ b/time_unix.c @@ -220,12 +220,17 @@ kt_now(struct timestamp *storage) /**********************************************************************/ -static void __match_proto__(tb_sleep_f) +static int __match_proto__(tb_sleep_f) kt_sleep(double dur) { struct pollfd fds[1]; + int i; - AZ(poll(fds, 0, (int)floor(dur * 1e3))); + i = poll(fds, 0, (int)floor(dur * 1e3)); + if (i < 0 && errno == EINTR) + return (1); + AZ(i); + return (0); } /**********************************************************************/ diff --git a/todo.c b/todo.c index dee8bae..3c23539 100644 --- a/todo.c +++ b/todo.c @@ -169,11 +169,15 @@ TODO_Run(struct ocx *ocx, struct todolist *tdl) struct todo *tp; enum todo_e ret = TODO_OK; char buf[40]; + int i; CHECK_OBJ_NOTNULL(tdl, TODOLIST_MAGIC); while(!TAILQ_EMPTY(&tdl->todolist)) { tp = TAILQ_FIRST(&tdl->todolist); - TS_SleepUntil(&tp->when); + i = TS_SleepUntil(&tp->when); + if (i == 1) + return (TODO_INTR); + AZ(i); TS_Format(buf, sizeof buf, &tp->when); Put(ocx, OCX_TRACE, "Now %s %s\n", buf, tp->what); ret = tp->func(ocx, tdl, tp->priv);