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