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