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