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