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