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