xref: /openbsd-src/usr.sbin/ntpd/sensors.c (revision 4c1e55dc91edd6e69ccc60ce855900fbc12cf34f)
1 /*	$OpenBSD: sensors.c,v 1.45 2010/04/20 20:49:36 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/param.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 	if ((s->device = strdup(dxname)) == NULL)
138 		fatal("sensor_add strdup");
139 	s->sensordevid = sensordev;
140 
141 	if (cs->refstr == NULL)
142 		memcpy(&s->refid, "HARD", sizeof(s->refid));
143 	else {
144 		s->refid = 0;
145 		strncpy((char *)&s->refid, cs->refstr, sizeof(s->refid));
146 	}
147 
148 	TAILQ_INSERT_TAIL(&conf->ntp_sensors, s, entry);
149 
150 	log_debug("sensor %s added (weight %d, correction %.6f, refstr %.4s)",
151 	    s->device, s->weight, s->correction / 1e6, &s->refid);
152 }
153 
154 void
155 sensor_remove(struct ntp_sensor *s)
156 {
157 	TAILQ_REMOVE(&conf->ntp_sensors, s, entry);
158 	free(s->device);
159 	free(s);
160 }
161 
162 void
163 sensor_query(struct ntp_sensor *s)
164 {
165 	char		 dxname[MAXDEVNAMLEN];
166 	struct sensor	 sensor;
167 
168 	if (conf->settime)
169 		s->next = getmonotime() + SENSOR_QUERY_INTERVAL_SETTIME;
170 	else
171 		s->next = getmonotime() + SENSOR_QUERY_INTERVAL;
172 
173 	/* rcvd is walltime here, monotime in client.c. not used elsewhere */
174 	if (s->update.rcvd < time(NULL) - SENSOR_DATA_MAXAGE)
175 		s->update.good = 0;
176 
177 	if (!sensor_probe(s->sensordevid, dxname, &sensor)) {
178 		sensor_remove(s);
179 		return;
180 	}
181 
182 	if (sensor.flags & SENSOR_FINVALID ||
183 	    sensor.status != SENSOR_S_OK)
184 		return;
185 
186 	if (strcmp(dxname, s->device)) {
187 		sensor_remove(s);
188 		return;
189 	}
190 
191 	if (sensor.tv.tv_sec == s->last)	/* already seen */
192 		return;
193 
194 	s->last = sensor.tv.tv_sec;
195 	/*
196 	 * TD = device time
197 	 * TS = system time
198 	 * sensor.value = TS - TD in ns
199 	 * if value is positive, system time is ahead
200 	 */
201 	s->offsets[s->shift].offset = (sensor.value / -1e9) - getoffset() +
202 	    (s->correction / 1e6);
203 	s->offsets[s->shift].rcvd = sensor.tv.tv_sec;
204 	s->offsets[s->shift].good = 1;
205 
206 	s->offsets[s->shift].status.send_refid = s->refid;
207 	s->offsets[s->shift].status.stratum = 0;	/* increased when sent out */
208 	s->offsets[s->shift].status.rootdelay = 0;
209 	s->offsets[s->shift].status.rootdispersion = 0;
210 	s->offsets[s->shift].status.reftime = sensor.tv.tv_sec;
211 	s->offsets[s->shift].status.synced = 1;
212 
213 	log_debug("sensor %s: offset %f", s->device,
214 	    s->offsets[s->shift].offset);
215 
216 	if (++s->shift >= SENSOR_OFFSETS) {
217 		s->shift = 0;
218 		sensor_update(s);
219 	}
220 
221 }
222 
223 void
224 sensor_update(struct ntp_sensor *s)
225 {
226 	struct ntp_offset	**offsets;
227 	int			  i;
228 
229 	if ((offsets = calloc(SENSOR_OFFSETS, sizeof(struct ntp_offset *))) ==
230 	    NULL)
231 		fatal("calloc sensor_update");
232 
233 	for (i = 0; i < SENSOR_OFFSETS; i++)
234 		offsets[i] = &s->offsets[i];
235 
236 	qsort(offsets, SENSOR_OFFSETS, sizeof(struct ntp_offset *),
237 	    offset_compare);
238 
239 	i = SENSOR_OFFSETS / 2;
240 	memcpy(&s->update, offsets[i], sizeof(s->update));
241 	if (SENSOR_OFFSETS % 2 == 0) {
242 		s->update.offset =
243 		    (offsets[i - 1]->offset + offsets[i]->offset) / 2;
244 	}
245 	free(offsets);
246 
247 	log_debug("sensor update %s: offset %f", s->device, s->update.offset);
248 	priv_adjtime();
249 }
250 
251 int
252 sensor_hotplugfd(void)
253 {
254 #ifdef notyet
255 	int	fd, flags;
256 
257 	if ((fd = open(_PATH_DEV_HOTPLUG, O_RDONLY, 0)) == -1) {
258 		log_warn("open %s", _PATH_DEV_HOTPLUG);
259 		return (-1);
260 	}
261 
262 	if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
263 		fatal("fcntl F_GETFL");
264 	flags |= O_NONBLOCK;
265 	if ((flags = fcntl(fd, F_SETFL, flags)) == -1)
266 		fatal("fcntl F_SETFL");
267 
268 	return (fd);
269 #else
270 	return (-1);
271 #endif
272 }
273 
274 void
275 sensor_hotplugevent(int fd)
276 {
277 	struct hotplug_event	he;
278 	ssize_t			n;
279 
280 	do {
281 		if ((n = read(fd, &he, sizeof(he))) == -1 &&
282 		    errno != EINTR && errno != EAGAIN)
283 			fatal("sensor_hotplugevent read");
284 
285 		if (n == sizeof(he))
286 			switch (he.he_type) {
287 			case HOTPLUG_DEVAT:
288 				if (he.he_devclass == DV_DULL &&
289 				    !strcmp(he.he_devname, "sensordev"))
290 					sensor_scan();
291 				break;
292 			default:		/* ignore */
293 				break;
294 			}
295 		else if (n > 0)
296 			fatal("sensor_hotplugevent: short read");
297 	} while (n > 0 || (n == -1 && errno == EINTR));
298 }
299