/* $OpenBSD: sensors.c,v 1.46 2012/09/20 12:43:16 patrick Exp $ */ /* * Copyright (c) 2006 Henning Brauer * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "ntpd.h" #define MAXDEVNAMLEN 16 #define _PATH_DEV_HOTPLUG "/dev/hotplug" int sensor_probe(int, char *, struct sensor *); void sensor_add(int, char *); void sensor_remove(struct ntp_sensor *); void sensor_update(struct ntp_sensor *); void sensor_init(void) { TAILQ_INIT(&conf->ntp_sensors); } int sensor_scan(void) { int i, n, err; char d[MAXDEVNAMLEN]; struct sensor s; n = 0; for (i = 0; ; i++) if ((err = sensor_probe(i, d, &s))) { if (err == 0) continue; if (err == -1) /* no further sensors */ break; sensor_add(i, d); n++; } return n; } /* * 1 = time sensor! * 0 = sensor exists... but is not a time sensor * -1: no sensor here, and no further sensors after this */ int sensor_probe(int devid, char *dxname, struct sensor *sensor) { int mib[5]; size_t slen, sdlen; struct sensordev sensordev; mib[0] = CTL_HW; mib[1] = HW_SENSORS; mib[2] = devid; mib[3] = SENSOR_TIMEDELTA; mib[4] = 0; sdlen = sizeof(sensordev); if (sysctl(mib, 3, &sensordev, &sdlen, NULL, 0) == -1) { if (errno == ENXIO) return (0); if (errno == ENOENT) return (-1); log_warn("sensor_probe sysctl"); } if (sensordev.maxnumt[SENSOR_TIMEDELTA] == 0) return (0); strlcpy(dxname, sensordev.xname, MAXDEVNAMLEN); slen = sizeof(*sensor); if (sysctl(mib, 5, sensor, &slen, NULL, 0) == -1) { if (errno != ENOENT) log_warn("sensor_probe sysctl"); return (0); } return (1); } void sensor_add(int sensordev, char *dxname) { struct ntp_sensor *s; struct ntp_conf_sensor *cs; /* check whether it is already there */ TAILQ_FOREACH(s, &conf->ntp_sensors, entry) if (!strcmp(s->device, dxname)) return; /* check whether it is requested in the config file */ for (cs = TAILQ_FIRST(&conf->ntp_conf_sensors); cs != NULL && strcmp(cs->device, dxname) && strcmp(cs->device, "*"); cs = TAILQ_NEXT(cs, entry)) ; /* nothing */ if (cs == NULL) return; if ((s = calloc(1, sizeof(*s))) == NULL) fatal("sensor_add calloc"); s->next = getmonotime(); s->weight = cs->weight; s->correction = cs->correction; s->stratum = cs->stratum - 1; if ((s->device = strdup(dxname)) == NULL) fatal("sensor_add strdup"); s->sensordevid = sensordev; if (cs->refstr == NULL) memcpy(&s->refid, "HARD", sizeof(s->refid)); else { s->refid = 0; strncpy((char *)&s->refid, cs->refstr, sizeof(s->refid)); } TAILQ_INSERT_TAIL(&conf->ntp_sensors, s, entry); log_debug("sensor %s added (weight %d, correction %.6f, refstr %.4s, " "stratum %d)", s->device, s->weight, s->correction / 1e6, &s->refid, s->stratum); } void sensor_remove(struct ntp_sensor *s) { TAILQ_REMOVE(&conf->ntp_sensors, s, entry); free(s->device); free(s); } void sensor_query(struct ntp_sensor *s) { char dxname[MAXDEVNAMLEN]; struct sensor sensor; if (conf->settime) s->next = getmonotime() + SENSOR_QUERY_INTERVAL_SETTIME; else s->next = getmonotime() + SENSOR_QUERY_INTERVAL; /* rcvd is walltime here, monotime in client.c. not used elsewhere */ if (s->update.rcvd < time(NULL) - SENSOR_DATA_MAXAGE) s->update.good = 0; if (!sensor_probe(s->sensordevid, dxname, &sensor)) { sensor_remove(s); return; } if (sensor.flags & SENSOR_FINVALID || sensor.status != SENSOR_S_OK) return; if (strcmp(dxname, s->device)) { sensor_remove(s); return; } if (sensor.tv.tv_sec == s->last) /* already seen */ return; s->last = sensor.tv.tv_sec; /* * TD = device time * TS = system time * sensor.value = TS - TD in ns * if value is positive, system time is ahead */ s->offsets[s->shift].offset = (sensor.value / -1e9) - getoffset() + (s->correction / 1e6); s->offsets[s->shift].rcvd = sensor.tv.tv_sec; s->offsets[s->shift].good = 1; s->offsets[s->shift].status.send_refid = s->refid; /* stratum increased when sent out */ s->offsets[s->shift].status.stratum = s->stratum; s->offsets[s->shift].status.rootdelay = 0; s->offsets[s->shift].status.rootdispersion = 0; s->offsets[s->shift].status.reftime = sensor.tv.tv_sec; s->offsets[s->shift].status.synced = 1; log_debug("sensor %s: offset %f", s->device, s->offsets[s->shift].offset); if (++s->shift >= SENSOR_OFFSETS) { s->shift = 0; sensor_update(s); } } void sensor_update(struct ntp_sensor *s) { struct ntp_offset **offsets; int i; if ((offsets = calloc(SENSOR_OFFSETS, sizeof(struct ntp_offset *))) == NULL) fatal("calloc sensor_update"); for (i = 0; i < SENSOR_OFFSETS; i++) offsets[i] = &s->offsets[i]; qsort(offsets, SENSOR_OFFSETS, sizeof(struct ntp_offset *), offset_compare); i = SENSOR_OFFSETS / 2; memcpy(&s->update, offsets[i], sizeof(s->update)); if (SENSOR_OFFSETS % 2 == 0) { s->update.offset = (offsets[i - 1]->offset + offsets[i]->offset) / 2; } free(offsets); log_debug("sensor update %s: offset %f", s->device, s->update.offset); priv_adjtime(); } int sensor_hotplugfd(void) { #ifdef notyet int fd, flags; if ((fd = open(_PATH_DEV_HOTPLUG, O_RDONLY, 0)) == -1) { log_warn("open %s", _PATH_DEV_HOTPLUG); return (-1); } if ((flags = fcntl(fd, F_GETFL, 0)) == -1) fatal("fcntl F_GETFL"); flags |= O_NONBLOCK; if ((flags = fcntl(fd, F_SETFL, flags)) == -1) fatal("fcntl F_SETFL"); return (fd); #else return (-1); #endif } void sensor_hotplugevent(int fd) { struct hotplug_event he; ssize_t n; do { if ((n = read(fd, &he, sizeof(he))) == -1 && errno != EINTR && errno != EAGAIN) fatal("sensor_hotplugevent read"); if (n == sizeof(he)) switch (he.he_type) { case HOTPLUG_DEVAT: if (he.he_devclass == DV_DULL && !strcmp(he.he_devname, "sensordev")) sensor_scan(); break; default: /* ignore */ break; } else if (n > 0) fatal("sensor_hotplugevent: short read"); } while (n > 0 || (n == -1 && errno == EINTR)); }