1*f1b790a5Sclaudio /* $OpenBSD: ntpd.c,v 1.142 2024/11/21 13:38:14 claudio Exp $ */ 2914fd659Shenning 3914fd659Shenning /* 4914fd659Shenning * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> 564c82965Sphessler * Copyright (c) 2012 Mike Miller <mmiller@mgm51.com> 6914fd659Shenning * 7914fd659Shenning * Permission to use, copy, modify, and distribute this software for any 8914fd659Shenning * purpose with or without fee is hereby granted, provided that the above 9914fd659Shenning * copyright notice and this permission notice appear in all copies. 10914fd659Shenning * 11914fd659Shenning * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12914fd659Shenning * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13914fd659Shenning * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14914fd659Shenning * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15774da4d1Sreyk * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16774da4d1Sreyk * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17774da4d1Sreyk * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18914fd659Shenning */ 19914fd659Shenning 20914fd659Shenning #include <sys/types.h> 21832033eaSdtucker #include <sys/resource.h> 22914fd659Shenning #include <sys/socket.h> 23841516aaSotto #include <sys/sysctl.h> 24914fd659Shenning #include <sys/wait.h> 2564c82965Sphessler #include <sys/un.h> 26914fd659Shenning #include <netinet/in.h> 27914fd659Shenning #include <errno.h> 28914fd659Shenning #include <poll.h> 29914fd659Shenning #include <pwd.h> 30914fd659Shenning #include <signal.h> 31914fd659Shenning #include <stdio.h> 32914fd659Shenning #include <stdlib.h> 33914fd659Shenning #include <string.h> 34579813e4Sreyk #include <syslog.h> 3584c1bf98Sjsing #include <tls.h> 367b6bdf51Sbcook #include <time.h> 37914fd659Shenning #include <unistd.h> 38348cf342Sderaadt #include <fcntl.h> 398d80afc1Sclaudio #include <err.h> 40914fd659Shenning 41914fd659Shenning #include "ntpd.h" 42914fd659Shenning 43914fd659Shenning void sighdlr(int); 44cbb557dbShenning __dead void usage(void); 45841516aaSotto int auto_preconditions(const struct ntpd_conf *); 46914fd659Shenning int main(int, char *[]); 4778541249Srzalamena void check_child(void); 485f14684eSrzalamena int dispatch_imsg(struct ntpd_conf *, int, char **); 49e10f1ca1Sotto void reset_adjtime(void); 507de07ab1Sdtucker int ntpd_adjtime(double); 51460e0564Sotto void ntpd_adjfreq(double, int); 5210da828bShenning void ntpd_settime(double); 53edb831d1Sotto void readfreq(void); 54a5e2cc1cSstevesk int writefreq(double); 5564c82965Sphessler void ctl_main(int, char*[]); 56ae834c44Sbenno const char *ctl_lookup_option(char *, const char **); 5764c82965Sphessler void show_status_msg(struct imsg *); 5864c82965Sphessler void show_peer_msg(struct imsg *, int); 5964c82965Sphessler void show_sensor_msg(struct imsg *, int); 60914fd659Shenning 61914fd659Shenning volatile sig_atomic_t quit = 0; 62914fd659Shenning volatile sig_atomic_t reconfig = 0; 63914fd659Shenning volatile sig_atomic_t sigchld = 0; 6438da1314Shenning struct imsgbuf *ibuf; 6520529c73Shenning int timeout = INFTIM; 66914fd659Shenning 67a257dd04Sreyk extern u_int constraint_cnt; 68a257dd04Sreyk 69ae834c44Sbenno const char *showopt; 70ae834c44Sbenno 71ae834c44Sbenno static const char *ctl_showopt_list[] = { 72ae834c44Sbenno "peers", "Sensors", "status", "all", NULL 73ae834c44Sbenno }; 74ae834c44Sbenno 75914fd659Shenning void 76914fd659Shenning sighdlr(int sig) 77914fd659Shenning { 78914fd659Shenning switch (sig) { 79914fd659Shenning case SIGTERM: 80914fd659Shenning case SIGINT: 81914fd659Shenning quit = 1; 82914fd659Shenning break; 83914fd659Shenning case SIGCHLD: 84914fd659Shenning sigchld = 1; 85914fd659Shenning break; 86914fd659Shenning case SIGHUP: 87914fd659Shenning reconfig = 1; 88914fd659Shenning break; 89914fd659Shenning } 90914fd659Shenning } 91914fd659Shenning 92cbb557dbShenning __dead void 93914fd659Shenning usage(void) 94914fd659Shenning { 95914fd659Shenning extern char *__progname; 96914fd659Shenning 97aa47326aSschwarze if (strcmp(__progname, "ntpctl") == 0) 98a0ef9696Sreyk fprintf(stderr, 9950633af3Sjmc "usage: ntpctl -s all | peers | Sensors | status\n"); 100aa47326aSschwarze else 1019c341873Sderaadt fprintf(stderr, "usage: %s [-dnv] [-f file]\n", 10264c82965Sphessler __progname); 103914fd659Shenning exit(1); 104914fd659Shenning } 105914fd659Shenning 106841516aaSotto int 107841516aaSotto auto_preconditions(const struct ntpd_conf *cnf) 108841516aaSotto { 109841516aaSotto int mib[2] = { CTL_KERN, KERN_SECURELVL }; 110841516aaSotto int constraints, securelevel; 111841516aaSotto size_t sz = sizeof(int); 112841516aaSotto 113df69c215Sderaadt if (sysctl(mib, 2, &securelevel, &sz, NULL, 0) == -1) 114841516aaSotto err(1, "sysctl"); 115841516aaSotto constraints = !TAILQ_EMPTY(&cnf->constraints); 1161511e2d1Sotto return !cnf->settime && (constraints || cnf->trusted_peers || 1171511e2d1Sotto conf->trusted_sensors) && securelevel == 0; 118841516aaSotto } 119841516aaSotto 120914fd659Shenning #define POLL_MAX 8 121914fd659Shenning #define PFD_PIPE 0 122a257dd04Sreyk #define PFD_MAX 1 123914fd659Shenning 124914fd659Shenning int 125914fd659Shenning main(int argc, char *argv[]) 126914fd659Shenning { 12796f46bd6Sotto struct ntpd_conf lconf; 128a257dd04Sreyk struct pollfd *pfd = NULL; 12978541249Srzalamena pid_t pid; 1302db3fbc0Shenning const char *conffile; 1314e840e7aSrzalamena int ch, nfds, i, j; 132914fd659Shenning int pipe_chld[2]; 13364c82965Sphessler extern char *__progname; 134a257dd04Sreyk u_int pfd_elms = 0, new_cnt; 135a257dd04Sreyk struct constraint *cstr; 136b1da763cSderaadt struct passwd *pw; 137a257dd04Sreyk void *newp; 1388d2ac903Sotto int argc0 = argc, logdest; 1394e840e7aSrzalamena char **argv0 = argv; 1404e840e7aSrzalamena char *pname = NULL; 141fe63d0d1Sotto time_t settime_deadline; 1429c341873Sderaadt int sopt = 0; 14364c82965Sphessler 14464c82965Sphessler if (strcmp(__progname, "ntpctl") == 0) { 14564c82965Sphessler ctl_main(argc, argv); 14664c82965Sphessler /* NOTREACHED */ 14764c82965Sphessler } 148914fd659Shenning 149914fd659Shenning conffile = CONFFILE; 150914fd659Shenning 151842d7e97Sbcook memset(&lconf, 0, sizeof(lconf)); 152e7e6fe2dShenning 1534e840e7aSrzalamena while ((ch = getopt(argc, argv, "df:nP:sSv")) != -1) { 154914fd659Shenning switch (ch) { 155914fd659Shenning case 'd': 1568d2ac903Sotto lconf.debug = 1; 157914fd659Shenning break; 158914fd659Shenning case 'f': 159914fd659Shenning conffile = optarg; 160914fd659Shenning break; 1618b4c855fSpyr case 'n': 1628d2ac903Sotto lconf.debug = 1; 1638b4c855fSpyr lconf.noaction = 1; 1648b4c855fSpyr break; 1654e840e7aSrzalamena case 'P': 1664e840e7aSrzalamena pname = optarg; 1674e840e7aSrzalamena break; 16810da828bShenning case 's': 1693c5d7519Shenning case 'S': 1709c341873Sderaadt sopt = ch; 1713c5d7519Shenning break; 172f5118933Sckuethe case 'v': 1733acd1ffeSbcook lconf.verbose++; 174f5118933Sckuethe break; 175914fd659Shenning default: 176914fd659Shenning usage(); 177914fd659Shenning /* NOTREACHED */ 178914fd659Shenning } 179914fd659Shenning } 180914fd659Shenning 1813acd1ffeSbcook /* log to stderr until daemonized */ 1828d2ac903Sotto logdest = LOG_TO_STDERR; 1838d2ac903Sotto if (!lconf.debug) 1848d2ac903Sotto logdest |= LOG_TO_SYSLOG; 1858d2ac903Sotto 1868d2ac903Sotto log_init(logdest, lconf.verbose, LOG_DAEMON); 1873acd1ffeSbcook 1889c341873Sderaadt if (sopt) { 1899c341873Sderaadt log_warnx("-%c option no longer works and will be removed soon.", 1909c341873Sderaadt sopt); 1919c341873Sderaadt log_warnx("Please reconfigure to use constraints or trusted servers."); 1929c341873Sderaadt } 1939c341873Sderaadt 194be24b426Spyr argc -= optind; 195be24b426Spyr argv += optind; 196be24b426Spyr if (argc > 0) 197be24b426Spyr usage(); 198be24b426Spyr 19996f46bd6Sotto if (parse_config(conffile, &lconf)) 200f64d3c3cShenning exit(1); 201f64d3c3cShenning 2028b4c855fSpyr if (lconf.noaction) { 2038b4c855fSpyr fprintf(stderr, "configuration OK\n"); 2048b4c855fSpyr exit(0); 2058b4c855fSpyr } 2068b4c855fSpyr 2078d80afc1Sclaudio if (geteuid()) 2088d80afc1Sclaudio errx(1, "need root privileges"); 209914fd659Shenning 2108d80afc1Sclaudio if ((pw = getpwnam(NTPD_USER)) == NULL) 2118d80afc1Sclaudio errx(1, "unknown user %s", NTPD_USER); 2128d80afc1Sclaudio 213841516aaSotto lconf.automatic = auto_preconditions(&lconf); 214841516aaSotto if (lconf.automatic) 215841516aaSotto lconf.settime = 1; 216841516aaSotto 2174e840e7aSrzalamena if (pname != NULL) { 2184e840e7aSrzalamena /* Remove our proc arguments, so child doesn't need to. */ 2194e840e7aSrzalamena if (sanitize_argv(&argc0, &argv0) == -1) 2204e840e7aSrzalamena fatalx("sanitize_argv"); 2214e840e7aSrzalamena 2224e840e7aSrzalamena if (strcmp(NTP_PROC_NAME, pname) == 0) 2234e840e7aSrzalamena ntp_main(&lconf, pw, argc0, argv0); 2244e840e7aSrzalamena else if (strcmp(NTPDNS_PROC_NAME, pname) == 0) 2254e840e7aSrzalamena ntp_dns(&lconf, pw); 2265f14684eSrzalamena else if (strcmp(CONSTRAINT_PROC_NAME, pname) == 0) 2275f14684eSrzalamena priv_constraint_child(pw->pw_dir, pw->pw_uid, 2285f14684eSrzalamena pw->pw_gid); 2294e840e7aSrzalamena else 2304e840e7aSrzalamena fatalx("%s: invalid process name '%s'", __func__, 2314e840e7aSrzalamena pname); 2324e840e7aSrzalamena 2334e840e7aSrzalamena fatalx("%s: process '%s' failed", __func__, pname); 234febce360Sflorian } else { 235febce360Sflorian if ((control_check(CTLSOCKET)) == -1) 236febce360Sflorian fatalx("ntpd already running"); 2374e840e7aSrzalamena } 2384e840e7aSrzalamena 239832033eaSdtucker if (setpriority(PRIO_PROCESS, 0, -20) == -1) 240832033eaSdtucker warn("can't set priority"); 241e10f1ca1Sotto reset_adjtime(); 2428d2ac903Sotto 2438d2ac903Sotto logdest = lconf.debug ? LOG_TO_STDERR : LOG_TO_SYSLOG; 24496f46bd6Sotto if (!lconf.settime) { 2458d2ac903Sotto log_init(logdest, lconf.verbose, LOG_DAEMON); 24696f46bd6Sotto if (!lconf.debug) 247f4aea9dcShenning if (daemon(1, 0)) 248f4aea9dcShenning fatal("daemon"); 249fe63d0d1Sotto } else { 250fe63d0d1Sotto settime_deadline = getmonotime(); 251fe63d0d1Sotto timeout = 100; 252fe63d0d1Sotto } 253914fd659Shenning 2544e840e7aSrzalamena if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, PF_UNSPEC, 2554e840e7aSrzalamena pipe_chld) == -1) 2561230b53bShenning fatal("socketpair"); 257914fd659Shenning 25850c479e0Sderaadt if (chdir("/") == -1) 25950c479e0Sderaadt fatal("chdir(\"/\")"); 26050c479e0Sderaadt 2612fcd4b6eSotto signal(SIGCHLD, sighdlr); 2624e840e7aSrzalamena 263f4aea9dcShenning /* fork child process */ 26478541249Srzalamena start_child(NTP_PROC_NAME, pipe_chld[1], argc0, argv0); 265914fd659Shenning 266579813e4Sreyk log_procinit("[priv]"); 267edb831d1Sotto readfreq(); 268914fd659Shenning 269914fd659Shenning signal(SIGTERM, sighdlr); 270914fd659Shenning signal(SIGINT, sighdlr); 271914fd659Shenning signal(SIGHUP, sighdlr); 272914fd659Shenning 273a257dd04Sreyk constraint_purge(); 274914fd659Shenning 27538da1314Shenning if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL) 27638da1314Shenning fatal(NULL); 277*f1b790a5Sclaudio if (imsgbuf_init(ibuf, pipe_chld[0]) == -1) 278*f1b790a5Sclaudio fatal(NULL); 279914fd659Shenning 280a257dd04Sreyk constraint_cnt = 0; 281a257dd04Sreyk 282348cf342Sderaadt /* 283348cf342Sderaadt * Constraint processes are forked with certificates in memory, 284348cf342Sderaadt * then privdrop into chroot before speaking to the outside world. 285348cf342Sderaadt */ 28601a88081Sderaadt if (unveil("/usr/sbin/ntpd", "x") == -1) 287bc5a8259Sbeck err(1, "unveil /usr/sbin/ntpd"); 2881fa741bdSmestre if (pledge("stdio settime proc exec", NULL) == -1) 289348cf342Sderaadt err(1, "pledge"); 290348cf342Sderaadt 291914fd659Shenning while (quit == 0) { 292a257dd04Sreyk new_cnt = PFD_MAX + constraint_cnt; 293a257dd04Sreyk if (new_cnt > pfd_elms) { 294a257dd04Sreyk if ((newp = reallocarray(pfd, new_cnt, 295a257dd04Sreyk sizeof(*pfd))) == NULL) { 296a257dd04Sreyk /* panic for now */ 297a257dd04Sreyk log_warn("could not resize pfd from %u -> " 298a257dd04Sreyk "%u entries", pfd_elms, new_cnt); 299a257dd04Sreyk fatalx("exiting"); 300a257dd04Sreyk } 301a257dd04Sreyk pfd = newp; 302a257dd04Sreyk pfd_elms = new_cnt; 303a257dd04Sreyk } 304a257dd04Sreyk 305a257dd04Sreyk memset(pfd, 0, sizeof(*pfd) * pfd_elms); 30638da1314Shenning pfd[PFD_PIPE].fd = ibuf->fd; 307914fd659Shenning pfd[PFD_PIPE].events = POLLIN; 30831be28caSclaudio if (imsgbuf_queuelen(ibuf) > 0) 309914fd659Shenning pfd[PFD_PIPE].events |= POLLOUT; 310914fd659Shenning 311a257dd04Sreyk i = PFD_MAX; 312a257dd04Sreyk TAILQ_FOREACH(cstr, &conf->constraints, entry) { 313a257dd04Sreyk pfd[i].fd = cstr->fd; 314a257dd04Sreyk pfd[i].events = POLLIN; 315a257dd04Sreyk i++; 316a257dd04Sreyk } 317a257dd04Sreyk 318a257dd04Sreyk if ((nfds = poll(pfd, i, timeout)) == -1) 319914fd659Shenning if (errno != EINTR) { 320914fd659Shenning log_warn("poll error"); 321914fd659Shenning quit = 1; 322914fd659Shenning } 323914fd659Shenning 324fe63d0d1Sotto if (nfds == 0 && lconf.settime && 325fe63d0d1Sotto getmonotime() > settime_deadline + SETTIME_TIMEOUT) { 32696f46bd6Sotto lconf.settime = 0; 32710da828bShenning timeout = INFTIM; 3288d2ac903Sotto log_init(logdest, lconf.verbose, LOG_DAEMON); 329aa81f020Sbcook log_warnx("no reply received in time, skipping initial " 3309fb4a489Shenning "time setting"); 33196f46bd6Sotto if (!lconf.debug) 332f4aea9dcShenning if (daemon(1, 0)) 333f4aea9dcShenning fatal("daemon"); 33410da828bShenning } 33510da828bShenning 336914fd659Shenning if (nfds > 0 && (pfd[PFD_PIPE].revents & POLLOUT)) 337dd7efffeSclaudio if (imsgbuf_write(ibuf) == -1) { 338519e28a9Smoritz log_warn("pipe write error (to child)"); 339914fd659Shenning quit = 1; 340914fd659Shenning } 341914fd659Shenning 342914fd659Shenning if (nfds > 0 && pfd[PFD_PIPE].revents & POLLIN) { 343914fd659Shenning nfds--; 3445f14684eSrzalamena if (dispatch_imsg(&lconf, argc0, argv0) == -1) 345914fd659Shenning quit = 1; 346914fd659Shenning } 347914fd659Shenning 348a257dd04Sreyk for (j = PFD_MAX; nfds > 0 && j < i; j++) { 349a257dd04Sreyk nfds -= priv_constraint_dispatch(&pfd[j]); 350a257dd04Sreyk } 351a257dd04Sreyk 352914fd659Shenning if (sigchld) { 35378541249Srzalamena check_child(); 354914fd659Shenning sigchld = 0; 355914fd659Shenning } 356914fd659Shenning } 357914fd659Shenning 35869ad80e1Shenning signal(SIGCHLD, SIG_DFL); 359914fd659Shenning 36078541249Srzalamena /* Close socket and start shutdown. */ 36178541249Srzalamena close(ibuf->fd); 362914fd659Shenning 363914fd659Shenning do { 364914fd659Shenning if ((pid = wait(NULL)) == -1 && 365914fd659Shenning errno != EINTR && errno != ECHILD) 366914fd659Shenning fatal("wait"); 367914fd659Shenning } while (pid != -1 || (pid == -1 && errno == EINTR)); 368914fd659Shenning 3699cbf9e90Sclaudio imsgbuf_clear(ibuf); 37038da1314Shenning free(ibuf); 371914fd659Shenning log_info("Terminating"); 372914fd659Shenning return (0); 373914fd659Shenning } 374914fd659Shenning 37578541249Srzalamena void 37678541249Srzalamena check_child(void) 377914fd659Shenning { 37878541249Srzalamena int status; 379a257dd04Sreyk pid_t pid; 380914fd659Shenning 381a257dd04Sreyk do { 382a257dd04Sreyk pid = waitpid(WAIT_ANY, &status, WNOHANG); 38378541249Srzalamena if (pid <= 0) 384a257dd04Sreyk continue; 385914fd659Shenning 38678541249Srzalamena priv_constraint_check_child(pid, status); 38778541249Srzalamena } while (pid > 0 || (pid == -1 && errno == EINTR)); 388914fd659Shenning } 389914fd659Shenning 390914fd659Shenning int 3915f14684eSrzalamena dispatch_imsg(struct ntpd_conf *lconf, int argc, char **argv) 392914fd659Shenning { 393914fd659Shenning struct imsg imsg; 3941964b08cSreyk int n; 395138ae74fShenning double d; 396914fd659Shenning 397f6bd242eSclaudio if (imsgbuf_read(ibuf) != 1) 398914fd659Shenning return (-1); 399914fd659Shenning 400914fd659Shenning for (;;) { 40138da1314Shenning if ((n = imsg_get(ibuf, &imsg)) == -1) 402914fd659Shenning return (-1); 403914fd659Shenning 404914fd659Shenning if (n == 0) 405914fd659Shenning break; 406914fd659Shenning 407914fd659Shenning switch (imsg.hdr.type) { 408138ae74fShenning case IMSG_ADJTIME: 409138ae74fShenning if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(d)) 410b4c9ba1fShenning fatalx("invalid IMSG_ADJTIME received"); 411138ae74fShenning memcpy(&d, imsg.data, sizeof(d)); 4127de07ab1Sdtucker n = ntpd_adjtime(d); 41396e77fb8Spyr imsg_compose(ibuf, IMSG_ADJTIME, 0, 0, -1, 41496e77fb8Spyr &n, sizeof(n)); 4155c35bc7eShenning break; 416164ca3f4Sotto case IMSG_ADJFREQ: 417164ca3f4Sotto if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(d)) 418164ca3f4Sotto fatalx("invalid IMSG_ADJFREQ received"); 419164ca3f4Sotto memcpy(&d, imsg.data, sizeof(d)); 420460e0564Sotto ntpd_adjfreq(d, 1); 421164ca3f4Sotto break; 42210da828bShenning case IMSG_SETTIME: 42310da828bShenning if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(d)) 424b4c9ba1fShenning fatalx("invalid IMSG_SETTIME received"); 42596f46bd6Sotto if (!lconf->settime) 42610da828bShenning break; 4278d2ac903Sotto log_init(lconf->debug ? LOG_TO_STDERR : LOG_TO_SYSLOG, 4288d2ac903Sotto lconf->verbose, LOG_DAEMON); 42910da828bShenning memcpy(&d, imsg.data, sizeof(d)); 43010da828bShenning ntpd_settime(d); 43110da828bShenning /* daemonize now */ 43296f46bd6Sotto if (!lconf->debug) 433f4aea9dcShenning if (daemon(1, 0)) 434f4aea9dcShenning fatal("daemon"); 43596f46bd6Sotto lconf->settime = 0; 43620529c73Shenning timeout = INFTIM; 43710da828bShenning break; 438a257dd04Sreyk case IMSG_CONSTRAINT_QUERY: 439a257dd04Sreyk priv_constraint_msg(imsg.hdr.peerid, 440b1da763cSderaadt imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE, 4415f14684eSrzalamena argc, argv); 442a257dd04Sreyk break; 4430a1ac5ecSreyk case IMSG_CONSTRAINT_KILL: 4440a1ac5ecSreyk priv_constraint_kill(imsg.hdr.peerid); 4450a1ac5ecSreyk break; 446914fd659Shenning default: 447914fd659Shenning break; 448914fd659Shenning } 449914fd659Shenning imsg_free(&imsg); 450914fd659Shenning } 451914fd659Shenning return (0); 452914fd659Shenning } 453138ae74fShenning 454e10f1ca1Sotto void 455e10f1ca1Sotto reset_adjtime(void) 456e10f1ca1Sotto { 457e10f1ca1Sotto struct timeval tv; 458e10f1ca1Sotto 459bd5070a8Sokan timerclear(&tv); 460e10f1ca1Sotto if (adjtime(&tv, NULL) == -1) 461e10f1ca1Sotto log_warn("reset adjtime failed"); 462e10f1ca1Sotto } 463e10f1ca1Sotto 4647de07ab1Sdtucker int 465138ae74fShenning ntpd_adjtime(double d) 466138ae74fShenning { 4677de07ab1Sdtucker struct timeval tv, olddelta; 4687de07ab1Sdtucker int synced = 0; 4697de07ab1Sdtucker static int firstadj = 1; 470138ae74fShenning 4714e6294c2Sotto d += getoffset(); 472b17618d7Sstevesk if (d >= (double)LOG_NEGLIGIBLE_ADJTIME / 1000 || 473b17618d7Sstevesk d <= -1 * (double)LOG_NEGLIGIBLE_ADJTIME / 1000) 474b5c6c051Shenning log_info("adjusting local clock by %fs", d); 47596d66300Smickey else 47696d66300Smickey log_debug("adjusting local clock by %fs", d); 47796d66300Smickey d_to_tv(d, &tv); 4787de07ab1Sdtucker if (adjtime(&tv, &olddelta) == -1) 479138ae74fShenning log_warn("adjtime failed"); 4807de07ab1Sdtucker else if (!firstadj && olddelta.tv_sec == 0 && olddelta.tv_usec == 0) 4817de07ab1Sdtucker synced = 1; 4827de07ab1Sdtucker firstadj = 0; 4837de07ab1Sdtucker return (synced); 484138ae74fShenning } 48510da828bShenning 48610da828bShenning void 487460e0564Sotto ntpd_adjfreq(double relfreq, int wrlog) 488164ca3f4Sotto { 489164ca3f4Sotto int64_t curfreq; 490d45de13aSstevesk double ppmfreq; 491a5e2cc1cSstevesk int r; 492164ca3f4Sotto 493164ca3f4Sotto if (adjfreq(NULL, &curfreq) == -1) { 494164ca3f4Sotto log_warn("adjfreq failed"); 495164ca3f4Sotto return; 496164ca3f4Sotto } 497164ca3f4Sotto 498164ca3f4Sotto /* 499164ca3f4Sotto * adjfreq's unit is ns/s shifted left 32; convert relfreq to 500164ca3f4Sotto * that unit before adding. We log values in part per million. 501164ca3f4Sotto */ 502164ca3f4Sotto curfreq += relfreq * 1e9 * (1LL << 32); 503a5e2cc1cSstevesk r = writefreq(curfreq / 1e9 / (1LL << 32)); 504d45de13aSstevesk ppmfreq = relfreq * 1e6; 505d45de13aSstevesk if (wrlog) { 506d45de13aSstevesk if (ppmfreq >= LOG_NEGLIGIBLE_ADJFREQ || 507d45de13aSstevesk ppmfreq <= -LOG_NEGLIGIBLE_ADJFREQ) 508a5e2cc1cSstevesk log_info("adjusting clock frequency by %f to %fppm%s", 509d45de13aSstevesk ppmfreq, curfreq / 1e3 / (1LL << 32), 510a5e2cc1cSstevesk r ? "" : " (no drift file)"); 511d45de13aSstevesk else 512d45de13aSstevesk log_debug("adjusting clock frequency by %f to %fppm%s", 513d45de13aSstevesk ppmfreq, curfreq / 1e3 / (1LL << 32), 514d45de13aSstevesk r ? "" : " (no drift file)"); 515d45de13aSstevesk } 516164ca3f4Sotto 517164ca3f4Sotto if (adjfreq(&curfreq, NULL) == -1) 518164ca3f4Sotto log_warn("adjfreq failed"); 519164ca3f4Sotto } 520164ca3f4Sotto 521164ca3f4Sotto void 52210da828bShenning ntpd_settime(double d) 52310da828bShenning { 52410da828bShenning struct timeval tv, curtime; 52510da828bShenning char buf[80]; 52610da828bShenning time_t tval; 52710da828bShenning 528841516aaSotto if (d == 0) 529841516aaSotto return; 530841516aaSotto 531f8ce85c6Sdtucker if (gettimeofday(&curtime, NULL) == -1) { 53210da828bShenning log_warn("gettimeofday"); 533f8ce85c6Sdtucker return; 53410da828bShenning } 535f8ce85c6Sdtucker d_to_tv(d, &tv); 536f8ce85c6Sdtucker curtime.tv_usec += tv.tv_usec + 1000000; 537f8ce85c6Sdtucker curtime.tv_sec += tv.tv_sec - 1 + (curtime.tv_usec / 1000000); 538f8ce85c6Sdtucker curtime.tv_usec %= 1000000; 539f8ce85c6Sdtucker 540f8ce85c6Sdtucker if (settimeofday(&curtime, NULL) == -1) { 54110da828bShenning log_warn("settimeofday"); 542f8ce85c6Sdtucker return; 543f8ce85c6Sdtucker } 54410da828bShenning tval = curtime.tv_sec; 54510da828bShenning strftime(buf, sizeof(buf), "%a %b %e %H:%M:%S %Z %Y", 54610da828bShenning localtime(&tval)); 54710da828bShenning log_info("set local clock to %s (offset %fs)", buf, d); 54810da828bShenning } 549edb831d1Sotto 550348cf342Sderaadt static FILE *freqfp; 551348cf342Sderaadt 552edb831d1Sotto void 553edb831d1Sotto readfreq(void) 554edb831d1Sotto { 555edb831d1Sotto int64_t current; 556348cf342Sderaadt int fd; 557edb831d1Sotto double d; 558edb831d1Sotto 559348cf342Sderaadt fd = open(DRIFTFILE, O_RDWR); 560348cf342Sderaadt if (fd == -1) { 561348cf342Sderaadt log_warnx("creating new %s", DRIFTFILE); 56214e938f1Sotto current = 0; 56314e938f1Sotto if (adjfreq(¤t, NULL) == -1) 56414e938f1Sotto log_warn("adjfreq reset failed"); 565348cf342Sderaadt freqfp = fopen(DRIFTFILE, "w"); 566edb831d1Sotto return; 567edb831d1Sotto } 568edb831d1Sotto 569348cf342Sderaadt freqfp = fdopen(fd, "r+"); 570348cf342Sderaadt 57114e938f1Sotto /* if we're adjusting frequency already, don't override */ 57214e938f1Sotto if (adjfreq(NULL, ¤t) == -1) 57314e938f1Sotto log_warn("adjfreq failed"); 574348cf342Sderaadt else if (current == 0 && freqfp) { 575348cf342Sderaadt if (fscanf(freqfp, "%lf", &d) == 1) { 5764b3d9004Snaddy d /= 1e6; /* scale from ppm */ 577460e0564Sotto ntpd_adjfreq(d, 0); 5784b3d9004Snaddy } else 579348cf342Sderaadt log_warnx("%s is empty", DRIFTFILE); 58014e938f1Sotto } 581edb831d1Sotto } 582edb831d1Sotto 583a5e2cc1cSstevesk int 584edb831d1Sotto writefreq(double d) 585edb831d1Sotto { 586edb831d1Sotto int r; 587a5e2cc1cSstevesk static int warnonce = 1; 588edb831d1Sotto 589348cf342Sderaadt if (freqfp == NULL) 590a5e2cc1cSstevesk return 0; 591348cf342Sderaadt rewind(freqfp); 59209522baaSmillert r = fprintf(freqfp, "%.3f\n", d * 1e6); /* scale to ppm */ 59309522baaSmillert if (r < 0 || fflush(freqfp) != 0) { 594a5e2cc1cSstevesk if (warnonce) { 595a5e2cc1cSstevesk log_warnx("can't write %s", DRIFTFILE); 596a5e2cc1cSstevesk warnonce = 0; 597a5e2cc1cSstevesk } 59809522baaSmillert clearerr(freqfp); 599a5e2cc1cSstevesk return 0; 600a5e2cc1cSstevesk } 601348cf342Sderaadt ftruncate(fileno(freqfp), ftello(freqfp)); 602348cf342Sderaadt fsync(fileno(freqfp)); 603a5e2cc1cSstevesk return 1; 604edb831d1Sotto } 60564c82965Sphessler 60664c82965Sphessler void 60764c82965Sphessler ctl_main(int argc, char *argv[]) 60864c82965Sphessler { 609accd9e67Sbcook struct sockaddr_un sa; 61064c82965Sphessler struct imsg imsg; 61164c82965Sphessler struct imsgbuf *ibuf_ctl; 612ae834c44Sbenno int fd, n, done, ch, action; 613ae834c44Sbenno char *sockname; 61464c82965Sphessler 61564c82965Sphessler sockname = CTLSOCKET; 61664c82965Sphessler 61764c82965Sphessler if (argc < 2) { 61864c82965Sphessler usage(); 61964c82965Sphessler /* NOTREACHED */ 62064c82965Sphessler } 62164c82965Sphessler 62264c82965Sphessler while ((ch = getopt(argc, argv, "s:")) != -1) { 62364c82965Sphessler switch (ch) { 62464c82965Sphessler case 's': 625ae834c44Sbenno showopt = ctl_lookup_option(optarg, ctl_showopt_list); 626ae834c44Sbenno if (showopt == NULL) { 627ae834c44Sbenno warnx("Unknown show modifier '%s'", optarg); 628ae834c44Sbenno usage(); 629ae834c44Sbenno } 63064c82965Sphessler break; 63164c82965Sphessler default: 63264c82965Sphessler usage(); 63364c82965Sphessler /* NOTREACHED */ 63464c82965Sphessler } 63564c82965Sphessler } 63664c82965Sphessler 63764c82965Sphessler action = -1; 638ae834c44Sbenno if (showopt != NULL) { 639ae834c44Sbenno switch (*showopt) { 640ae834c44Sbenno case 'p': 64164c82965Sphessler action = CTL_SHOW_PEERS; 642ae834c44Sbenno break; 643ae834c44Sbenno case 's': 64464c82965Sphessler action = CTL_SHOW_STATUS; 645ae834c44Sbenno break; 646ae834c44Sbenno case 'S': 647ae834c44Sbenno action = CTL_SHOW_SENSORS; 648ae834c44Sbenno break; 649ae834c44Sbenno case 'a': 65064c82965Sphessler action = CTL_SHOW_ALL; 651ae834c44Sbenno break; 65225018bd4Sbcook } 65325018bd4Sbcook } 65425018bd4Sbcook if (action == -1) 65564c82965Sphessler usage(); 65664c82965Sphessler /* NOTREACHED */ 65764c82965Sphessler 65864c82965Sphessler if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) 65964c82965Sphessler err(1, "ntpctl: socket"); 66064c82965Sphessler 661842d7e97Sbcook memset(&sa, 0, sizeof(sa)); 662accd9e67Sbcook sa.sun_family = AF_UNIX; 663accd9e67Sbcook if (strlcpy(sa.sun_path, sockname, sizeof(sa.sun_path)) >= 664accd9e67Sbcook sizeof(sa.sun_path)) 66564c82965Sphessler errx(1, "ctl socket name too long"); 666accd9e67Sbcook if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) 66764c82965Sphessler err(1, "connect: %s", sockname); 66864c82965Sphessler 6690bd1216cSderaadt if (pledge("stdio", NULL) == -1) 6700bd1216cSderaadt err(1, "pledge"); 671a09466a0Sderaadt 67264c82965Sphessler if ((ibuf_ctl = malloc(sizeof(struct imsgbuf))) == NULL) 67364c82965Sphessler err(1, NULL); 674*f1b790a5Sclaudio if (imsgbuf_init(ibuf_ctl, fd) == -1) 675*f1b790a5Sclaudio err(1, NULL); 67664c82965Sphessler 67764c82965Sphessler switch (action) { 67864c82965Sphessler case CTL_SHOW_STATUS: 67964c82965Sphessler imsg_compose(ibuf_ctl, IMSG_CTL_SHOW_STATUS, 68064c82965Sphessler 0, 0, -1, NULL, 0); 68164c82965Sphessler break; 68264c82965Sphessler case CTL_SHOW_PEERS: 68364c82965Sphessler imsg_compose(ibuf_ctl, IMSG_CTL_SHOW_PEERS, 68464c82965Sphessler 0, 0, -1, NULL, 0); 68564c82965Sphessler break; 68664c82965Sphessler case CTL_SHOW_SENSORS: 68764c82965Sphessler imsg_compose(ibuf_ctl, IMSG_CTL_SHOW_SENSORS, 68864c82965Sphessler 0, 0, -1, NULL, 0); 68964c82965Sphessler break; 69064c82965Sphessler case CTL_SHOW_ALL: 69164c82965Sphessler imsg_compose(ibuf_ctl, IMSG_CTL_SHOW_ALL, 69264c82965Sphessler 0, 0, -1, NULL, 0); 69364c82965Sphessler break; 69464c82965Sphessler default: 69564c82965Sphessler errx(1, "invalid action"); 69664c82965Sphessler break; /* NOTREACHED */ 69764c82965Sphessler } 69864c82965Sphessler 699dd7efffeSclaudio if (imsgbuf_flush(ibuf_ctl) == -1) 700ef2e27a1Sclaudio err(1, "write error"); 70164c82965Sphessler 70264c82965Sphessler done = 0; 70364c82965Sphessler while (!done) { 704f6bd242eSclaudio if ((n = imsgbuf_read(ibuf_ctl)) == -1) 705ef2e27a1Sclaudio err(1, "read error"); 70664c82965Sphessler if (n == 0) 707ef2e27a1Sclaudio errx(1, "pipe closed"); 70864c82965Sphessler 70964c82965Sphessler while (!done) { 71064c82965Sphessler if ((n = imsg_get(ibuf_ctl, &imsg)) == -1) 71164c82965Sphessler err(1, "ibuf_ctl: imsg_get error"); 71264c82965Sphessler if (n == 0) 71364c82965Sphessler break; 71464c82965Sphessler 71564c82965Sphessler switch (action) { 71664c82965Sphessler case CTL_SHOW_STATUS: 71764c82965Sphessler show_status_msg(&imsg); 71864c82965Sphessler done = 1; 71964c82965Sphessler break; 72064c82965Sphessler case CTL_SHOW_PEERS: 72164c82965Sphessler show_peer_msg(&imsg, 0); 72264c82965Sphessler if (imsg.hdr.type == 72364c82965Sphessler IMSG_CTL_SHOW_PEERS_END) 72464c82965Sphessler done = 1; 72564c82965Sphessler break; 72664c82965Sphessler case CTL_SHOW_SENSORS: 72764c82965Sphessler show_sensor_msg(&imsg, 0); 72864c82965Sphessler if (imsg.hdr.type == 72964c82965Sphessler IMSG_CTL_SHOW_SENSORS_END) 73064c82965Sphessler done = 1; 73164c82965Sphessler break; 73264c82965Sphessler case CTL_SHOW_ALL: 73364c82965Sphessler switch (imsg.hdr.type) { 73464c82965Sphessler case IMSG_CTL_SHOW_STATUS: 73564c82965Sphessler show_status_msg(&imsg); 73664c82965Sphessler break; 73764c82965Sphessler case IMSG_CTL_SHOW_PEERS: 73864c82965Sphessler show_peer_msg(&imsg, 1); 73964c82965Sphessler break; 74064c82965Sphessler case IMSG_CTL_SHOW_SENSORS: 74164c82965Sphessler show_sensor_msg(&imsg, 1); 74264c82965Sphessler break; 74364c82965Sphessler case IMSG_CTL_SHOW_PEERS_END: 74464c82965Sphessler case IMSG_CTL_SHOW_SENSORS_END: 74564c82965Sphessler /* do nothing */ 74664c82965Sphessler break; 74764c82965Sphessler case IMSG_CTL_SHOW_ALL_END: 74864c82965Sphessler done=1; 74964c82965Sphessler break; 75064c82965Sphessler default: 75164c82965Sphessler /* no action taken */ 75264c82965Sphessler break; 75364c82965Sphessler } 75464c82965Sphessler default: 75564c82965Sphessler /* no action taken */ 75664c82965Sphessler break; 75764c82965Sphessler } 75864c82965Sphessler imsg_free(&imsg); 75964c82965Sphessler } 76064c82965Sphessler } 76164c82965Sphessler close(fd); 76264c82965Sphessler free(ibuf_ctl); 76364c82965Sphessler exit(0); 76464c82965Sphessler } 76564c82965Sphessler 766ae834c44Sbenno const char * 767ae834c44Sbenno ctl_lookup_option(char *cmd, const char **list) 768ae834c44Sbenno { 769ae834c44Sbenno const char *item = NULL; 770ae834c44Sbenno if (cmd != NULL && *cmd) 771ae834c44Sbenno for (; *list; list++) 772ae834c44Sbenno if (!strncmp(cmd, *list, strlen(cmd))) { 773ae834c44Sbenno if (item == NULL) 774ae834c44Sbenno item = *list; 775ae834c44Sbenno else 7762723b2b7Ssthen errx(1, "%s is ambiguous", cmd); 777ae834c44Sbenno } 778ae834c44Sbenno return (item); 779ae834c44Sbenno } 780ae834c44Sbenno 78164c82965Sphessler void 78264c82965Sphessler show_status_msg(struct imsg *imsg) 78364c82965Sphessler { 78464c82965Sphessler struct ctl_show_status *cstatus; 78564c82965Sphessler double clock_offset; 786bc58a738Sreyk struct timeval tv; 78764c82965Sphessler 78864c82965Sphessler if (imsg->hdr.len != IMSG_HEADER_SIZE + sizeof(struct ctl_show_status)) 78964c82965Sphessler fatalx("invalid IMSG_CTL_SHOW_STATUS received"); 79064c82965Sphessler 79164c82965Sphessler cstatus = (struct ctl_show_status *)imsg->data; 79264c82965Sphessler 79364c82965Sphessler if (cstatus->peercnt > 0) 7941ae089b6Sderaadt printf("%d/%d peers valid, ", 79564c82965Sphessler cstatus->valid_peers, cstatus->peercnt); 79664c82965Sphessler 79764c82965Sphessler if (cstatus->sensorcnt > 0) 7981ae089b6Sderaadt printf("%d/%d sensors valid, ", 79964c82965Sphessler cstatus->valid_sensors, cstatus->sensorcnt); 80064c82965Sphessler 801bc58a738Sreyk if (cstatus->constraint_median) { 802bc58a738Sreyk tv.tv_sec = cstatus->constraint_median + 803bc58a738Sreyk (getmonotime() - cstatus->constraint_last); 804bc58a738Sreyk tv.tv_usec = 0; 805a5f7863aSreyk d_to_tv(gettime_from_timeval(&tv) - gettime(), &tv); 806a5f7863aSreyk printf("constraint offset %llds", (long long)tv.tv_sec); 807bc58a738Sreyk if (cstatus->constraint_errors) 808bc58a738Sreyk printf(" (%d errors)", 809bc58a738Sreyk cstatus->constraint_errors); 810bc58a738Sreyk printf(", "); 8111908d877Sotto } else if (cstatus->constraints) 8121908d877Sotto printf("constraints configured but none available, "); 813bc58a738Sreyk 81464c82965Sphessler if (cstatus->peercnt + cstatus->sensorcnt == 0) 81564c82965Sphessler printf("no peers and no sensors configured\n"); 81664c82965Sphessler 81764c82965Sphessler if (cstatus->synced == 1) 8181ae089b6Sderaadt printf("clock synced, stratum %u\n", cstatus->stratum); 81964c82965Sphessler else { 8201ae089b6Sderaadt printf("clock unsynced"); 82164c82965Sphessler clock_offset = cstatus->clock_offset < 0 ? 82264c82965Sphessler -1.0 * cstatus->clock_offset : cstatus->clock_offset; 82364c82965Sphessler if (clock_offset > 5e-7) 82464c82965Sphessler printf(", clock offset is %.3fms\n", 82564c82965Sphessler cstatus->clock_offset); 82664c82965Sphessler else 82764c82965Sphessler printf("\n"); 82864c82965Sphessler } 82964c82965Sphessler } 83064c82965Sphessler 83164c82965Sphessler void 83264c82965Sphessler show_peer_msg(struct imsg *imsg, int calledfromshowall) 83364c82965Sphessler { 83464c82965Sphessler struct ctl_show_peer *cpeer; 83564c82965Sphessler int cnt; 83664c82965Sphessler char stratum[3]; 83764c82965Sphessler static int firsttime = 1; 83864c82965Sphessler 83964c82965Sphessler if (imsg->hdr.type == IMSG_CTL_SHOW_PEERS_END) { 84064c82965Sphessler if (imsg->hdr.len != IMSG_HEADER_SIZE + sizeof(cnt)) 84164c82965Sphessler fatalx("invalid IMSG_CTL_SHOW_PEERS_END received"); 84264c82965Sphessler memcpy(&cnt, imsg->data, sizeof(cnt)); 84364c82965Sphessler if (cnt == 0) 84464c82965Sphessler printf("no peers configured\n"); 84564c82965Sphessler return; 84664c82965Sphessler } 84764c82965Sphessler 84864c82965Sphessler if (imsg->hdr.len != IMSG_HEADER_SIZE + sizeof(struct ctl_show_peer)) 84964c82965Sphessler fatalx("invalid IMSG_CTL_SHOW_PEERS received"); 85064c82965Sphessler 85164c82965Sphessler cpeer = (struct ctl_show_peer *)imsg->data; 85264c82965Sphessler 85364c82965Sphessler if (strlen(cpeer->peer_desc) > MAX_DISPLAY_WIDTH - 1) 85464c82965Sphessler fatalx("peer_desc is too long"); 85564c82965Sphessler 85664c82965Sphessler if (firsttime) { 85764c82965Sphessler firsttime = 0; 85864c82965Sphessler if (calledfromshowall) 85964c82965Sphessler printf("\n"); 86064c82965Sphessler printf("peer\n wt tl st next poll " 86164c82965Sphessler "offset delay jitter\n"); 86264c82965Sphessler } 86364c82965Sphessler 86464c82965Sphessler if (cpeer->stratum > 0) 86564c82965Sphessler snprintf(stratum, sizeof(stratum), "%2u", cpeer->stratum); 86664c82965Sphessler else 86764c82965Sphessler strlcpy(stratum, " -", sizeof (stratum)); 86864c82965Sphessler 869aa47326aSschwarze printf("%s\n %1s %2u %2u %2s %4llds %4llds", 87064c82965Sphessler cpeer->peer_desc, cpeer->syncedto == 1 ? "*" : " ", 871aa47326aSschwarze cpeer->weight, cpeer->trustlevel, stratum, 872aa47326aSschwarze (long long)cpeer->next, (long long)cpeer->poll); 87364c82965Sphessler 87464c82965Sphessler if (cpeer->trustlevel >= TRUSTLEVEL_BADPEER) 87564c82965Sphessler printf(" %12.3fms %9.3fms %8.3fms\n", cpeer->offset, 87664c82965Sphessler cpeer->delay, cpeer->jitter); 87764c82965Sphessler else 87864c82965Sphessler printf(" ---- peer not valid ----\n"); 87964c82965Sphessler 88064c82965Sphessler } 88164c82965Sphessler 88264c82965Sphessler void 88364c82965Sphessler show_sensor_msg(struct imsg *imsg, int calledfromshowall) 88464c82965Sphessler { 88564c82965Sphessler struct ctl_show_sensor *csensor; 88664c82965Sphessler int cnt; 88764c82965Sphessler static int firsttime = 1; 88864c82965Sphessler 88964c82965Sphessler if (imsg->hdr.type == IMSG_CTL_SHOW_SENSORS_END) { 89064c82965Sphessler if (imsg->hdr.len != IMSG_HEADER_SIZE + sizeof(cnt)) 89164c82965Sphessler fatalx("invalid IMSG_CTL_SHOW_SENSORS_END received"); 89264c82965Sphessler memcpy(&cnt, imsg->data, sizeof(cnt)); 89364c82965Sphessler if (cnt == 0) 89464c82965Sphessler printf("no sensors configured\n"); 89564c82965Sphessler return; 89664c82965Sphessler } 89764c82965Sphessler 89864c82965Sphessler if (imsg->hdr.len != IMSG_HEADER_SIZE + sizeof(struct ctl_show_sensor)) 89964c82965Sphessler fatalx("invalid IMSG_CTL_SHOW_SENSORS received"); 90064c82965Sphessler 90164c82965Sphessler csensor = (struct ctl_show_sensor *)imsg->data; 90264c82965Sphessler 90364c82965Sphessler if (strlen(csensor->sensor_desc) > MAX_DISPLAY_WIDTH - 1) 90464c82965Sphessler fatalx("sensor_desc is too long"); 90564c82965Sphessler 90664c82965Sphessler if (firsttime) { 90764c82965Sphessler firsttime = 0; 90864c82965Sphessler if (calledfromshowall) 90964c82965Sphessler printf("\n"); 91064c82965Sphessler printf("sensor\n wt gd st next poll " 91164c82965Sphessler "offset correction\n"); 91264c82965Sphessler } 91364c82965Sphessler 914aa47326aSschwarze printf("%s\n %1s %2u %2u %2u %4llds %4llds", 915aa47326aSschwarze csensor->sensor_desc, csensor->syncedto == 1 ? "*" : " ", 916aa47326aSschwarze csensor->weight, csensor->good, csensor->stratum, 917aa47326aSschwarze (long long)csensor->next, (long long)csensor->poll); 91864c82965Sphessler 91964c82965Sphessler if (csensor->good == 1) 92064c82965Sphessler printf(" %11.3fms %9.3fms\n", 92164c82965Sphessler csensor->offset, csensor->correction); 92264c82965Sphessler else 92364c82965Sphessler printf(" - sensor not valid -\n"); 92464c82965Sphessler 92564c82965Sphessler } 926