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