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