xref: /openbsd-src/usr.sbin/sensorsd/sensorsd.c (revision b32bcfa8f8a7c72bd2727c674608a7facc50b67d)
1 /*	$OpenBSD: sensorsd.c,v 1.45 2008/06/11 21:21:50 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 <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 < 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 	/* Ensure that our queue is consistent. */
313 	for (sdlim = TAILQ_FIRST(&sdlims);
314 	    (next = TAILQ_NEXT(sdlim, entries)) != NULL;
315 	    sdlim = next)
316 		if (sdlim->dev > next->dev)
317 			syslog(LOG_ALERT, "inconsistent sdlims queue");
318 }
319 
320 void
321 check_sdlim(struct sdlim_t *sdlim, time_t this_check)
322 {
323 	struct sensor		 sensor;
324 	struct limits_t		*limit;
325 	size_t		 	 len;
326 	int		 	 mib[5];
327 
328 	mib[0] = CTL_HW;
329 	mib[1] = HW_SENSORS;
330 	mib[2] = sdlim->dev;
331 	len = sizeof(sensor);
332 
333 	TAILQ_FOREACH(limit, &sdlim->limits, entries) {
334 		if ((limit->flags & SENSORSD_L_ISTATUS) &&
335 		    !(limit->flags & SENSORSD_L_USERLIMIT))
336 			continue;
337 
338 		mib[3] = limit->type;
339 		mib[4] = limit->numt;
340 		if (sysctl(mib, 5, &sensor, &len, NULL, 0) == -1)
341 			err(1, "sysctl");
342 
343 		if (!(limit->flags & SENSORSD_L_ISTATUS)) {
344 			enum sensor_status	newastatus = sensor.status;
345 
346 			if (limit->astatus != newastatus) {
347 				if (limit->astatus2 != newastatus) {
348 					limit->astatus2 = newastatus;
349 					limit->acount = 0;
350 				} else if (++limit->acount >= 3) {
351 					limit->last_val = sensor.value;
352 					limit->astatus2 =
353 					    limit->astatus = newastatus;
354 					limit->astatus_changed = this_check;
355 				}
356 			}
357 		}
358 
359 		if (limit->flags & SENSORSD_L_USERLIMIT) {
360 			enum sensorsd_s_status 	 newustatus;
361 
362 			if (sensor.flags & SENSOR_FINVALID)
363 				newustatus = SENSORSD_S_INVALID;
364 			else if (sensor.value > limit->upper)
365 				newustatus = SENSORSD_S_ABOVE;
366 			else if (sensor.value < limit->lower)
367 				newustatus = SENSORSD_S_BELOW;
368 			else
369 				newustatus = SENSORSD_S_WITHIN;
370 
371 			if (limit->ustatus != newustatus) {
372 				if (limit->ustatus2 != newustatus) {
373 					limit->ustatus2 = newustatus;
374 					limit->ucount = 0;
375 				} else if (++limit->ucount >= 3) {
376 					limit->last_val = sensor.value;
377 					limit->ustatus2 =
378 					    limit->ustatus = newustatus;
379 					limit->ustatus_changed = this_check;
380 				}
381 			}
382 		}
383 	}
384 }
385 
386 void
387 execute(char *command)
388 {
389 	char *argp[] = {"sh", "-c", command, NULL};
390 
391 	switch (fork()) {
392 	case -1:
393 		syslog(LOG_CRIT, "execute: fork() failed");
394 		break;
395 	case 0:
396 		execv("/bin/sh", argp);
397 		_exit(1);
398 		/* NOTREACHED */
399 	default:
400 		break;
401 	}
402 }
403 
404 void
405 report(time_t last_report)
406 {
407 	struct sdlim_t	*sdlim;
408 
409 	TAILQ_FOREACH(sdlim, &sdlims, entries)
410 		report_sdlim(sdlim, last_report);
411 }
412 
413 void
414 report_sdlim(struct sdlim_t *sdlim, time_t last_report)
415 {
416 	struct limits_t	*limit;
417 
418 	TAILQ_FOREACH(limit, &sdlim->limits, entries) {
419 		if ((limit->astatus_changed <= last_report) &&
420 		    (limit->ustatus_changed <= last_report))
421 			continue;
422 
423 		if (limit->astatus_changed > last_report) {
424 			const char *as = NULL;
425 
426 			switch (limit->astatus) {
427 			case SENSOR_S_UNSPEC:
428 				as = "";
429 				break;
430 			case SENSOR_S_OK:
431 				as = ", OK";
432 				break;
433 			case SENSOR_S_WARN:
434 				as = ", WARN";
435 				break;
436 			case SENSOR_S_CRIT:
437 				as = ", CRITICAL";
438 				break;
439 			case SENSOR_S_UNKNOWN:
440 				as = ", UNKNOWN";
441 				break;
442 			}
443 			syslog(limit->astatus == SENSOR_S_OK ? LOG_INFO :
444 			    LOG_ALERT, "%s.%s%d: %s%s",
445 			    sdlim->dxname, sensor_type_s[limit->type],
446 			    limit->numt,
447 			    print_sensor(limit->type, limit->last_val), as);
448 		}
449 
450 		if (limit->ustatus_changed > last_report) {
451 			char us[BUFSIZ];
452 
453 			switch (limit->ustatus) {
454 			case SENSORSD_S_UNSPEC:
455 				snprintf(us, sizeof(us),
456 				    "ustatus uninitialised");
457 				break;
458 			case SENSORSD_S_INVALID:
459 				snprintf(us, sizeof(us), "marked invalid");
460 				break;
461 			case SENSORSD_S_WITHIN:
462 				snprintf(us, sizeof(us), "within limits: %s",
463 				    print_sensor(limit->type, limit->last_val));
464 				break;
465 			case SENSORSD_S_ABOVE:
466 				snprintf(us, sizeof(us), "exceeds limits: %s is above %s",
467 				    print_sensor(limit->type, limit->last_val),
468 				    print_sensor(limit->type, limit->upper));
469 				break;
470 			case SENSORSD_S_BELOW:
471 				snprintf(us, sizeof(us), "exceeds limits: %s is below %s",
472 				    print_sensor(limit->type, limit->last_val),
473 				    print_sensor(limit->type, limit->lower));
474 				break;
475 			}
476 			syslog(limit->ustatus == SENSORSD_S_WITHIN ? LOG_INFO :
477 			    LOG_ALERT, "%s.%s%d: %s",
478 			    sdlim->dxname, sensor_type_s[limit->type],
479 			    limit->numt, us);
480 		}
481 
482 		if (limit->command) {
483 			int i = 0, n = 0, r;
484 			char *cmd = limit->command;
485 			char buf[BUFSIZ];
486 			int len = sizeof(buf);
487 
488 			buf[0] = '\0';
489 			for (i = n = 0; n < len; ++i) {
490 				if (cmd[i] == '\0') {
491 					buf[n++] = '\0';
492 					break;
493 				}
494 				if (cmd[i] != '%') {
495 					buf[n++] = limit->command[i];
496 					continue;
497 				}
498 				i++;
499 				if (cmd[i] == '\0') {
500 					buf[n++] = '\0';
501 					break;
502 				}
503 
504 				switch (cmd[i]) {
505 				case 'x':
506 					r = snprintf(&buf[n], len - n, "%s",
507 					    sdlim->dxname);
508 					break;
509 				case 't':
510 					r = snprintf(&buf[n], len - n, "%s",
511 					    sensor_type_s[limit->type]);
512 					break;
513 				case 'n':
514 					r = snprintf(&buf[n], len - n, "%d",
515 					    limit->numt);
516 					break;
517 				case 'l':
518 				{
519 					char *s = "";
520 					switch(limit->ustatus){
521 					case SENSORSD_S_UNSPEC:
522 						s = "uninitialised";
523 						break;
524 					case SENSORSD_S_INVALID:
525 						s = "invalid";
526 						break;
527 					case SENSORSD_S_WITHIN:
528 						s = "within";
529 						break;
530 					case SENSORSD_S_ABOVE:
531 						s = "above";
532 						break;
533 					case SENSORSD_S_BELOW:
534 						s = "below";
535 						break;
536 					}
537 					r = snprintf(&buf[n], len - n, "%s",
538 					    s);
539 					break;
540 				}
541 				case 's':
542 				{
543 					char *s;
544 					switch(limit->astatus){
545 					case SENSOR_S_UNSPEC:
546 						s = "UNSPEC";
547 						break;
548 					case SENSOR_S_OK:
549 						s = "OK";
550 						break;
551 					case SENSOR_S_WARN:
552 						s = "WARNING";
553 						break;
554 					case SENSOR_S_CRIT:
555 						s = "CRITICAL";
556 						break;
557 					default:
558 						s = "UNKNOWN";
559 					}
560 					r = snprintf(&buf[n], len - n, "%s",
561 					    s);
562 					break;
563 				}
564 				case '2':
565 					r = snprintf(&buf[n], len - n, "%s",
566 					    print_sensor(limit->type,
567 					    limit->last_val));
568 					break;
569 				case '3':
570 					r = snprintf(&buf[n], len - n, "%s",
571 					    print_sensor(limit->type,
572 					    limit->lower));
573 					break;
574 				case '4':
575 					r = snprintf(&buf[n], len - n, "%s",
576 					    print_sensor(limit->type,
577 					    limit->upper));
578 					break;
579 				default:
580 					r = snprintf(&buf[n], len - n, "%%%c",
581 					    cmd[i]);
582 					break;
583 				}
584 				if (r < 0 || (r >= len - n)) {
585 					syslog(LOG_CRIT, "could not parse "
586 					    "command");
587 					return;
588 				}
589 				if (r > 0)
590 					n += r;
591 			}
592 			if (buf[0])
593 				execute(buf);
594 		}
595 	}
596 }
597 
598 const char *drvstat[] = {
599 	NULL, "empty", "ready", "powerup", "online", "idle", "active",
600 	"rebuild", "powerdown", "fail", "pfail"
601 };
602 
603 static char *
604 print_sensor(enum sensor_type type, int64_t value)
605 {
606 	static char	 rfbuf[RFBUFCNT][RFBUFSIZ];	/* ring buffer */
607 	static int	 idx;
608 	char		*fbuf;
609 
610 	fbuf = rfbuf[idx++];
611 	if (idx == RFBUFCNT)
612 		idx = 0;
613 
614 	switch (type) {
615 	case SENSOR_TEMP:
616 		snprintf(fbuf, RFBUFSIZ, "%.2f degC",
617 		    (value - 273150000) / 1000000.0);
618 		break;
619 	case SENSOR_FANRPM:
620 		snprintf(fbuf, RFBUFSIZ, "%lld RPM", value);
621 		break;
622 	case SENSOR_VOLTS_DC:
623 		snprintf(fbuf, RFBUFSIZ, "%.2f V DC", value / 1000000.0);
624 		break;
625 	case SENSOR_AMPS:
626 		snprintf(fbuf, RFBUFSIZ, "%.2f A", value / 1000000.0);
627 		break;
628 	case SENSOR_WATTHOUR:
629 		snprintf(fbuf, RFBUFSIZ, "%.2f Wh", value / 1000000.0);
630 		break;
631 	case SENSOR_AMPHOUR:
632 		snprintf(fbuf, RFBUFSIZ, "%.2f Ah", value / 1000000.0);
633 		break;
634 	case SENSOR_INDICATOR:
635 		snprintf(fbuf, RFBUFSIZ, "%s", value? "On" : "Off");
636 		break;
637 	case SENSOR_INTEGER:
638 		snprintf(fbuf, RFBUFSIZ, "%lld", value);
639 		break;
640 	case SENSOR_PERCENT:
641 		snprintf(fbuf, RFBUFSIZ, "%.2f%%", value / 1000.0);
642 		break;
643 	case SENSOR_LUX:
644 		snprintf(fbuf, RFBUFSIZ, "%.2f lx", value / 1000000.0);
645 		break;
646 	case SENSOR_DRIVE:
647 		if (0 < value && value < sizeof(drvstat)/sizeof(drvstat[0]))
648 			snprintf(fbuf, RFBUFSIZ, "%s", drvstat[value]);
649 		else
650 			snprintf(fbuf, RFBUFSIZ, "%lld ???", value);
651 		break;
652 	case SENSOR_TIMEDELTA:
653 		snprintf(fbuf, RFBUFSIZ, "%.6f secs", value / 1000000000.0);
654 		break;
655 	default:
656 		snprintf(fbuf, RFBUFSIZ, "%lld ???", value);
657 	}
658 
659 	return (fbuf);
660 }
661 
662 void
663 parse_config(char *cf)
664 {
665 	struct sdlim_t	 *sdlim;
666 
667 	TAILQ_FOREACH(sdlim, &sdlims, entries)
668 		parse_config_sdlim(sdlim, cf);
669 }
670 
671 void
672 parse_config_sdlim(struct sdlim_t *sdlim, char *cf)
673 {
674 	struct limits_t	 *p;
675 	char		 *buf = NULL, *ebuf = NULL;
676 	char		  node[48];
677 	char		 *cfa[2];
678 
679 	cfa[0] = cf;
680 	cfa[1] = NULL;
681 
682 	TAILQ_FOREACH(p, &sdlim->limits, entries) {
683 		snprintf(node, sizeof(node), "hw.sensors.%s.%s%d",
684 		    sdlim->dxname, sensor_type_s[p->type], p->numt);
685 		p->flags = 0;
686 		if (cgetent(&buf, cfa, node) != 0)
687 			if (cgetent(&buf, cfa, sensor_type_s[p->type]) != 0)
688 				continue;
689 		if (cgetcap(buf, "istatus", ':'))
690 			p->flags |= SENSORSD_L_ISTATUS;
691 		if (cgetstr(buf, "low", &ebuf) < 0)
692 			ebuf = NULL;
693 		p->lower = get_val(ebuf, 0, p->type);
694 		if (cgetstr(buf, "high", &ebuf) < 0)
695 			ebuf = NULL;
696 		p->upper = get_val(ebuf, 1, p->type);
697 		if (cgetstr(buf, "command", &ebuf) < 0)
698 			ebuf = NULL;
699 		if (ebuf)
700 			asprintf(&(p->command), "%s", ebuf);
701 		free(buf);
702 		buf = NULL;
703 		if (p->lower != LLONG_MIN || p->upper != LLONG_MAX)
704 			p->flags |= SENSORSD_L_USERLIMIT;
705 	}
706 }
707 
708 int64_t
709 get_val(char *buf, int upper, enum sensor_type type)
710 {
711 	double	 val;
712 	int64_t	 rval = 0;
713 	char	*p;
714 
715 	if (buf == NULL) {
716 		if (upper)
717 			return (LLONG_MAX);
718 		else
719 			return (LLONG_MIN);
720 	}
721 
722 	val = strtod(buf, &p);
723 	if (buf == p)
724 		err(1, "incorrect value: %s", buf);
725 
726 	switch(type) {
727 	case SENSOR_TEMP:
728 		switch(*p) {
729 		case 'C':
730 			printf("C");
731 			rval = val * 1000 * 1000 + 273150000;
732 			break;
733 		case 'F':
734 			printf("F");
735 			rval = (val * 1000 * 1000 + 459670000) / 9 * 5;
736 			break;
737 		default:
738 			errx(1, "unknown unit %s for temp sensor", p);
739 		}
740 		break;
741 	case SENSOR_FANRPM:
742 		rval = val;
743 		break;
744 	case SENSOR_VOLTS_DC:
745 		if (*p != 'V')
746 			errx(1, "unknown unit %s for voltage sensor", p);
747 		rval = val * 1000 * 1000;
748 		break;
749 	case SENSOR_PERCENT:
750 		rval = val * 1000.0;
751 		break;
752 	case SENSOR_INDICATOR:
753 	case SENSOR_INTEGER:
754 	case SENSOR_DRIVE:
755 		rval = val;
756 		break;
757 	case SENSOR_AMPS:
758 	case SENSOR_WATTHOUR:
759 	case SENSOR_AMPHOUR:
760 	case SENSOR_LUX:
761 		rval = val * 1000 * 1000;
762 		break;
763 	case SENSOR_TIMEDELTA:
764 		rval = val * 1000 * 1000 * 1000;
765 		break;
766 	default:
767 		errx(1, "unsupported sensor type");
768 		/* not reached */
769 	}
770 	free(buf);
771 	return (rval);
772 }
773 
774 /* ARGSUSED */
775 void
776 reparse_cfg(int signo)
777 {
778 	reload = 1;
779 }
780