xref: /openbsd-src/usr.sbin/ntpd/control.c (revision f1b790a5738b7375271fee81f99119b1f82f2cfd)
1*f1b790a5Sclaudio /*	$OpenBSD: control.c,v 1.27 2024/11/21 13:38:14 claudio Exp $ */
264c82965Sphessler 
364c82965Sphessler /*
464c82965Sphessler  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
564c82965Sphessler  * Copyright (c) 2012 Mike Miller <mmiller@mgm51.com>
664c82965Sphessler  *
764c82965Sphessler  * Permission to use, copy, modify, and distribute this software for any
864c82965Sphessler  * purpose with or without fee is hereby granted, provided that the above
964c82965Sphessler  * copyright notice and this permission notice appear in all copies.
1064c82965Sphessler  *
1164c82965Sphessler  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1264c82965Sphessler  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1364c82965Sphessler  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1464c82965Sphessler  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1564c82965Sphessler  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1664c82965Sphessler  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1764c82965Sphessler  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1864c82965Sphessler  */
1964c82965Sphessler 
2064c82965Sphessler #include <sys/types.h>
2164c82965Sphessler #include <sys/stat.h>
2264c82965Sphessler #include <sys/socket.h>
2364c82965Sphessler #include <sys/un.h>
2464c82965Sphessler #include <errno.h>
255fbd837dStedu #include <math.h>
2664c82965Sphessler #include <stdio.h>
2764c82965Sphessler #include <stdlib.h>
2864c82965Sphessler #include <string.h>
2964c82965Sphessler #include <unistd.h>
3064c82965Sphessler #include <fcntl.h>
3164c82965Sphessler #include <err.h>
3264c82965Sphessler 
3364c82965Sphessler #include "ntpd.h"
3464c82965Sphessler 
3564c82965Sphessler #define	CONTROL_BACKLOG	5
3664c82965Sphessler 
375fbd837dStedu #define square(x) ((x) * (x))
385fbd837dStedu 
3964c82965Sphessler int
40febce360Sflorian control_check(char *path)
41febce360Sflorian {
42febce360Sflorian 	struct sockaddr_un	 sun;
43febce360Sflorian 	int			 fd;
44febce360Sflorian 
45febce360Sflorian 	bzero(&sun, sizeof(sun));
46febce360Sflorian 	sun.sun_family = AF_UNIX;
47febce360Sflorian 	strlcpy(sun.sun_path, path, sizeof(sun.sun_path));
48febce360Sflorian 
49febce360Sflorian 	if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
50e6c01febSotto 		log_debug("control_check: socket check");
51febce360Sflorian 		return (-1);
52febce360Sflorian 	}
53febce360Sflorian 
54febce360Sflorian 	if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) == 0) {
55e6c01febSotto 		log_debug("control_check: socket in use");
56febce360Sflorian 		close(fd);
57febce360Sflorian 		return (-1);
58febce360Sflorian 	}
59febce360Sflorian 
60febce360Sflorian 	close(fd);
61febce360Sflorian 
62febce360Sflorian 	return (0);
63febce360Sflorian }
64febce360Sflorian 
65febce360Sflorian int
6664c82965Sphessler control_init(char *path)
6764c82965Sphessler {
68accd9e67Sbcook 	struct sockaddr_un	 sa;
6964c82965Sphessler 	int			 fd;
7064c82965Sphessler 	mode_t			 old_umask;
7164c82965Sphessler 
724e840e7aSrzalamena 	if ((fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)) == -1) {
7364c82965Sphessler 		log_warn("control_init: socket");
7464c82965Sphessler 		return (-1);
7564c82965Sphessler 	}
7664c82965Sphessler 
77842d7e97Sbcook 	memset(&sa, 0, sizeof(sa));
78accd9e67Sbcook 	sa.sun_family = AF_UNIX;
79accd9e67Sbcook 	if (strlcpy(sa.sun_path, path, sizeof(sa.sun_path)) >=
80accd9e67Sbcook 	    sizeof(sa.sun_path))
8164c82965Sphessler 		errx(1, "ctl socket name too long");
8264c82965Sphessler 
8364c82965Sphessler 	if (unlink(path) == -1)
8464c82965Sphessler 		if (errno != ENOENT) {
8564c82965Sphessler 			log_warn("control_init: unlink %s", path);
8664c82965Sphessler 			close(fd);
8764c82965Sphessler 			return (-1);
8864c82965Sphessler 		}
8964c82965Sphessler 
9064c82965Sphessler 	old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
91accd9e67Sbcook 	if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
9264c82965Sphessler 		log_warn("control_init: bind: %s", path);
9364c82965Sphessler 		close(fd);
9464c82965Sphessler 		umask(old_umask);
9564c82965Sphessler 		return (-1);
9664c82965Sphessler 	}
9764c82965Sphessler 	umask(old_umask);
9864c82965Sphessler 
9964c82965Sphessler 	if (chmod(path, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1) {
10064c82965Sphessler 		log_warn("control_init: chmod");
10164c82965Sphessler 		close(fd);
10264c82965Sphessler 		(void)unlink(path);
10364c82965Sphessler 		return (-1);
10464c82965Sphessler 	}
10564c82965Sphessler 
1068bf26dd9Skrw 	session_socket_nonblockmode(fd);
10764c82965Sphessler 
10864c82965Sphessler 	return (fd);
10964c82965Sphessler }
11064c82965Sphessler 
11164c82965Sphessler int
11264c82965Sphessler control_listen(int fd)
11364c82965Sphessler {
11464c82965Sphessler 	if (fd != -1 && listen(fd, CONTROL_BACKLOG) == -1) {
11564c82965Sphessler 		log_warn("control_listen: listen");
11664c82965Sphessler 		return (-1);
11764c82965Sphessler 	}
11864c82965Sphessler 
11964c82965Sphessler 	return (0);
12064c82965Sphessler }
12164c82965Sphessler 
12264c82965Sphessler void
12364c82965Sphessler control_shutdown(int fd)
12464c82965Sphessler {
12564c82965Sphessler 	close(fd);
12664c82965Sphessler }
12764c82965Sphessler 
12864c82965Sphessler int
12964c82965Sphessler control_accept(int listenfd)
13064c82965Sphessler {
13164c82965Sphessler 	int			 connfd;
13264c82965Sphessler 	socklen_t		 len;
133accd9e67Sbcook 	struct sockaddr_un	 sa;
13464c82965Sphessler 	struct ctl_conn		*ctl_conn;
13564c82965Sphessler 
136accd9e67Sbcook 	len = sizeof(sa);
13764c82965Sphessler 	if ((connfd = accept(listenfd,
138accd9e67Sbcook 	    (struct sockaddr *)&sa, &len)) == -1) {
13964c82965Sphessler 		if (errno != EWOULDBLOCK && errno != EINTR)
14064c82965Sphessler 			log_warn("control_accept: accept");
14164c82965Sphessler 		return (0);
14264c82965Sphessler 	}
14364c82965Sphessler 
1448bf26dd9Skrw 	session_socket_nonblockmode(connfd);
14564c82965Sphessler 
14664c82965Sphessler 	if ((ctl_conn = calloc(1, sizeof(struct ctl_conn))) == NULL) {
14764c82965Sphessler 		log_warn("control_accept");
14864c82965Sphessler 		close(connfd);
14964c82965Sphessler 		return (0);
15064c82965Sphessler 	}
15164c82965Sphessler 
152*f1b790a5Sclaudio 	if (imsgbuf_init(&ctl_conn->ibuf, connfd) == -1) {
153*f1b790a5Sclaudio 		log_warn("control_accept");
154*f1b790a5Sclaudio 		close(connfd);
155*f1b790a5Sclaudio 		free(ctl_conn);
156*f1b790a5Sclaudio 		return (0);
157*f1b790a5Sclaudio 	}
15864c82965Sphessler 
15964c82965Sphessler 	TAILQ_INSERT_TAIL(&ctl_conns, ctl_conn, entry);
16064c82965Sphessler 
16164c82965Sphessler 	return (1);
16264c82965Sphessler }
16364c82965Sphessler 
16464c82965Sphessler struct ctl_conn *
16564c82965Sphessler control_connbyfd(int fd)
16664c82965Sphessler {
16764c82965Sphessler 	struct ctl_conn	*c;
16864c82965Sphessler 
1694ff7cad5Skrw 	TAILQ_FOREACH(c, &ctl_conns, entry) {
1704ff7cad5Skrw 		if (c->ibuf.fd == fd)
1714ff7cad5Skrw 			break;
1724ff7cad5Skrw 	}
17364c82965Sphessler 
17464c82965Sphessler 	return (c);
17564c82965Sphessler }
17664c82965Sphessler 
17764c82965Sphessler int
17864c82965Sphessler control_close(int fd)
17964c82965Sphessler {
18064c82965Sphessler 	struct ctl_conn	*c;
18164c82965Sphessler 
18264c82965Sphessler 	if ((c = control_connbyfd(fd)) == NULL) {
18364c82965Sphessler 		log_warn("control_close: fd %d: not found", fd);
18464c82965Sphessler 		return (0);
18564c82965Sphessler 	}
18664c82965Sphessler 
1879cbf9e90Sclaudio 	imsgbuf_clear(&c->ibuf);
18864c82965Sphessler 	TAILQ_REMOVE(&ctl_conns, c, entry);
18964c82965Sphessler 
19064c82965Sphessler 	close(c->ibuf.fd);
19164c82965Sphessler 	free(c);
19264c82965Sphessler 
19364c82965Sphessler 	return (1);
19464c82965Sphessler }
19564c82965Sphessler 
19664c82965Sphessler int
19764c82965Sphessler control_dispatch_msg(struct pollfd *pfd, u_int *ctl_cnt)
19864c82965Sphessler {
19964c82965Sphessler 	struct imsg		 imsg;
20064c82965Sphessler 	struct ctl_conn		*c;
20164c82965Sphessler 	struct ntp_peer		*p;
20264c82965Sphessler 	struct ntp_sensor	*s;
20364c82965Sphessler 	struct ctl_show_status	 c_status;
20464c82965Sphessler 	struct ctl_show_peer	 c_peer;
20564c82965Sphessler 	struct ctl_show_sensor	 c_sensor;
20664c82965Sphessler 	int			 cnt;
20764c82965Sphessler 	ssize_t			 n;
20864c82965Sphessler 
20964c82965Sphessler 	if ((c = control_connbyfd(pfd->fd)) == NULL) {
21064c82965Sphessler 		log_warn("control_dispatch_msg: fd %d: not found", pfd->fd);
21164c82965Sphessler 		return (0);
21264c82965Sphessler 	}
21364c82965Sphessler 
21464c82965Sphessler 	if (pfd->revents & POLLOUT)
215dd7efffeSclaudio 		if (imsgbuf_write(&c->ibuf) == -1) {
21664c82965Sphessler 			*ctl_cnt -= control_close(pfd->fd);
21764c82965Sphessler 			return (1);
21864c82965Sphessler 		}
21964c82965Sphessler 
22064c82965Sphessler 	if (!(pfd->revents & POLLIN))
22164c82965Sphessler 		return (0);
22264c82965Sphessler 
223f6bd242eSclaudio 	if (imsgbuf_read(&c->ibuf) != 1) {
22464c82965Sphessler 		*ctl_cnt -= control_close(pfd->fd);
22564c82965Sphessler 		return (1);
22664c82965Sphessler 	}
22764c82965Sphessler 
22864c82965Sphessler 	for (;;) {
22964c82965Sphessler 		if ((n = imsg_get(&c->ibuf, &imsg)) == -1) {
23064c82965Sphessler 			*ctl_cnt -= control_close(pfd->fd);
23164c82965Sphessler 			return (1);
23264c82965Sphessler 		}
23364c82965Sphessler 		if (n == 0)
23464c82965Sphessler 			break;
23564c82965Sphessler 
23664c82965Sphessler 		switch (imsg.hdr.type) {
23764c82965Sphessler 		case IMSG_CTL_SHOW_STATUS:
23864c82965Sphessler 			build_show_status(&c_status);
23964c82965Sphessler 			imsg_compose(&c->ibuf, IMSG_CTL_SHOW_STATUS, 0, 0, -1,
24064c82965Sphessler 			    &c_status, sizeof (c_status));
24164c82965Sphessler 			break;
24264c82965Sphessler 		case IMSG_CTL_SHOW_PEERS:
24364c82965Sphessler 			cnt = 0;
24464c82965Sphessler 			TAILQ_FOREACH(p, &conf->ntp_peers, entry) {
24564c82965Sphessler 				build_show_peer(&c_peer, p);
24664c82965Sphessler 				imsg_compose(&c->ibuf, IMSG_CTL_SHOW_PEERS,
24764c82965Sphessler 				    0, 0, -1, &c_peer, sizeof(c_peer));
24864c82965Sphessler 				cnt++;
24964c82965Sphessler 			}
25064c82965Sphessler 			imsg_compose(&c->ibuf, IMSG_CTL_SHOW_PEERS_END,
25164c82965Sphessler 			    0, 0, -1, &cnt, sizeof(cnt));
25264c82965Sphessler 			break;
25364c82965Sphessler 		case IMSG_CTL_SHOW_SENSORS:
25464c82965Sphessler 			cnt = 0;
25564c82965Sphessler 			TAILQ_FOREACH(s, &conf->ntp_sensors, entry) {
25664c82965Sphessler 				build_show_sensor(&c_sensor, s);
25764c82965Sphessler 				imsg_compose(&c->ibuf, IMSG_CTL_SHOW_SENSORS,
25864c82965Sphessler 				    0, 0, -1, &c_sensor, sizeof(c_sensor));
25964c82965Sphessler 				cnt++;
26064c82965Sphessler 			}
26164c82965Sphessler 			imsg_compose(&c->ibuf, IMSG_CTL_SHOW_SENSORS_END,
26264c82965Sphessler 			    0, 0, -1, &cnt, sizeof(cnt));
26364c82965Sphessler 			break;
26464c82965Sphessler 		case IMSG_CTL_SHOW_ALL:
26564c82965Sphessler 			build_show_status(&c_status);
26664c82965Sphessler 			imsg_compose(&c->ibuf, IMSG_CTL_SHOW_STATUS, 0, 0, -1,
26764c82965Sphessler 			    &c_status, sizeof (c_status));
26864c82965Sphessler 
26964c82965Sphessler 			cnt = 0;
27064c82965Sphessler 			TAILQ_FOREACH(p, &conf->ntp_peers, entry) {
27164c82965Sphessler 				build_show_peer(&c_peer, p);
27264c82965Sphessler 				imsg_compose(&c->ibuf, IMSG_CTL_SHOW_PEERS,
27364c82965Sphessler 				    0, 0, -1, &c_peer, sizeof(c_peer));
27464c82965Sphessler 				cnt++;
27564c82965Sphessler 			}
27664c82965Sphessler 			imsg_compose(&c->ibuf, IMSG_CTL_SHOW_PEERS_END,
27764c82965Sphessler 			    0, 0, -1, &cnt, sizeof(cnt));
27864c82965Sphessler 
27964c82965Sphessler 			cnt = 0;
28064c82965Sphessler 			TAILQ_FOREACH(s, &conf->ntp_sensors, entry) {
28164c82965Sphessler 				build_show_sensor(&c_sensor, s);
28264c82965Sphessler 				imsg_compose(&c->ibuf, IMSG_CTL_SHOW_SENSORS,
28364c82965Sphessler 				    0, 0, -1, &c_sensor, sizeof(c_sensor));
28464c82965Sphessler 				cnt++;
28564c82965Sphessler 			}
28664c82965Sphessler 			imsg_compose(&c->ibuf, IMSG_CTL_SHOW_SENSORS_END,
28764c82965Sphessler 			    0, 0, -1, &cnt, sizeof(cnt));
28864c82965Sphessler 
28964c82965Sphessler 			imsg_compose(&c->ibuf, IMSG_CTL_SHOW_ALL_END,
29064c82965Sphessler 			    0, 0, -1, NULL, 0);
29164c82965Sphessler 			break;
29264c82965Sphessler 		default:
29364c82965Sphessler 			break;
29464c82965Sphessler 		}
29564c82965Sphessler 		imsg_free(&imsg);
29664c82965Sphessler 	}
29764c82965Sphessler 	return (0);
29864c82965Sphessler }
29964c82965Sphessler 
30064c82965Sphessler void
3018bf26dd9Skrw session_socket_nonblockmode(int fd)
30264c82965Sphessler {
30364c82965Sphessler 	int	flags;
30464c82965Sphessler 
3058bf26dd9Skrw 	if ((flags = fcntl(fd, F_GETFL)) == -1)
30664c82965Sphessler 		fatal("fcntl F_GETFL");
30764c82965Sphessler 
30864c82965Sphessler 	flags |= O_NONBLOCK;
30964c82965Sphessler 
31064c82965Sphessler 	if ((flags = fcntl(fd, F_SETFL, flags)) == -1)
31164c82965Sphessler 		fatal("fcntl F_SETFL");
31264c82965Sphessler }
31364c82965Sphessler 
31464c82965Sphessler void
31564c82965Sphessler build_show_status(struct ctl_show_status *cs)
31664c82965Sphessler {
31764c82965Sphessler 	struct ntp_peer		*p;
31864c82965Sphessler 	struct ntp_sensor	*s;
31964c82965Sphessler 
32064c82965Sphessler 	cs->peercnt = cs->valid_peers = 0;
32164c82965Sphessler 	cs->sensorcnt = cs->valid_sensors = 0;
32264c82965Sphessler 
32364c82965Sphessler 	TAILQ_FOREACH(p, &conf->ntp_peers, entry) {
32464c82965Sphessler 		cs->peercnt++;
32564c82965Sphessler 		if (p->trustlevel >= TRUSTLEVEL_BADPEER)
32664c82965Sphessler 			cs->valid_peers++;
32764c82965Sphessler 	}
32864c82965Sphessler 	TAILQ_FOREACH(s, &conf->ntp_sensors, entry) {
32964c82965Sphessler 		cs->sensorcnt++;
33064c82965Sphessler 		if (s->update.good)
33164c82965Sphessler 			cs->valid_sensors++;
33264c82965Sphessler 	}
33364c82965Sphessler 
33464c82965Sphessler 	cs->synced = conf->status.synced;
33564c82965Sphessler 	cs->stratum = conf->status.stratum;
33664c82965Sphessler 	cs->clock_offset = getoffset() * 1000.0;
3371908d877Sotto 	cs->constraints = !TAILQ_EMPTY(&conf->constraints);
338bc58a738Sreyk 	cs->constraint_median = conf->constraint_median;
339bc58a738Sreyk 	cs->constraint_last = conf->constraint_last;
340bc58a738Sreyk 	cs->constraint_errors = conf->constraint_errors;
34164c82965Sphessler }
34264c82965Sphessler 
34364c82965Sphessler void
34464c82965Sphessler build_show_peer(struct ctl_show_peer *cp, struct ntp_peer *p)
34564c82965Sphessler {
34664c82965Sphessler 	const char	*a = "not resolved";
34764c82965Sphessler 	const char	*pool = "", *addr_head_name = "";
348b98b0a5cSotto 	const char	*auth = "";
3494c6157faSbluhm 	int		 shift, best = -1, validdelaycnt = 0, jittercnt = 0;
35064c82965Sphessler 	time_t		 now;
35164c82965Sphessler 
35264c82965Sphessler 	now = getmonotime();
35364c82965Sphessler 
354b98b0a5cSotto 	if (p->addr) {
3558745f5cfSotto 		a = log_ntp_addr(p->addr);
356b98b0a5cSotto 		if (p->addr->notauth)
357b98b0a5cSotto 			auth = " (non-dnssec lookup)";
358b98b0a5cSotto 	}
35964c82965Sphessler 	if (p->addr_head.pool)
36064c82965Sphessler 		pool = "from pool ";
36164c82965Sphessler 
362f64e4370Sotto 	if (0 != strcmp(a, p->addr_head.name) || p->addr_head.pool)
36364c82965Sphessler 		addr_head_name = p->addr_head.name;
36464c82965Sphessler 
36564c82965Sphessler 	snprintf(cp->peer_desc, sizeof(cp->peer_desc),
366b98b0a5cSotto 	    "%s %s%s%s", a, pool, addr_head_name, auth);
36764c82965Sphessler 
36864c82965Sphessler 	cp->offset = cp->delay = 0.0;
36964c82965Sphessler 	for (shift = 0; shift < OFFSET_ARRAY_SIZE; shift++) {
37064c82965Sphessler 		if (p->reply[shift].delay > 0.0) {
37164c82965Sphessler 			cp->offset += p->reply[shift].offset;
37264c82965Sphessler 			cp->delay += p->reply[shift].delay;
37364c82965Sphessler 
3744c6157faSbluhm 			if (best == -1 ||
3754c6157faSbluhm 			    p->reply[shift].delay < p->reply[best].delay)
37664c82965Sphessler 				best = shift;
37764c82965Sphessler 
37864c82965Sphessler 			validdelaycnt++;
37964c82965Sphessler 		}
38064c82965Sphessler 	}
38164c82965Sphessler 
38264c82965Sphessler 	if (validdelaycnt > 1) {
38364c82965Sphessler 		cp->offset /= validdelaycnt;
38464c82965Sphessler 		cp->delay /= validdelaycnt;
38564c82965Sphessler 	}
38664c82965Sphessler 
38764c82965Sphessler 	cp->jitter = 0.0;
3884c6157faSbluhm 	if (best != -1) {
38964c82965Sphessler 		for (shift = 0; shift < OFFSET_ARRAY_SIZE; shift++) {
39064c82965Sphessler 			if (p->reply[shift].delay > 0.0 && shift != best) {
3915fbd837dStedu 				cp->jitter += square(p->reply[shift].delay -
3925fbd837dStedu 				    p->reply[best].delay);
39364c82965Sphessler 				jittercnt++;
39464c82965Sphessler 			}
39564c82965Sphessler 		}
39664c82965Sphessler 		if (jittercnt > 1)
39764c82965Sphessler 			cp->jitter /= jittercnt;
3985fbd837dStedu 		cp->jitter = sqrt(cp->jitter);
3994c6157faSbluhm 	}
40064c82965Sphessler 
40164c82965Sphessler 	if (p->shift == 0)
40264c82965Sphessler 		shift = OFFSET_ARRAY_SIZE - 1;
40364c82965Sphessler 	else
40464c82965Sphessler 		shift = p->shift - 1;
40564c82965Sphessler 
40664c82965Sphessler 	if (conf->status.synced == 1 &&
40764c82965Sphessler 	    p->reply[shift].status.send_refid == conf->status.refid)
40864c82965Sphessler 		cp->syncedto = 1;
40964c82965Sphessler 	else
41064c82965Sphessler 		cp->syncedto = 0;
41164c82965Sphessler 
41264c82965Sphessler 	/* milliseconds to reduce number of leading zeroes */
41364c82965Sphessler 	cp->offset *= 1000.0;
41464c82965Sphessler 	cp->delay *= 1000.0;
41564c82965Sphessler 	cp->jitter *= 1000.0;
41664c82965Sphessler 
41764c82965Sphessler 	cp->weight = p->weight;
41864c82965Sphessler 	cp->trustlevel = p->trustlevel;
41964c82965Sphessler 	cp->stratum = p->reply[shift].status.stratum;
42064c82965Sphessler 	cp->next = p->next - now < 0 ? 0 : p->next - now;
42164c82965Sphessler 	cp->poll = p->poll;
42264c82965Sphessler }
42364c82965Sphessler 
42464c82965Sphessler void
42564c82965Sphessler build_show_sensor(struct ctl_show_sensor *cs, struct ntp_sensor *s)
42664c82965Sphessler {
42764c82965Sphessler 	time_t		 now;
42864c82965Sphessler 	u_int8_t	 shift;
42964c82965Sphessler 	u_int32_t	 refid;
43064c82965Sphessler 
43164c82965Sphessler 	now = getmonotime();
43264c82965Sphessler 
43364c82965Sphessler 	memcpy(&refid, SENSOR_DEFAULT_REFID, sizeof(refid));
43464c82965Sphessler 	refid = refid == s->refid ? 0 : s->refid;
43564c82965Sphessler 
43664c82965Sphessler 	snprintf(cs->sensor_desc, sizeof(cs->sensor_desc),
43764c82965Sphessler 	    "%s  %.4s", s->device, (char *)&refid);
43864c82965Sphessler 
43964c82965Sphessler 	if (s->shift == 0)
44064c82965Sphessler 		shift = SENSOR_OFFSETS - 1;
44164c82965Sphessler 	else
44264c82965Sphessler 		shift = s->shift - 1;
44364c82965Sphessler 
44464c82965Sphessler 	if (conf->status.synced == 1 &&
44564c82965Sphessler 	    s->offsets[shift].status.send_refid == conf->status.refid)
44664c82965Sphessler 		cs->syncedto = 1;
44764c82965Sphessler 	else
44864c82965Sphessler 		cs->syncedto = 0;
44964c82965Sphessler 
45064c82965Sphessler 	cs->weight = s->weight;
45164c82965Sphessler 	cs->good = s->update.good;
45264c82965Sphessler 	cs->stratum = s->offsets[shift].status.stratum;
45364c82965Sphessler 	cs->next = s->next - now < 0 ? 0 : s->next - now;
45464c82965Sphessler 	cs->poll = SENSOR_QUERY_INTERVAL;
45564c82965Sphessler 	cs->offset = s->offsets[shift].offset * 1000.0;
45664c82965Sphessler 	cs->correction = (double)s->correction / 1000.0;
45764c82965Sphessler }
458