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