xref: /openbsd-src/usr.sbin/sensorsd/sensorsd.c (revision 99fd087599a8791921855f21bd7e36130f39aadc)
1 /*	$OpenBSD: sensorsd.c,v 1.66 2019/06/28 13:32:50 deraadt Exp $ */
2 
3 /*
4  * Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
5  * Copyright (c) 2005 Matthew Gream <matthew.gream@pobox.com>
6  * Copyright (c) 2006 Constantine A. Murenin <cnst+openbsd@bugmail.mojo.ru>
7  *
8  * Permission to use, copy, modify, and distribute this software for any
9  * purpose with or without fee is hereby granted, provided that the above
10  * copyright notice and this permission notice appear in all copies.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19  */
20 
21 #include <sys/types.h>
22 #include <sys/sysctl.h>
23 #include <sys/queue.h>
24 #include <sys/time.h>
25 #include <sys/sensors.h>
26 
27 #include <err.h>
28 #include <errno.h>
29 #include <signal.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <syslog.h>
34 #include <time.h>
35 #include <unistd.h>
36 #include <limits.h>
37 
38 #define	RFBUFSIZ	28	/* buffer size for print_sensor */
39 #define	RFBUFCNT	4	/* ring buffers */
40 #define CHECK_PERIOD	20	/* check every n seconds */
41 
42 enum sensorsd_s_status {
43 	SENSORSD_S_UNSPEC,	/* status is unspecified */
44 	SENSORSD_S_INVALID,	/* status is invalid, per SENSOR_FINVALID */
45 	SENSORSD_S_WITHIN,	/* status is within limits */
46 	SENSORSD_S_ABOVE,	/* status is above the higher limit */
47 	SENSORSD_S_BELOW	/* status is below the lower limit */
48 };
49 
50 struct limits_t {
51 	TAILQ_ENTRY(limits_t)	entries;
52 	enum sensor_type	type;		/* sensor type */
53 	int			numt;		/* sensor number */
54 	int64_t			last_val;
55 	int64_t			lower;		/* lower limit */
56 	int64_t			upper;		/* upper limit */
57 	char			*command;	/* failure command */
58 	time_t			astatus_changed;
59 	time_t			ustatus_changed;
60 	enum sensor_status	astatus;	/* last automatic status */
61 	enum sensor_status	astatus2;
62 	enum sensorsd_s_status	ustatus;	/* last user-limit status */
63 	enum sensorsd_s_status	ustatus2;
64 	int			acount;		/* stat change counter */
65 	int			ucount;		/* stat change counter */
66 	u_int8_t		flags;		/* sensorsd limit flags */
67 #define SENSORSD_L_USERLIMIT		0x0001	/* user specified limit */
68 #define SENSORSD_L_ISTATUS		0x0002	/* ignore automatic status */
69 };
70 
71 struct sdlim_t {
72 	TAILQ_ENTRY(sdlim_t)	entries;
73 	char			dxname[16];	/* device unix name */
74 	int			dev;		/* device number */
75 	int			sensor_cnt;
76 	TAILQ_HEAD(, limits_t)	limits;
77 };
78 
79 void		 usage(void);
80 void		 create(void);
81 struct sdlim_t	*create_sdlim(struct sensordev *);
82 void		 destroy_sdlim(struct sdlim_t *);
83 void		 check(time_t);
84 void		 check_sdlim(struct sdlim_t *, time_t);
85 void		 execute(char *);
86 void		 report(time_t);
87 void		 report_sdlim(struct sdlim_t *, time_t);
88 static char	*print_sensor(enum sensor_type, int64_t);
89 void		 parse_config(char *);
90 void		 parse_config_sdlim(struct sdlim_t *, char *);
91 int64_t		 get_val(char *, int, enum sensor_type);
92 void		 reparse_cfg(int);
93 
94 TAILQ_HEAD(sdlimhead_t, sdlim_t);
95 struct sdlimhead_t sdlims = TAILQ_HEAD_INITIALIZER(sdlims);
96 
97 char			 *configfile;
98 volatile sig_atomic_t	  reload = 0;
99 int			  debug = 0;
100 
101 void
102 usage(void)
103 {
104 	extern char *__progname;
105 	fprintf(stderr, "usage: %s [-d] [-c check] [-f file]\n",
106 	    __progname);
107 	exit(1);
108 }
109 
110 int
111 main(int argc, char *argv[])
112 {
113 	time_t		 last_report = 0, this_check;
114 	int		 ch, check_period = CHECK_PERIOD;
115 	const char	*errstr;
116 
117 	while ((ch = getopt(argc, argv, "c:df:")) != -1) {
118 		switch (ch) {
119 		case 'c':
120 			check_period = strtonum(optarg, 1, 600, &errstr);
121 			if (errstr)
122 				errx(1, "check %s", errstr);
123 			break;
124 		case 'd':
125 			debug = 1;
126 			break;
127 		case 'f':
128 			configfile = optarg;
129 			if (access(configfile, R_OK) != 0)
130 				err(1, "access configuration file %s",
131 				    configfile);
132 			break;
133 		default:
134 			usage();
135 		}
136 	}
137 
138 	argc -= optind;
139 	argv += optind;
140 	if (argc > 0)
141 		usage();
142 
143 	if (configfile == NULL)
144 		if (asprintf(&configfile, "/etc/sensorsd.conf") == -1)
145 			err(1, "out of memory");
146 
147 	if (unveil(configfile, "r") == -1)
148 		err(1, "unveil");
149 	if (unveil("/", "x") == -1)
150 		err(1, "unveil");
151 
152 	if (pledge("stdio rpath proc exec", NULL) == -1)
153 		err(1, "pledge");
154 
155 	openlog("sensorsd", LOG_PID | LOG_NDELAY, LOG_DAEMON);
156 
157 	create();
158 
159 	parse_config(configfile);
160 
161 	if (debug == 0 && daemon(0, 0) == -1)
162 		err(1, "unable to fork");
163 
164 	signal(SIGHUP, reparse_cfg);
165 	signal(SIGCHLD, SIG_IGN);
166 
167 	for (;;) {
168 		if (reload) {
169 			parse_config(configfile);
170 			syslog(LOG_INFO, "configuration reloaded");
171 			reload = 0;
172 		}
173 		this_check = time(NULL);
174 		if (!(last_report < this_check))
175 			this_check = last_report + 1;
176 		check(this_check);
177 		report(last_report);
178 		last_report = this_check;
179 		sleep(check_period);
180 	}
181 }
182 
183 void
184 create(void)
185 {
186 	struct sensordev sensordev;
187 	struct sdlim_t	*sdlim;
188 	size_t		 sdlen = sizeof(sensordev);
189 	int		 mib[3], dev, sensor_cnt = 0;
190 
191 	mib[0] = CTL_HW;
192 	mib[1] = HW_SENSORS;
193 
194 	for (dev = 0; ; dev++) {
195 		mib[2] = dev;
196 		if (sysctl(mib, 3, &sensordev, &sdlen, NULL, 0) == -1) {
197 			if (errno == ENXIO)
198 				continue;
199 			if (errno == ENOENT)
200 				break;
201 			warn("sysctl");
202 		}
203 		sdlim = create_sdlim(&sensordev);
204 		TAILQ_INSERT_TAIL(&sdlims, sdlim, entries);
205 		sensor_cnt += sdlim->sensor_cnt;
206 	}
207 
208 	syslog(LOG_INFO, "startup, system has %d sensors", sensor_cnt);
209 }
210 
211 struct sdlim_t *
212 create_sdlim(struct sensordev *snsrdev)
213 {
214 	struct sensor	 sensor;
215 	struct sdlim_t	*sdlim;
216 	struct limits_t	*limit;
217 	size_t		 slen = sizeof(sensor);
218 	int		 mib[5], numt;
219 	enum sensor_type type;
220 
221 	if ((sdlim = calloc(1, sizeof(struct sdlim_t))) == NULL)
222 		err(1, "calloc");
223 
224 	strlcpy(sdlim->dxname, snsrdev->xname, sizeof(sdlim->dxname));
225 
226 	mib[0] = CTL_HW;
227 	mib[1] = HW_SENSORS;
228 	mib[2] = sdlim->dev = snsrdev->num;
229 
230 	TAILQ_INIT(&sdlim->limits);
231 
232 	for (type = 0; type < SENSOR_MAX_TYPES; type++) {
233 		mib[3] = type;
234 		for (numt = 0; numt < snsrdev->maxnumt[type]; numt++) {
235 			mib[4] = numt;
236 			if (sysctl(mib, 5, &sensor, &slen, NULL, 0) == -1) {
237 				if (errno != ENOENT)
238 					warn("sysctl");
239 				continue;
240 			}
241 			if ((limit = calloc(1, sizeof(struct limits_t))) ==
242 			    NULL)
243 				err(1, "calloc");
244 			limit->type = type;
245 			limit->numt = numt;
246 			TAILQ_INSERT_TAIL(&sdlim->limits, limit, entries);
247 			sdlim->sensor_cnt++;
248 		}
249 	}
250 
251 	return (sdlim);
252 }
253 
254 void
255 destroy_sdlim(struct sdlim_t *sdlim)
256 {
257 	struct limits_t		*limit;
258 
259 	while ((limit = TAILQ_FIRST(&sdlim->limits)) != NULL) {
260 		TAILQ_REMOVE(&sdlim->limits, limit, entries);
261 		free(limit->command);
262 		free(limit);
263 	}
264 	free(sdlim);
265 }
266 
267 void
268 check(time_t this_check)
269 {
270 	struct sensordev	 sensordev;
271 	struct sdlim_t		*sdlim, *next;
272 	int			 mib[3];
273 	int			 h, t, i;
274 	size_t			 sdlen = sizeof(sensordev);
275 
276 	if (TAILQ_EMPTY(&sdlims)) {
277 		h = 0;
278 		t = -1;
279 	} else {
280 		h = TAILQ_FIRST(&sdlims)->dev;
281 		t = TAILQ_LAST(&sdlims, sdlimhead_t)->dev;
282 	}
283 	sdlim = TAILQ_FIRST(&sdlims);
284 
285 	mib[0] = CTL_HW;
286 	mib[1] = HW_SENSORS;
287 	/* look ahead for 4 more sensordevs */
288 	for (i = h; i <= t + 4; i++) {
289 		if (sdlim != NULL && i > sdlim->dev)
290 			sdlim = TAILQ_NEXT(sdlim, entries);
291 		if (sdlim == NULL && i <= t)
292 			syslog(LOG_ALERT, "inconsistent sdlim logic");
293 		mib[2] = i;
294 		if (sysctl(mib, 3, &sensordev, &sdlen, NULL, 0) == -1) {
295 			if (errno != ENOENT)
296 				warn("sysctl");
297 			if (sdlim != NULL && i == sdlim->dev) {
298 				next = TAILQ_NEXT(sdlim, entries);
299 				TAILQ_REMOVE(&sdlims, sdlim, entries);
300 				syslog(LOG_INFO, "%s has disappeared",
301 				    sdlim->dxname);
302 				destroy_sdlim(sdlim);
303 				sdlim = next;
304 			}
305 			continue;
306 		}
307 		if (sdlim != NULL && i == sdlim->dev) {
308 			if (strcmp(sdlim->dxname, sensordev.xname) == 0) {
309 				check_sdlim(sdlim, this_check);
310 				continue;
311 			} else {
312 				next = TAILQ_NEXT(sdlim, entries);
313 				TAILQ_REMOVE(&sdlims, sdlim, entries);
314 				syslog(LOG_INFO, "%s has been replaced",
315 				    sdlim->dxname);
316 				destroy_sdlim(sdlim);
317 				sdlim = next;
318 			}
319 		}
320 		next = create_sdlim(&sensordev);
321 		/* inserting next before sdlim */
322 		if (sdlim != NULL)
323 			TAILQ_INSERT_BEFORE(sdlim, next, entries);
324 		else
325 			TAILQ_INSERT_TAIL(&sdlims, next, entries);
326 		syslog(LOG_INFO, "%s has appeared", next->dxname);
327 		sdlim = next;
328 		parse_config_sdlim(sdlim, configfile);
329 		check_sdlim(sdlim, this_check);
330 	}
331 
332 	if (TAILQ_EMPTY(&sdlims))
333 		return;
334 	/* Ensure that our queue is consistent. */
335 	for (sdlim = TAILQ_FIRST(&sdlims);
336 	    (next = TAILQ_NEXT(sdlim, entries)) != NULL;
337 	    sdlim = next)
338 		if (sdlim->dev > next->dev)
339 			syslog(LOG_ALERT, "inconsistent sdlims queue");
340 }
341 
342 void
343 check_sdlim(struct sdlim_t *sdlim, time_t this_check)
344 {
345 	struct sensor		 sensor;
346 	struct limits_t		*limit;
347 	size_t		 	 len;
348 	int		 	 mib[5];
349 
350 	mib[0] = CTL_HW;
351 	mib[1] = HW_SENSORS;
352 	mib[2] = sdlim->dev;
353 	len = sizeof(sensor);
354 
355 	TAILQ_FOREACH(limit, &sdlim->limits, entries) {
356 		if ((limit->flags & SENSORSD_L_ISTATUS) &&
357 		    !(limit->flags & SENSORSD_L_USERLIMIT))
358 			continue;
359 
360 		mib[3] = limit->type;
361 		mib[4] = limit->numt;
362 		if (sysctl(mib, 5, &sensor, &len, NULL, 0) == -1)
363 			err(1, "sysctl");
364 
365 		if (!(limit->flags & SENSORSD_L_ISTATUS)) {
366 			enum sensor_status	newastatus = sensor.status;
367 
368 			if (limit->astatus != newastatus) {
369 				if (limit->astatus2 != newastatus) {
370 					limit->astatus2 = newastatus;
371 					limit->acount = 0;
372 				} else if (++limit->acount >= 3) {
373 					limit->last_val = sensor.value;
374 					limit->astatus2 =
375 					    limit->astatus = newastatus;
376 					limit->astatus_changed = this_check;
377 				}
378 			}
379 		}
380 
381 		if (limit->flags & SENSORSD_L_USERLIMIT) {
382 			enum sensorsd_s_status 	 newustatus;
383 
384 			if (sensor.flags & SENSOR_FINVALID)
385 				newustatus = SENSORSD_S_INVALID;
386 			else if (sensor.value > limit->upper)
387 				newustatus = SENSORSD_S_ABOVE;
388 			else if (sensor.value < limit->lower)
389 				newustatus = SENSORSD_S_BELOW;
390 			else
391 				newustatus = SENSORSD_S_WITHIN;
392 
393 			if (limit->ustatus != newustatus) {
394 				if (limit->ustatus2 != newustatus) {
395 					limit->ustatus2 = newustatus;
396 					limit->ucount = 0;
397 				} else if (++limit->ucount >= 3) {
398 					limit->last_val = sensor.value;
399 					limit->ustatus2 =
400 					    limit->ustatus = newustatus;
401 					limit->ustatus_changed = this_check;
402 				}
403 			}
404 		}
405 	}
406 }
407 
408 void
409 execute(char *command)
410 {
411 	char *argp[] = {"sh", "-c", command, NULL};
412 
413 	switch (fork()) {
414 	case -1:
415 		syslog(LOG_CRIT, "execute: fork() failed");
416 		break;
417 	case 0:
418 		execv("/bin/sh", argp);
419 		_exit(1);
420 		/* NOTREACHED */
421 	default:
422 		break;
423 	}
424 }
425 
426 void
427 report(time_t last_report)
428 {
429 	struct sdlim_t	*sdlim;
430 
431 	TAILQ_FOREACH(sdlim, &sdlims, entries)
432 		report_sdlim(sdlim, last_report);
433 }
434 
435 void
436 report_sdlim(struct sdlim_t *sdlim, time_t last_report)
437 {
438 	struct limits_t	*limit;
439 
440 	TAILQ_FOREACH(limit, &sdlim->limits, entries) {
441 		if ((limit->astatus_changed <= last_report) &&
442 		    (limit->ustatus_changed <= last_report))
443 			continue;
444 
445 		if (limit->astatus_changed > last_report) {
446 			const char *as = NULL;
447 
448 			switch (limit->astatus) {
449 			case SENSOR_S_UNSPEC:
450 				as = "";
451 				break;
452 			case SENSOR_S_OK:
453 				as = ", OK";
454 				break;
455 			case SENSOR_S_WARN:
456 				as = ", WARN";
457 				break;
458 			case SENSOR_S_CRIT:
459 				as = ", CRITICAL";
460 				break;
461 			case SENSOR_S_UNKNOWN:
462 				as = ", UNKNOWN";
463 				break;
464 			}
465 			syslog(limit->astatus == SENSOR_S_OK ? LOG_INFO :
466 			    LOG_ALERT, "%s.%s%d: %s%s",
467 			    sdlim->dxname, sensor_type_s[limit->type],
468 			    limit->numt,
469 			    print_sensor(limit->type, limit->last_val), as);
470 		}
471 
472 		if (limit->ustatus_changed > last_report) {
473 			char us[BUFSIZ];
474 
475 			switch (limit->ustatus) {
476 			case SENSORSD_S_UNSPEC:
477 				snprintf(us, sizeof(us),
478 				    "ustatus uninitialised");
479 				break;
480 			case SENSORSD_S_INVALID:
481 				snprintf(us, sizeof(us), "marked invalid");
482 				break;
483 			case SENSORSD_S_WITHIN:
484 				snprintf(us, sizeof(us),
485 				    "within limits: %s",
486 				    print_sensor(limit->type, limit->last_val));
487 				break;
488 			case SENSORSD_S_ABOVE:
489 				snprintf(us, sizeof(us),
490 				    "exceeds limits: %s is above %s",
491 				    print_sensor(limit->type, limit->last_val),
492 				    print_sensor(limit->type, limit->upper));
493 				break;
494 			case SENSORSD_S_BELOW:
495 				snprintf(us, sizeof(us),
496 				    "exceeds limits: %s is below %s",
497 				    print_sensor(limit->type, limit->last_val),
498 				    print_sensor(limit->type, limit->lower));
499 				break;
500 			}
501 			syslog(limit->ustatus == SENSORSD_S_WITHIN ? LOG_INFO :
502 			    LOG_ALERT, "%s.%s%d: %s",
503 			    sdlim->dxname, sensor_type_s[limit->type],
504 			    limit->numt, us);
505 		}
506 
507 		if (limit->command) {
508 			int i = 0, n = 0, r;
509 			char *cmd = limit->command;
510 			char buf[BUFSIZ];
511 			int len = sizeof(buf);
512 
513 			buf[0] = '\0';
514 			for (i = n = 0; n < len; ++i) {
515 				if (cmd[i] == '\0') {
516 					buf[n++] = '\0';
517 					break;
518 				}
519 				if (cmd[i] != '%') {
520 					buf[n++] = limit->command[i];
521 					continue;
522 				}
523 				i++;
524 				if (cmd[i] == '\0') {
525 					buf[n++] = '\0';
526 					break;
527 				}
528 
529 				switch (cmd[i]) {
530 				case 'x':
531 					r = snprintf(&buf[n], len - n, "%s",
532 					    sdlim->dxname);
533 					break;
534 				case 't':
535 					r = snprintf(&buf[n], len - n, "%s",
536 					    sensor_type_s[limit->type]);
537 					break;
538 				case 'n':
539 					r = snprintf(&buf[n], len - n, "%d",
540 					    limit->numt);
541 					break;
542 				case 'l':
543 				{
544 					char *s = "";
545 					switch (limit->ustatus) {
546 					case SENSORSD_S_UNSPEC:
547 						s = "uninitialised";
548 						break;
549 					case SENSORSD_S_INVALID:
550 						s = "invalid";
551 						break;
552 					case SENSORSD_S_WITHIN:
553 						s = "within";
554 						break;
555 					case SENSORSD_S_ABOVE:
556 						s = "above";
557 						break;
558 					case SENSORSD_S_BELOW:
559 						s = "below";
560 						break;
561 					}
562 					r = snprintf(&buf[n], len - n, "%s",
563 					    s);
564 					break;
565 				}
566 				case 's':
567 				{
568 					char *s;
569 					switch (limit->astatus) {
570 					case SENSOR_S_UNSPEC:
571 						s = "UNSPEC";
572 						break;
573 					case SENSOR_S_OK:
574 						s = "OK";
575 						break;
576 					case SENSOR_S_WARN:
577 						s = "WARNING";
578 						break;
579 					case SENSOR_S_CRIT:
580 						s = "CRITICAL";
581 						break;
582 					default:
583 						s = "UNKNOWN";
584 					}
585 					r = snprintf(&buf[n], len - n, "%s",
586 					    s);
587 					break;
588 				}
589 				case '2':
590 					r = snprintf(&buf[n], len - n, "%s",
591 					    print_sensor(limit->type,
592 					    limit->last_val));
593 					break;
594 				case '3':
595 					r = snprintf(&buf[n], len - n, "%s",
596 					    print_sensor(limit->type,
597 					    limit->lower));
598 					break;
599 				case '4':
600 					r = snprintf(&buf[n], len - n, "%s",
601 					    print_sensor(limit->type,
602 					    limit->upper));
603 					break;
604 				default:
605 					r = snprintf(&buf[n], len - n, "%%%c",
606 					    cmd[i]);
607 					break;
608 				}
609 				if (r == -1 || (r >= len - n)) {
610 					syslog(LOG_CRIT, "could not parse "
611 					    "command");
612 					return;
613 				}
614 				if (r > 0)
615 					n += r;
616 			}
617 			if (buf[0])
618 				execute(buf);
619 		}
620 	}
621 }
622 
623 const char *drvstat[] = {
624 	NULL, "empty", "ready", "powerup", "online", "idle", "active",
625 	"rebuild", "powerdown", "fail", "pfail"
626 };
627 
628 static char *
629 print_sensor(enum sensor_type type, int64_t value)
630 {
631 	static char	 rfbuf[RFBUFCNT][RFBUFSIZ];	/* ring buffer */
632 	static int	 idx;
633 	char		*fbuf;
634 
635 	fbuf = rfbuf[idx++];
636 	if (idx == RFBUFCNT)
637 		idx = 0;
638 
639 	switch (type) {
640 	case SENSOR_TEMP:
641 		snprintf(fbuf, RFBUFSIZ, "%.2f degC",
642 		    (value - 273150000) / 1000000.0);
643 		break;
644 	case SENSOR_FANRPM:
645 		snprintf(fbuf, RFBUFSIZ, "%lld RPM", value);
646 		break;
647 	case SENSOR_VOLTS_DC:
648 		snprintf(fbuf, RFBUFSIZ, "%.2f V DC", value / 1000000.0);
649 		break;
650 	case SENSOR_VOLTS_AC:
651 		snprintf(fbuf, RFBUFSIZ, "%.2f V AC", value / 1000000.0);
652 		break;
653 	case SENSOR_WATTS:
654 		snprintf(fbuf, RFBUFSIZ, "%.2f W", value / 1000000.0);
655 		break;
656 	case SENSOR_AMPS:
657 		snprintf(fbuf, RFBUFSIZ, "%.2f A", value / 1000000.0);
658 		break;
659 	case SENSOR_WATTHOUR:
660 		snprintf(fbuf, RFBUFSIZ, "%.2f Wh", value / 1000000.0);
661 		break;
662 	case SENSOR_AMPHOUR:
663 		snprintf(fbuf, RFBUFSIZ, "%.2f Ah", value / 1000000.0);
664 		break;
665 	case SENSOR_INDICATOR:
666 		snprintf(fbuf, RFBUFSIZ, "%s", value? "On" : "Off");
667 		break;
668 	case SENSOR_INTEGER:
669 		snprintf(fbuf, RFBUFSIZ, "%lld", value);
670 		break;
671 	case SENSOR_PERCENT:
672 		snprintf(fbuf, RFBUFSIZ, "%.2f%%", value / 1000.0);
673 		break;
674 	case SENSOR_LUX:
675 		snprintf(fbuf, RFBUFSIZ, "%.2f lx", value / 1000000.0);
676 		break;
677 	case SENSOR_DRIVE:
678 		if (0 < value && value < sizeof(drvstat)/sizeof(drvstat[0]))
679 			snprintf(fbuf, RFBUFSIZ, "%s", drvstat[value]);
680 		else
681 			snprintf(fbuf, RFBUFSIZ, "%lld ???", value);
682 		break;
683 	case SENSOR_TIMEDELTA:
684 		snprintf(fbuf, RFBUFSIZ, "%.6f secs", value / 1000000000.0);
685 		break;
686 	case SENSOR_HUMIDITY:
687 		snprintf(fbuf, RFBUFSIZ, "%.2f%%", value / 1000.0);
688 		break;
689 	case SENSOR_FREQ:
690 		snprintf(fbuf, RFBUFSIZ, "%.2f Hz", value / 1000000.0);
691 		break;
692 	case SENSOR_ANGLE:
693 		snprintf(fbuf, RFBUFSIZ, "%lld", value);
694 		break;
695 	case SENSOR_DISTANCE:
696 		snprintf(fbuf, RFBUFSIZ, "%.3f m", value / 1000000.0);
697 		break;
698 	case SENSOR_PRESSURE:
699 		snprintf(fbuf, RFBUFSIZ, "%.2f Pa", value / 1000.0);
700 		break;
701 	case SENSOR_ACCEL:
702 		snprintf(fbuf, RFBUFSIZ, "%2.4f m/s^2", value / 1000000.0);
703 		break;
704 	case SENSOR_VELOCITY:
705 		snprintf(fbuf, RFBUFSIZ, "%4.3f m/s", value / 1000000.0);
706 		break;
707 	default:
708 		snprintf(fbuf, RFBUFSIZ, "%lld ???", value);
709 	}
710 
711 	return (fbuf);
712 }
713 
714 void
715 parse_config(char *cf)
716 {
717 	struct sdlim_t	 *sdlim;
718 
719 	TAILQ_FOREACH(sdlim, &sdlims, entries)
720 		parse_config_sdlim(sdlim, cf);
721 }
722 
723 void
724 parse_config_sdlim(struct sdlim_t *sdlim, char *cf)
725 {
726 	struct limits_t	 *p;
727 	char		 *buf = NULL, *ebuf = NULL;
728 	char		  node[48];
729 	char		 *cfa[2];
730 
731 	cfa[0] = cf;
732 	cfa[1] = NULL;
733 
734 	TAILQ_FOREACH(p, &sdlim->limits, entries) {
735 		snprintf(node, sizeof(node), "hw.sensors.%s.%s%d",
736 		    sdlim->dxname, sensor_type_s[p->type], p->numt);
737 		p->flags = 0;
738 		if (cgetent(&buf, cfa, node) != 0)
739 			if (cgetent(&buf, cfa, sensor_type_s[p->type]) != 0)
740 				continue;
741 		if (cgetcap(buf, "istatus", ':'))
742 			p->flags |= SENSORSD_L_ISTATUS;
743 		if (cgetstr(buf, "low", &ebuf) < 0)
744 			ebuf = NULL;
745 		p->lower = get_val(ebuf, 0, p->type);
746 		if (cgetstr(buf, "high", &ebuf) < 0)
747 			ebuf = NULL;
748 		p->upper = get_val(ebuf, 1, p->type);
749 		if (cgetstr(buf, "command", &ebuf) < 0)
750 			ebuf = NULL;
751 		if (ebuf != NULL) {
752 			p->command = ebuf;
753 			ebuf = NULL;
754 		}
755 		free(buf);
756 		buf = NULL;
757 		if (p->lower != LLONG_MIN || p->upper != LLONG_MAX)
758 			p->flags |= SENSORSD_L_USERLIMIT;
759 	}
760 }
761 
762 int64_t
763 get_val(char *buf, int upper, enum sensor_type type)
764 {
765 	double	 val;
766 	int64_t	 rval = 0;
767 	char	*p;
768 
769 	if (buf == NULL) {
770 		if (upper)
771 			return (LLONG_MAX);
772 		else
773 			return (LLONG_MIN);
774 	}
775 
776 	val = strtod(buf, &p);
777 	if (buf == p)
778 		err(1, "incorrect value: %s", buf);
779 
780 	switch (type) {
781 	case SENSOR_TEMP:
782 		switch (*p) {
783 		case 'C':
784 			printf("C");
785 			rval = val * 1000 * 1000 + 273150000;
786 			break;
787 		case 'F':
788 			printf("F");
789 			rval = (val * 1000 * 1000 + 459670000) / 9 * 5;
790 			break;
791 		default:
792 			errx(1, "unknown unit %s for temp sensor", p);
793 		}
794 		break;
795 	case SENSOR_FANRPM:
796 		rval = val;
797 		break;
798 	case SENSOR_VOLTS_DC:
799 	case SENSOR_VOLTS_AC:
800 		if (*p != 'V')
801 			errx(1, "unknown unit %s for voltage sensor", p);
802 		rval = val * 1000 * 1000;
803 		break;
804 	case SENSOR_PERCENT:
805 		rval = val * 1000.0;
806 		break;
807 	case SENSOR_INDICATOR:
808 	case SENSOR_INTEGER:
809 	case SENSOR_DRIVE:
810 	case SENSOR_ANGLE:
811 		rval = val;
812 		break;
813 	case SENSOR_WATTS:
814 	case SENSOR_AMPS:
815 	case SENSOR_WATTHOUR:
816 	case SENSOR_AMPHOUR:
817 	case SENSOR_LUX:
818 	case SENSOR_FREQ:
819 	case SENSOR_ACCEL:
820 	case SENSOR_DISTANCE:
821 	case SENSOR_VELOCITY:
822 		rval = val * 1000 * 1000;
823 		break;
824 	case SENSOR_TIMEDELTA:
825 		rval = val * 1000 * 1000 * 1000;
826 		break;
827 	case SENSOR_HUMIDITY:
828 	case SENSOR_PRESSURE:
829 		rval = val * 1000.0;
830 		break;
831 	default:
832 		errx(1, "unsupported sensor type");
833 		/* not reached */
834 	}
835 	free(buf);
836 	return (rval);
837 }
838 
839 /* ARGSUSED */
840 void
841 reparse_cfg(int signo)
842 {
843 	reload = 1;
844 }
845