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