1 /* $OpenBSD: sensors.c,v 1.49 2015/01/09 07:35:37 deraadt Exp $ */ 2 3 /* 4 * Copyright (c) 2006 Henning Brauer <henning@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 #include <sys/queue.h> 21 #include <sys/sensors.h> 22 #include <sys/sysctl.h> 23 #include <sys/device.h> 24 #include <sys/hotplug.h> 25 26 #include <errno.h> 27 #include <fcntl.h> 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <string.h> 31 #include <unistd.h> 32 33 #include "ntpd.h" 34 35 #define MAXDEVNAMLEN 16 36 #define _PATH_DEV_HOTPLUG "/dev/hotplug" 37 38 int sensor_probe(int, char *, struct sensor *); 39 void sensor_add(int, char *); 40 void sensor_remove(struct ntp_sensor *); 41 void sensor_update(struct ntp_sensor *); 42 43 void 44 sensor_init(void) 45 { 46 TAILQ_INIT(&conf->ntp_sensors); 47 } 48 49 int 50 sensor_scan(void) 51 { 52 int i, n, err; 53 char d[MAXDEVNAMLEN]; 54 struct sensor s; 55 56 n = 0; 57 for (i = 0; ; i++) 58 if ((err = sensor_probe(i, d, &s))) { 59 if (err == 0) 60 continue; 61 if (err == -1) /* no further sensors */ 62 break; 63 sensor_add(i, d); 64 n++; 65 } 66 67 return n; 68 } 69 70 /* 71 * 1 = time sensor! 72 * 0 = sensor exists... but is not a time sensor 73 * -1: no sensor here, and no further sensors after this 74 */ 75 int 76 sensor_probe(int devid, char *dxname, struct sensor *sensor) 77 { 78 int mib[5]; 79 size_t slen, sdlen; 80 struct sensordev sensordev; 81 82 mib[0] = CTL_HW; 83 mib[1] = HW_SENSORS; 84 mib[2] = devid; 85 mib[3] = SENSOR_TIMEDELTA; 86 mib[4] = 0; 87 88 sdlen = sizeof(sensordev); 89 if (sysctl(mib, 3, &sensordev, &sdlen, NULL, 0) == -1) { 90 if (errno == ENXIO) 91 return (0); 92 if (errno == ENOENT) 93 return (-1); 94 log_warn("sensor_probe sysctl"); 95 } 96 97 if (sensordev.maxnumt[SENSOR_TIMEDELTA] == 0) 98 return (0); 99 100 strlcpy(dxname, sensordev.xname, MAXDEVNAMLEN); 101 102 slen = sizeof(*sensor); 103 if (sysctl(mib, 5, sensor, &slen, NULL, 0) == -1) { 104 if (errno != ENOENT) 105 log_warn("sensor_probe sysctl"); 106 return (0); 107 } 108 109 return (1); 110 } 111 112 void 113 sensor_add(int sensordev, char *dxname) 114 { 115 struct ntp_sensor *s; 116 struct ntp_conf_sensor *cs; 117 118 /* check whether it is already there */ 119 TAILQ_FOREACH(s, &conf->ntp_sensors, entry) 120 if (!strcmp(s->device, dxname)) 121 return; 122 123 /* check whether it is requested in the config file */ 124 for (cs = TAILQ_FIRST(&conf->ntp_conf_sensors); cs != NULL && 125 strcmp(cs->device, dxname) && strcmp(cs->device, "*"); 126 cs = TAILQ_NEXT(cs, entry)) 127 ; /* nothing */ 128 if (cs == NULL) 129 return; 130 131 if ((s = calloc(1, sizeof(*s))) == NULL) 132 fatal("sensor_add calloc"); 133 134 s->next = getmonotime(); 135 s->weight = cs->weight; 136 s->correction = cs->correction; 137 s->stratum = cs->stratum - 1; 138 if ((s->device = strdup(dxname)) == NULL) 139 fatal("sensor_add strdup"); 140 s->sensordevid = sensordev; 141 142 if (cs->refstr == NULL) 143 memcpy(&s->refid, SENSOR_DEFAULT_REFID, sizeof(s->refid)); 144 else { 145 s->refid = 0; 146 strncpy((char *)&s->refid, cs->refstr, sizeof(s->refid)); 147 } 148 149 TAILQ_INSERT_TAIL(&conf->ntp_sensors, s, entry); 150 151 log_debug("sensor %s added (weight %d, correction %.6f, refstr %.4u, " 152 "stratum %d)", s->device, s->weight, s->correction / 1e6, 153 s->refid, s->stratum); 154 } 155 156 void 157 sensor_remove(struct ntp_sensor *s) 158 { 159 TAILQ_REMOVE(&conf->ntp_sensors, s, entry); 160 free(s->device); 161 free(s); 162 } 163 164 void 165 sensor_query(struct ntp_sensor *s) 166 { 167 char dxname[MAXDEVNAMLEN]; 168 struct sensor sensor; 169 170 if (conf->settime) 171 s->next = getmonotime() + SENSOR_QUERY_INTERVAL_SETTIME; 172 else 173 s->next = getmonotime() + SENSOR_QUERY_INTERVAL; 174 175 /* rcvd is walltime here, monotime in client.c. not used elsewhere */ 176 if (s->update.rcvd < time(NULL) - SENSOR_DATA_MAXAGE) 177 s->update.good = 0; 178 179 if (!sensor_probe(s->sensordevid, dxname, &sensor)) { 180 sensor_remove(s); 181 return; 182 } 183 184 if (sensor.flags & SENSOR_FINVALID || 185 sensor.status != SENSOR_S_OK) 186 return; 187 188 if (strcmp(dxname, s->device)) { 189 sensor_remove(s); 190 return; 191 } 192 193 if (sensor.tv.tv_sec == s->last) /* already seen */ 194 return; 195 196 s->last = sensor.tv.tv_sec; 197 /* 198 * TD = device time 199 * TS = system time 200 * sensor.value = TS - TD in ns 201 * if value is positive, system time is ahead 202 */ 203 s->offsets[s->shift].offset = (sensor.value / -1e9) - getoffset() + 204 (s->correction / 1e6); 205 s->offsets[s->shift].rcvd = sensor.tv.tv_sec; 206 s->offsets[s->shift].good = 1; 207 208 s->offsets[s->shift].status.send_refid = s->refid; 209 /* stratum increased when sent out */ 210 s->offsets[s->shift].status.stratum = s->stratum; 211 s->offsets[s->shift].status.rootdelay = 0; 212 s->offsets[s->shift].status.rootdispersion = 0; 213 s->offsets[s->shift].status.reftime = sensor.tv.tv_sec; 214 s->offsets[s->shift].status.synced = 1; 215 216 log_debug("sensor %s: offset %f", s->device, 217 s->offsets[s->shift].offset); 218 219 if (++s->shift >= SENSOR_OFFSETS) { 220 s->shift = 0; 221 sensor_update(s); 222 } 223 224 } 225 226 void 227 sensor_update(struct ntp_sensor *s) 228 { 229 struct ntp_offset **offsets; 230 int i; 231 232 if ((offsets = calloc(SENSOR_OFFSETS, sizeof(struct ntp_offset *))) == 233 NULL) 234 fatal("calloc sensor_update"); 235 236 for (i = 0; i < SENSOR_OFFSETS; i++) 237 offsets[i] = &s->offsets[i]; 238 239 qsort(offsets, SENSOR_OFFSETS, sizeof(struct ntp_offset *), 240 offset_compare); 241 242 i = SENSOR_OFFSETS / 2; 243 memcpy(&s->update, offsets[i], sizeof(s->update)); 244 if (SENSOR_OFFSETS % 2 == 0) { 245 s->update.offset = 246 (offsets[i - 1]->offset + offsets[i]->offset) / 2; 247 } 248 free(offsets); 249 250 log_debug("sensor update %s: offset %f", s->device, s->update.offset); 251 priv_adjtime(); 252 } 253 254 int 255 sensor_hotplugfd(void) 256 { 257 #ifdef notyet 258 int fd, flags; 259 260 if ((fd = open(_PATH_DEV_HOTPLUG, O_RDONLY, 0)) == -1) { 261 log_warn("open %s", _PATH_DEV_HOTPLUG); 262 return (-1); 263 } 264 265 if ((flags = fcntl(fd, F_GETFL, 0)) == -1) 266 fatal("fcntl F_GETFL"); 267 flags |= O_NONBLOCK; 268 if ((flags = fcntl(fd, F_SETFL, flags)) == -1) 269 fatal("fcntl F_SETFL"); 270 271 return (fd); 272 #else 273 return (-1); 274 #endif 275 } 276 277 void 278 sensor_hotplugevent(int fd) 279 { 280 struct hotplug_event he; 281 ssize_t n; 282 283 do { 284 if ((n = read(fd, &he, sizeof(he))) == -1 && 285 errno != EINTR && errno != EAGAIN) 286 fatal("sensor_hotplugevent read"); 287 288 if (n == sizeof(he)) 289 switch (he.he_type) { 290 case HOTPLUG_DEVAT: 291 if (he.he_devclass == DV_DULL && 292 !strcmp(he.he_devname, "sensordev")) 293 sensor_scan(); 294 break; 295 default: /* ignore */ 296 break; 297 } 298 else if (n > 0) 299 fatal("sensor_hotplugevent: short read"); 300 } while (n > 0 || (n == -1 && errno == EINTR)); 301 } 302