xref: /openbsd/usr.sbin/ntpd/sensors.c (revision 74ae6390)
1 /*	$OpenBSD: sensors.c,v 1.52 2016/09/03 11:52:06 reyk 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 USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * 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/time.h>
22 #include <sys/sensors.h>
23 #include <sys/sysctl.h>
24 #include <sys/device.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 
37 int	sensor_probe(int, char *, struct sensor *);
38 void	sensor_add(int, char *);
39 void	sensor_remove(struct ntp_sensor *);
40 void	sensor_update(struct ntp_sensor *);
41 
42 void
43 sensor_init(void)
44 {
45 	TAILQ_INIT(&conf->ntp_sensors);
46 }
47 
48 int
49 sensor_scan(void)
50 {
51 	int		i, n, err;
52 	char		d[MAXDEVNAMLEN];
53 	struct sensor	s;
54 
55 	n = 0;
56 	for (i = 0; ; i++)
57 		if ((err = sensor_probe(i, d, &s))) {
58 			if (err == 0)
59 				continue;
60 			if (err == -1)	/* no further sensors */
61 				break;
62 			sensor_add(i, d);
63 			n++;
64 		}
65 
66 	return n;
67 }
68 
69 /*
70  * 1 = time sensor!
71  * 0 = sensor exists... but is not a time sensor
72  * -1: no sensor here, and no further sensors after this
73  */
74 int
75 sensor_probe(int devid, char *dxname, struct sensor *sensor)
76 {
77 	int			mib[5];
78 	size_t			slen, sdlen;
79 	struct sensordev	sensordev;
80 
81 	mib[0] = CTL_HW;
82 	mib[1] = HW_SENSORS;
83 	mib[2] = devid;
84 	mib[3] = SENSOR_TIMEDELTA;
85 	mib[4] = 0;
86 
87 	sdlen = sizeof(sensordev);
88 	if (sysctl(mib, 3, &sensordev, &sdlen, NULL, 0) == -1) {
89 		if (errno == ENXIO)
90 			return (0);
91 		if (errno == ENOENT)
92 			return (-1);
93 		log_warn("sensor_probe sysctl");
94 	}
95 
96 	if (sensordev.maxnumt[SENSOR_TIMEDELTA] == 0)
97 		return (0);
98 
99 	strlcpy(dxname, sensordev.xname, MAXDEVNAMLEN);
100 
101 	slen = sizeof(*sensor);
102 	if (sysctl(mib, 5, sensor, &slen, NULL, 0) == -1) {
103 		if (errno != ENOENT)
104 			log_warn("sensor_probe sysctl");
105 		return (0);
106 	}
107 
108 	return (1);
109 }
110 
111 void
112 sensor_add(int sensordev, char *dxname)
113 {
114 	struct ntp_sensor	*s;
115 	struct ntp_conf_sensor	*cs;
116 
117 	/* check whether it is already there */
118 	TAILQ_FOREACH(s, &conf->ntp_sensors, entry)
119 		if (!strcmp(s->device, dxname))
120 			return;
121 
122 	/* check whether it is requested in the config file */
123 	for (cs = TAILQ_FIRST(&conf->ntp_conf_sensors); cs != NULL &&
124 	    strcmp(cs->device, dxname) && strcmp(cs->device, "*");
125 	    cs = TAILQ_NEXT(cs, entry))
126 		; /* nothing */
127 	if (cs == NULL)
128 		return;
129 
130 	if ((s = calloc(1, sizeof(*s))) == NULL)
131 		fatal("sensor_add calloc");
132 
133 	s->next = getmonotime();
134 	s->weight = cs->weight;
135 	s->correction = cs->correction;
136 	s->stratum = cs->stratum - 1;
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, SENSOR_DEFAULT_REFID, 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 %.4u, "
151 	     "stratum %d)", s->device, s->weight, s->correction / 1e6,
152 	     s->refid, s->stratum);
153 }
154 
155 void
156 sensor_remove(struct ntp_sensor *s)
157 {
158 	TAILQ_REMOVE(&conf->ntp_sensors, s, entry);
159 	free(s->device);
160 	free(s);
161 }
162 
163 void
164 sensor_query(struct ntp_sensor *s)
165 {
166 	char		 dxname[MAXDEVNAMLEN];
167 	struct sensor	 sensor;
168 
169 	if (conf->settime)
170 		s->next = getmonotime() + SENSOR_QUERY_INTERVAL_SETTIME;
171 	else
172 		s->next = getmonotime() + SENSOR_QUERY_INTERVAL;
173 
174 	/* rcvd is walltime here, monotime in client.c. not used elsewhere */
175 	if (s->update.rcvd < time(NULL) - SENSOR_DATA_MAXAGE)
176 		s->update.good = 0;
177 
178 	if (!sensor_probe(s->sensordevid, dxname, &sensor)) {
179 		sensor_remove(s);
180 		return;
181 	}
182 
183 	if (sensor.flags & SENSOR_FINVALID ||
184 	    sensor.status != SENSOR_S_OK)
185 		return;
186 
187 	if (strcmp(dxname, s->device)) {
188 		sensor_remove(s);
189 		return;
190 	}
191 
192 	if (sensor.tv.tv_sec == s->last)	/* already seen */
193 		return;
194 
195 	s->last = sensor.tv.tv_sec;
196 	/*
197 	 * TD = device time
198 	 * TS = system time
199 	 * sensor.value = TS - TD in ns
200 	 * if value is positive, system time is ahead
201 	 */
202 	s->offsets[s->shift].offset = (sensor.value / -1e9) - getoffset() +
203 	    (s->correction / 1e6);
204 	s->offsets[s->shift].rcvd = sensor.tv.tv_sec;
205 	s->offsets[s->shift].good = 1;
206 
207 	s->offsets[s->shift].status.send_refid = s->refid;
208 	/* stratum increased when sent out */
209 	s->offsets[s->shift].status.stratum = s->stratum;
210 	s->offsets[s->shift].status.rootdelay = 0;
211 	s->offsets[s->shift].status.rootdispersion = 0;
212 	s->offsets[s->shift].status.reftime = sensor.tv.tv_sec;
213 	s->offsets[s->shift].status.synced = 1;
214 
215 	log_debug("sensor %s: offset %f", s->device,
216 	    s->offsets[s->shift].offset);
217 
218 	if (++s->shift >= SENSOR_OFFSETS) {
219 		s->shift = 0;
220 		sensor_update(s);
221 	}
222 
223 }
224 
225 void
226 sensor_update(struct ntp_sensor *s)
227 {
228 	struct ntp_offset	**offsets;
229 	int			  i;
230 
231 	if ((offsets = calloc(SENSOR_OFFSETS, sizeof(struct ntp_offset *))) ==
232 	    NULL)
233 		fatal("calloc sensor_update");
234 
235 	for (i = 0; i < SENSOR_OFFSETS; i++)
236 		offsets[i] = &s->offsets[i];
237 
238 	qsort(offsets, SENSOR_OFFSETS, sizeof(struct ntp_offset *),
239 	    offset_compare);
240 
241 	i = SENSOR_OFFSETS / 2;
242 	memcpy(&s->update, offsets[i], sizeof(s->update));
243 	if (SENSOR_OFFSETS % 2 == 0) {
244 		s->update.offset =
245 		    (offsets[i - 1]->offset + offsets[i]->offset) / 2;
246 	}
247 	free(offsets);
248 
249 	log_debug("sensor update %s: offset %f", s->device, s->update.offset);
250 	priv_adjtime();
251 }
252