xref: /dragonfly/usr.sbin/sensorsd/sensorsd.c (revision b7367ef6)
1 /* $OpenBSD: sensorsd.c,v 1.34 2007/08/14 17:10:02 cnst Exp $ */
2 /* $DragonFly: src/usr.sbin/sensorsd/sensorsd.c,v 1.1 2007/10/02 12:57:01 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/cdefs.h>
23 #include <sys/param.h>
24 #include <sys/sysctl.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 
37 #define	RFBUFSIZ	28	/* buffer size for print_sensor */
38 #define	RFBUFCNT	4	/* ring buffers */
39 #define REPORT_PERIOD	60	/* report every n seconds */
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_OUTSIDE	/* status is outside limits */
47 };
48 
49 struct limits_t {
50 	TAILQ_ENTRY(limits_t)	entries;
51 	enum sensor_type	type;		/* sensor type */
52 	int			numt;		/* sensor number */
53 	int64_t			last_val;
54 	int64_t			lower;		/* lower limit */
55 	int64_t			upper;		/* upper limit */
56 	char			*command;	/* failure command */
57 	time_t			astatus_changed;
58 	time_t			ustatus_changed;
59 	enum sensor_status	astatus;	/* last automatic status */
60 	enum sensor_status	astatus2;
61 	enum sensorsd_s_status	ustatus;	/* last user-limit status */
62 	enum sensorsd_s_status	ustatus2;
63 	int			acount;		/* stat change counter */
64 	int			ucount;		/* stat change counter */
65 	u_int8_t		flags;		/* sensorsd limit flags */
66 #define SENSORSD_L_USERLIMIT		0x0001	/* user specified limit */
67 #define SENSORSD_L_ISTATUS		0x0002	/* ignore automatic status */
68 };
69 
70 struct sdlim_t {
71 	TAILQ_ENTRY(sdlim_t)	entries;
72 	char			dxname[16];	/* device unix name */
73 	int			dev;		/* device number */
74 	int			sensor_cnt;
75 	TAILQ_HEAD(, limits_t)	limits;
76 };
77 
78 void		 usage(void);
79 struct sdlim_t	*create_sdlim(struct sensordev *);
80 void		 check(void);
81 void		 check_sdlim(struct sdlim_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(, sdlim_t) sdlims = TAILQ_HEAD_INITIALIZER(sdlims);
92 
93 char			 *configfile;
94 volatile sig_atomic_t	  reload = 0;
95 int			  debug = 0;
96 
97 void
98 usage(void)
99 {
100 	extern char *__progname;
101 	fprintf(stderr, "usage: %s [-d]\n", __progname);
102 	exit(1);
103 }
104 
105 int
106 main(int argc, char *argv[])
107 {
108 	struct sensordev sensordev;
109 	struct sdlim_t	*sdlim;
110 	size_t		 sdlen = sizeof(sensordev);
111 	time_t		 next_report, last_report = 0, next_check;
112 	int		 mib[3], dev;
113 	int		 sleeptime, sensor_cnt = 0, ch;
114 
115 	while ((ch = getopt(argc, argv, "d")) != -1) {
116 		switch (ch) {
117 		case 'd':
118 			debug = 1;
119 			break;
120 		default:
121 			usage();
122 		}
123 	}
124 
125 	mib[0] = CTL_HW;
126 	mib[1] = HW_SENSORS;
127 
128 	for (dev = 0; dev < MAXSENSORDEVICES; dev++) {
129 		mib[2] = dev;
130 		if (sysctl(mib, 3, &sensordev, &sdlen, NULL, 0) == -1) {
131 			if (errno != ENOENT)
132 				warn("sysctl");
133 			continue;
134 		}
135 		sdlim = create_sdlim(&sensordev);
136 		TAILQ_INSERT_TAIL(&sdlims, sdlim, entries);
137 		sensor_cnt += sdlim->sensor_cnt;
138 	}
139 
140 	if (sensor_cnt == 0)
141 		errx(1, "no sensors found");
142 
143 	openlog("sensorsd", LOG_PID | LOG_NDELAY, LOG_DAEMON);
144 
145 	if (configfile == NULL)
146 		if (asprintf(&configfile, "/etc/sensorsd.conf") == -1)
147 			err(1, "out of memory");
148 	parse_config(configfile);
149 
150 	if (debug == 0 && daemon(0, 0) == -1)
151 		err(1, "unable to fork");
152 
153 	signal(SIGHUP, reparse_cfg);
154 	signal(SIGCHLD, SIG_IGN);
155 
156 	syslog(LOG_INFO, "startup, system has %d sensors", sensor_cnt);
157 
158 	next_check = next_report = time(NULL);
159 
160 	for (;;) {
161 		if (reload) {
162 			parse_config(configfile);
163 			syslog(LOG_INFO, "configuration reloaded");
164 			reload = 0;
165 		}
166 		if (next_check <= time(NULL)) {
167 			check();
168 			next_check = time(NULL) + CHECK_PERIOD;
169 		}
170 		if (next_report <= time(NULL)) {
171 			report(last_report);
172 			last_report = next_report;
173 			next_report = time(NULL) + REPORT_PERIOD;
174 		}
175 		if (next_report < next_check)
176 			sleeptime = next_report - time(NULL);
177 		else
178 			sleeptime = next_check - time(NULL);
179 		if (sleeptime > 0)
180 			sleep(sleeptime);
181 	}
182 }
183 
184 struct sdlim_t *
185 create_sdlim(struct sensordev *snsrdev)
186 {
187 	struct sensor	 sensor;
188 	struct sdlim_t	*sdlim;
189 	struct limits_t	*limit;
190 	size_t		 slen = sizeof(sensor);
191 	int		 mib[5], numt;
192 	enum sensor_type type;
193 
194 	if ((sdlim = calloc(1, sizeof(struct sdlim_t))) == NULL)
195 		err(1, "calloc");
196 
197 	strlcpy(sdlim->dxname, snsrdev->xname, sizeof(sdlim->dxname));
198 
199 	mib[0] = CTL_HW;
200 	mib[1] = HW_SENSORS;
201 	mib[2] = sdlim->dev = snsrdev->num;
202 
203 	TAILQ_INIT(&sdlim->limits);
204 
205 	for (type = 0; type < SENSOR_MAX_TYPES; type++) {
206 		mib[3] = type;
207 		for (numt = 0; numt < snsrdev->maxnumt[type]; numt++) {
208 			mib[4] = numt;
209 			if (sysctl(mib, 5, &sensor, &slen, NULL, 0) == -1) {
210 				if (errno != ENOENT)
211 					warn("sysctl");
212 				continue;
213 			}
214 			if ((limit = calloc(1, sizeof(struct limits_t))) ==
215 			    NULL)
216 				err(1, "calloc");
217 			limit->type = type;
218 			limit->numt = numt;
219 			TAILQ_INSERT_TAIL(&sdlim->limits, limit, entries);
220 			sdlim->sensor_cnt++;
221 		}
222 	}
223 
224 	return (sdlim);
225 }
226 
227 void
228 check(void)
229 {
230 	struct sdlim_t	*sdlim;
231 
232 	TAILQ_FOREACH(sdlim, &sdlims, entries)
233 		check_sdlim(sdlim);
234 }
235 
236 void
237 check_sdlim(struct sdlim_t *sdlim)
238 {
239 	struct sensor		 sensor;
240 	struct limits_t		*limit;
241 	size_t		 	 len;
242 	int		 	 mib[5];
243 
244 	mib[0] = CTL_HW;
245 	mib[1] = HW_SENSORS;
246 	mib[2] = sdlim->dev;
247 	len = sizeof(sensor);
248 
249 	TAILQ_FOREACH(limit, &sdlim->limits, entries) {
250 		if ((limit->flags & SENSORSD_L_ISTATUS) &&
251 		    !(limit->flags & SENSORSD_L_USERLIMIT))
252 			continue;
253 
254 		mib[3] = limit->type;
255 		mib[4] = limit->numt;
256 		if (sysctl(mib, 5, &sensor, &len, NULL, 0) == -1)
257 			err(1, "sysctl");
258 
259 		if (!(limit->flags & SENSORSD_L_ISTATUS)) {
260 			enum sensor_status	newastatus = sensor.status;
261 
262 			if (limit->astatus != newastatus) {
263 				if (limit->astatus2 != newastatus) {
264 					limit->astatus2 = newastatus;
265 					limit->acount = 0;
266 				} else if (++limit->acount >= 3) {
267 					limit->last_val = sensor.value;
268 					limit->astatus2 =
269 					    limit->astatus = newastatus;
270 					limit->astatus_changed = time(NULL);
271 				}
272 			}
273 		}
274 
275 		if (limit->flags & SENSORSD_L_USERLIMIT) {
276 			enum sensorsd_s_status 	 newustatus;
277 
278 			if (sensor.flags & SENSOR_FINVALID)
279 				newustatus = SENSORSD_S_INVALID;
280 			else if (sensor.value > limit->upper ||
281 				sensor.value < limit->lower)
282 				newustatus = SENSORSD_S_OUTSIDE;
283 			else
284 				newustatus = SENSORSD_S_WITHIN;
285 
286 			if (limit->ustatus != newustatus) {
287 				if (limit->ustatus2 != newustatus) {
288 					limit->ustatus2 = newustatus;
289 					limit->ucount = 0;
290 				} else if (++limit->ucount >= 3) {
291 					limit->last_val = sensor.value;
292 					limit->ustatus2 =
293 					    limit->ustatus = newustatus;
294 					limit->ustatus_changed = time(NULL);
295 				}
296 			}
297 		}
298 	}
299 }
300 
301 void
302 execute(char *command)
303 {
304 	char *argp[] = {"sh", "-c", command, NULL};
305 
306 	switch (fork()) {
307 	case -1:
308 		syslog(LOG_CRIT, "execute: fork() failed");
309 		break;
310 	case 0:
311 		execv("/bin/sh", argp);
312 		_exit(1);
313 		/* NOTREACHED */
314 	default:
315 		break;
316 	}
317 }
318 
319 void
320 report(time_t last_report)
321 {
322 	struct sdlim_t	*sdlim;
323 
324 	TAILQ_FOREACH(sdlim, &sdlims, entries)
325 		report_sdlim(sdlim, last_report);
326 }
327 
328 void
329 report_sdlim(struct sdlim_t *sdlim, time_t last_report)
330 {
331 	struct limits_t	*limit;
332 
333 	TAILQ_FOREACH(limit, &sdlim->limits, entries) {
334 		if ((limit->astatus_changed <= last_report) &&
335 		    (limit->ustatus_changed <= last_report))
336 			continue;
337 
338 		if (limit->astatus_changed > last_report) {
339 			const char *as = NULL;
340 
341 			switch (limit->astatus) {
342 			case SENSOR_S_UNSPEC:
343 				as = "";
344 				break;
345 			case SENSOR_S_OK:
346 				as = ", OK";
347 				break;
348 			case SENSOR_S_WARN:
349 				as = ", WARN";
350 				break;
351 			case SENSOR_S_CRIT:
352 				as = ", CRITICAL";
353 				break;
354 			case SENSOR_S_UNKNOWN:
355 				as = ", UNKNOWN";
356 				break;
357 			}
358 			syslog(LOG_ALERT, "%s.%s%d: %s%s",
359 			    sdlim->dxname, sensor_type_s[limit->type],
360 			    limit->numt,
361 			    print_sensor(limit->type, limit->last_val), as);
362 		}
363 
364 		if (limit->ustatus_changed > last_report) {
365 			char us[BUFSIZ];
366 
367 			switch (limit->ustatus) {
368 			case SENSORSD_S_UNSPEC:
369 				snprintf(us, sizeof(us),
370 				    "ustatus uninitialised");
371 				break;
372 			case SENSORSD_S_INVALID:
373 				snprintf(us, sizeof(us), "marked invalid");
374 				break;
375 			case SENSORSD_S_WITHIN:
376 				snprintf(us, sizeof(us), "within limits: %s",
377 				    print_sensor(limit->type, limit->last_val));
378 				break;
379 			case SENSORSD_S_OUTSIDE:
380 				snprintf(us, sizeof(us), "exceeds limits: %s",
381 				    print_sensor(limit->type, limit->last_val));
382 				break;
383 			}
384 			syslog(LOG_ALERT, "%s.%s%d: %s",
385 			    sdlim->dxname, sensor_type_s[limit->type],
386 			    limit->numt, us);
387 		}
388 
389 		if (limit->command) {
390 			int i = 0, n = 0, r;
391 			char *cmd = limit->command;
392 			char buf[BUFSIZ];
393 			int len = sizeof(buf);
394 
395 			buf[0] = '\0';
396 			for (i = n = 0; n < len; ++i) {
397 				if (cmd[i] == '\0') {
398 					buf[n++] = '\0';
399 					break;
400 				}
401 				if (cmd[i] != '%') {
402 					buf[n++] = limit->command[i];
403 					continue;
404 				}
405 				i++;
406 				if (cmd[i] == '\0') {
407 					buf[n++] = '\0';
408 					break;
409 				}
410 
411 				switch (cmd[i]) {
412 				case 'x':
413 					r = snprintf(&buf[n], len - n, "%s",
414 					    sdlim->dxname);
415 					break;
416 				case 't':
417 					r = snprintf(&buf[n], len - n, "%s",
418 					    sensor_type_s[limit->type]);
419 					break;
420 				case 'n':
421 					r = snprintf(&buf[n], len - n, "%d",
422 					    limit->numt);
423 					break;
424 				case '2':
425 					r = snprintf(&buf[n], len - n, "%s",
426 					    print_sensor(limit->type,
427 					    limit->last_val));
428 					break;
429 				case '3':
430 					r = snprintf(&buf[n], len - n, "%s",
431 					    print_sensor(limit->type,
432 					    limit->lower));
433 					break;
434 				case '4':
435 					r = snprintf(&buf[n], len - n, "%s",
436 					    print_sensor(limit->type,
437 					    limit->upper));
438 					break;
439 				default:
440 					r = snprintf(&buf[n], len - n, "%%%c",
441 					    cmd[i]);
442 					break;
443 				}
444 				if (r < 0 || (r >= len - n)) {
445 					syslog(LOG_CRIT, "could not parse "
446 					    "command");
447 					return;
448 				}
449 				if (r > 0)
450 					n += r;
451 			}
452 			if (buf[0])
453 				execute(buf);
454 		}
455 	}
456 }
457 
458 const char *drvstat[] = {
459 	NULL, "empty", "ready", "powerup", "online", "idle", "active",
460 	"rebuild", "powerdown", "fail", "pfail"
461 };
462 
463 static char *
464 print_sensor(enum sensor_type type, int64_t value)
465 {
466 	static char	 rfbuf[RFBUFCNT][RFBUFSIZ];	/* ring buffer */
467 	static int	 idx;
468 	char		*fbuf;
469 
470 	fbuf = rfbuf[idx++];
471 	if (idx == RFBUFCNT)
472 		idx = 0;
473 
474 	switch (type) {
475 	case SENSOR_TEMP:
476 		snprintf(fbuf, RFBUFSIZ, "%.2f degC",
477 		    (value - 273150000) / 1000000.0);
478 		break;
479 	case SENSOR_FANRPM:
480 		snprintf(fbuf, RFBUFSIZ, "%lld RPM", value);
481 		break;
482 	case SENSOR_VOLTS_DC:
483 		snprintf(fbuf, RFBUFSIZ, "%.2f V DC", value / 1000000.0);
484 		break;
485 	case SENSOR_AMPS:
486 		snprintf(fbuf, RFBUFSIZ, "%.2f A", value / 1000000.0);
487 		break;
488 	case SENSOR_WATTHOUR:
489 		snprintf(fbuf, RFBUFSIZ, "%.2f Wh", value / 1000000.0);
490 		break;
491 	case SENSOR_AMPHOUR:
492 		snprintf(fbuf, RFBUFSIZ, "%.2f Ah", value / 1000000.0);
493 		break;
494 	case SENSOR_INDICATOR:
495 		snprintf(fbuf, RFBUFSIZ, "%s", value? "On" : "Off");
496 		break;
497 	case SENSOR_INTEGER:
498 		snprintf(fbuf, RFBUFSIZ, "%lld", value);
499 		break;
500 	case SENSOR_PERCENT:
501 		snprintf(fbuf, RFBUFSIZ, "%.2f%%", value / 1000.0);
502 		break;
503 	case SENSOR_LUX:
504 		snprintf(fbuf, RFBUFSIZ, "%.2f lx", value / 1000000.0);
505 		break;
506 	case SENSOR_DRIVE:
507 		if (0 < value && value < sizeof(drvstat)/sizeof(drvstat[0]))
508 			snprintf(fbuf, RFBUFSIZ, "%s", drvstat[value]);
509 		else
510 			snprintf(fbuf, RFBUFSIZ, "%lld ???", value);
511 		break;
512 	case SENSOR_TIMEDELTA:
513 		snprintf(fbuf, RFBUFSIZ, "%.6f secs", value / 1000000000.0);
514 		break;
515 	default:
516 		snprintf(fbuf, RFBUFSIZ, "%lld ???", value);
517 	}
518 
519 	return (fbuf);
520 }
521 
522 void
523 parse_config(char *cf)
524 {
525 	struct sdlim_t	 *sdlim;
526 	char		**cfa;
527 
528 	if ((cfa = calloc(2, sizeof(char *))) == NULL)
529 		err(1, "calloc");
530 	cfa[0] = cf;
531 	cfa[1] = NULL;
532 
533 	TAILQ_FOREACH(sdlim, &sdlims, entries)
534 		parse_config_sdlim(sdlim, cfa);
535 	free(cfa);
536 }
537 
538 void
539 parse_config_sdlim(struct sdlim_t *sdlim, char **cfa)
540 {
541 	struct limits_t	 *p;
542 	char		 *buf = NULL, *ebuf = NULL;
543 	char		  node[48];
544 
545 	TAILQ_FOREACH(p, &sdlim->limits, entries) {
546 		snprintf(node, sizeof(node), "hw.sensors.%s.%s%d",
547 		    sdlim->dxname, sensor_type_s[p->type], p->numt);
548 		p->flags = 0;
549 		if (cgetent(&buf, cfa, node) != 0)
550 			if (cgetent(&buf, cfa, sensor_type_s[p->type]) != 0)
551 				continue;
552 		if (cgetcap(buf, "istatus", ':'))
553 			p->flags |= SENSORSD_L_ISTATUS;
554 		if (cgetstr(buf, "low", &ebuf) < 0)
555 			ebuf = NULL;
556 		p->lower = get_val(ebuf, 0, p->type);
557 		if (cgetstr(buf, "high", &ebuf) < 0)
558 			ebuf = NULL;
559 		p->upper = get_val(ebuf, 1, p->type);
560 		if (cgetstr(buf, "command", &ebuf) < 0)
561 			ebuf = NULL;
562 		if (ebuf)
563 			asprintf(&(p->command), "%s", ebuf);
564 		free(buf);
565 		buf = NULL;
566 		if (p->lower != LLONG_MIN || p->upper != LLONG_MAX)
567 			p->flags |= SENSORSD_L_USERLIMIT;
568 	}
569 }
570 
571 int64_t
572 get_val(char *buf, int upper, enum sensor_type type)
573 {
574 	double	 val;
575 	int64_t	 rval = 0;
576 	char	*p;
577 
578 	if (buf == NULL) {
579 		if (upper)
580 			return (LLONG_MAX);
581 		else
582 			return (LLONG_MIN);
583 	}
584 
585 	val = strtod(buf, &p);
586 	if (buf == p)
587 		err(1, "incorrect value: %s", buf);
588 
589 	switch(type) {
590 	case SENSOR_TEMP:
591 		switch(*p) {
592 		case 'C':
593 			printf("C");
594 			rval = (val + 273.16) * 1000 * 1000;
595 			break;
596 		case 'F':
597 			printf("F");
598 			rval = ((val - 32.0) / 9 * 5 + 273.16) * 1000 * 1000;
599 			break;
600 		default:
601 			errx(1, "unknown unit %s for temp sensor", p);
602 		}
603 		break;
604 	case SENSOR_FANRPM:
605 		rval = val;
606 		break;
607 	case SENSOR_VOLTS_DC:
608 		if (*p != 'V')
609 			errx(1, "unknown unit %s for voltage sensor", p);
610 		rval = val * 1000 * 1000;
611 		break;
612 	case SENSOR_PERCENT:
613 		rval = val * 1000.0;
614 		break;
615 	case SENSOR_INDICATOR:
616 	case SENSOR_INTEGER:
617 	case SENSOR_DRIVE:
618 		rval = val;
619 		break;
620 	case SENSOR_AMPS:
621 	case SENSOR_WATTHOUR:
622 	case SENSOR_AMPHOUR:
623 	case SENSOR_LUX:
624 		rval = val * 1000 * 1000;
625 		break;
626 	case SENSOR_TIMEDELTA:
627 		rval = val * 1000 * 1000 * 1000;
628 		break;
629 	default:
630 		errx(1, "unsupported sensor type");
631 		/* not reached */
632 	}
633 	free(buf);
634 	return (rval);
635 }
636 
637 /* ARGSUSED */
638 void
639 reparse_cfg(int signo)
640 {
641 	reload = 1;
642 }
643