1 /* $OpenBSD: sensorsd.c,v 1.46 2008/06/14 00:16:10 cnst Exp $ */
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>
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.
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.
21 #include <sys/param.h>
22 #include <sys/sysctl.h>
23 #include <sys/sensors.h>
36 #define RFBUFSIZ 28 /* buffer size for print_sensor */
37 #define RFBUFCNT 4 /* ring buffers */
38 #define CHECK_PERIOD 20 /* check every n seconds */
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 */
49 TAILQ_ENTRY(limits_t) entries;
50 enum sensor_type type; /* sensor type */
51 int numt; /* sensor number */
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 */
70 TAILQ_ENTRY(sdlim_t) entries;
71 char dxname[16]; /* device unix name */
72 int dev; /* device number */
74 TAILQ_HEAD(, limits_t) limits;
79 struct sdlim_t *create_sdlim(struct sensordev *);
80 void destroy_sdlim(struct sdlim_t *);
82 void check_sdlim(struct sdlim_t *, 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);
92 TAILQ_HEAD(sdlimhead_t, sdlim_t);
93 struct sdlimhead_t sdlims = TAILQ_HEAD_INITIALIZER(sdlims);
96 volatile sig_atomic_t reload = 0;
102 fprintf(stderr, "usage: %s [-d] [-c check]\n", getprogname());
107 main(int argc, char *argv[])
109 time_t last_report = 0, this_check;
110 int ch, check_period = CHECK_PERIOD;
113 while ((ch = getopt(argc, argv, "c:d")) != -1) {
116 check_period = strtonum(optarg, 1, 600, &errstr);
118 errx(1, "check %s", errstr);
133 openlog("sensorsd", LOG_PID | LOG_NDELAY, LOG_DAEMON);
137 if (configfile == NULL)
138 if (asprintf(&configfile, "/etc/sensorsd.conf") == -1)
139 err(1, "out of memory");
140 parse_config(configfile);
142 if (debug == 0 && daemon(0, 0) == -1)
143 err(1, "unable to fork");
145 signal(SIGHUP, reparse_cfg);
146 signal(SIGCHLD, SIG_IGN);
150 parse_config(configfile);
151 syslog(LOG_INFO, "configuration reloaded");
154 this_check = time(NULL);
155 if (!(last_report < this_check))
156 this_check = last_report + 1;
159 last_report = this_check;
167 struct sensordev sensordev;
168 struct sdlim_t *sdlim;
169 size_t sdlen = sizeof(sensordev);
170 int mib[3], dev, sensor_cnt = 0;
175 for (dev = 0; dev < MAXSENSORDEVICES; dev++) {
177 if (sysctl(mib, 3, &sensordev, &sdlen, NULL, 0) == -1) {
182 sdlim = create_sdlim(&sensordev);
183 TAILQ_INSERT_TAIL(&sdlims, sdlim, entries);
184 sensor_cnt += sdlim->sensor_cnt;
187 syslog(LOG_INFO, "startup, system has %d sensors", sensor_cnt);
191 create_sdlim(struct sensordev *snsrdev)
193 struct sensor sensor;
194 struct sdlim_t *sdlim;
195 struct limits_t *limit;
196 size_t slen = sizeof(sensor);
198 enum sensor_type type;
200 if ((sdlim = calloc(1, sizeof(struct sdlim_t))) == NULL)
203 strlcpy(sdlim->dxname, snsrdev->xname, sizeof(sdlim->dxname));
207 mib[2] = sdlim->dev = snsrdev->num;
209 TAILQ_INIT(&sdlim->limits);
211 for (type = 0; type < SENSOR_MAX_TYPES; type++) {
213 for (numt = 0; numt < snsrdev->maxnumt[type]; numt++) {
215 if (sysctl(mib, 5, &sensor, &slen, NULL, 0) == -1) {
220 if ((limit = calloc(1, sizeof(struct limits_t))) ==
225 TAILQ_INSERT_TAIL(&sdlim->limits, limit, entries);
234 destroy_sdlim(struct sdlim_t *sdlim)
236 struct limits_t *limit;
238 while((limit = TAILQ_FIRST(&sdlim->limits)) != NULL) {
239 TAILQ_REMOVE(&sdlim->limits, limit, entries);
240 if (limit->command != NULL)
241 free(limit->command);
248 check(time_t this_check)
250 struct sensordev sensordev;
251 struct sdlim_t *sdlim, *next;
254 size_t sdlen = sizeof(sensordev);
256 if (TAILQ_EMPTY(&sdlims)) {
260 h = TAILQ_FIRST(&sdlims)->dev;
261 t = TAILQ_LAST(&sdlims, sdlimhead_t)->dev;
263 sdlim = TAILQ_FIRST(&sdlims);
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");
274 if (sysctl(mib, 3, &sensordev, &sdlen, NULL, 0) == -1) {
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",
282 destroy_sdlim(sdlim);
287 if (sdlim != NULL && i == sdlim->dev) {
288 if (strcmp(sdlim->dxname, sensordev.xname) == 0) {
289 check_sdlim(sdlim, this_check);
292 next = TAILQ_NEXT(sdlim, entries);
293 TAILQ_REMOVE(&sdlims, sdlim, entries);
294 syslog(LOG_INFO, "%s has been replaced",
296 destroy_sdlim(sdlim);
300 next = create_sdlim(&sensordev);
301 /* inserting next before sdlim */
303 TAILQ_INSERT_BEFORE(sdlim, next, entries);
305 TAILQ_INSERT_TAIL(&sdlims, next, entries);
306 syslog(LOG_INFO, "%s has appeared", next->dxname);
308 parse_config_sdlim(sdlim, configfile);
309 check_sdlim(sdlim, this_check);
312 if (TAILQ_EMPTY(&sdlims))
314 /* Ensure that our queue is consistent. */
315 for (sdlim = TAILQ_FIRST(&sdlims);
316 (next = TAILQ_NEXT(sdlim, entries)) != NULL;
318 if (sdlim->dev > next->dev)
319 syslog(LOG_ALERT, "inconsistent sdlims queue");
323 check_sdlim(struct sdlim_t *sdlim, time_t this_check)
325 struct sensor sensor;
326 struct limits_t *limit;
333 len = sizeof(sensor);
335 TAILQ_FOREACH(limit, &sdlim->limits, entries) {
336 if ((limit->flags & SENSORSD_L_ISTATUS) &&
337 !(limit->flags & SENSORSD_L_USERLIMIT))
340 mib[3] = limit->type;
341 mib[4] = limit->numt;
342 if (sysctl(mib, 5, &sensor, &len, NULL, 0) == -1)
345 if (!(limit->flags & SENSORSD_L_ISTATUS)) {
346 enum sensor_status newastatus = sensor.status;
348 if (limit->astatus != newastatus) {
349 if (limit->astatus2 != newastatus) {
350 limit->astatus2 = newastatus;
352 } else if (++limit->acount >= 3) {
353 limit->last_val = sensor.value;
355 limit->astatus = newastatus;
356 limit->astatus_changed = this_check;
361 if (limit->flags & SENSORSD_L_USERLIMIT) {
362 enum sensorsd_s_status newustatus;
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;
371 newustatus = SENSORSD_S_WITHIN;
373 if (limit->ustatus != newustatus) {
374 if (limit->ustatus2 != newustatus) {
375 limit->ustatus2 = newustatus;
377 } else if (++limit->ucount >= 3) {
378 limit->last_val = sensor.value;
380 limit->ustatus = newustatus;
381 limit->ustatus_changed = this_check;
389 execute(char *command)
391 const char *argp[] = {"sh", "-c", command, NULL};
395 syslog(LOG_CRIT, "execute: fork() failed");
398 execv("/bin/sh", __DECONST(char **, argp));
407 report(time_t last_report)
409 struct sdlim_t *sdlim;
411 TAILQ_FOREACH(sdlim, &sdlims, entries)
412 report_sdlim(sdlim, last_report);
416 report_sdlim(struct sdlim_t *sdlim, time_t last_report)
418 struct limits_t *limit;
420 TAILQ_FOREACH(limit, &sdlim->limits, entries) {
421 if ((limit->astatus_changed <= last_report) &&
422 (limit->ustatus_changed <= last_report))
425 if (limit->astatus_changed > last_report) {
426 const char *as = NULL;
428 switch (limit->astatus) {
429 case SENSOR_S_UNSPEC:
441 case SENSOR_S_UNKNOWN:
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],
449 print_sensor(limit->type, limit->last_val), as);
452 if (limit->ustatus_changed > last_report) {
455 switch (limit->ustatus) {
456 case SENSORSD_S_UNSPEC:
457 snprintf(us, sizeof(us),
458 "ustatus uninitialised");
460 case SENSORSD_S_INVALID:
461 snprintf(us, sizeof(us), "marked invalid");
463 case SENSORSD_S_WITHIN:
464 snprintf(us, sizeof(us), "within limits: %s",
465 print_sensor(limit->type, limit->last_val));
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));
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));
478 syslog(limit->ustatus == SENSORSD_S_WITHIN ? LOG_INFO :
479 LOG_ALERT, "%s.%s%d: %s",
480 sdlim->dxname, sensor_type_s[limit->type],
484 if (limit->command) {
486 char *cmd = limit->command;
488 int len = sizeof(buf);
491 for (i = n = 0; n < len; ++i) {
492 if (cmd[i] == '\0') {
497 buf[n++] = limit->command[i];
501 if (cmd[i] == '\0') {
508 r = snprintf(&buf[n], len - n, "%s",
512 r = snprintf(&buf[n], len - n, "%s",
513 sensor_type_s[limit->type]);
516 r = snprintf(&buf[n], len - n, "%d",
522 switch(limit->ustatus){
523 case SENSORSD_S_UNSPEC:
526 case SENSORSD_S_INVALID:
529 case SENSORSD_S_WITHIN:
532 case SENSORSD_S_ABOVE:
535 case SENSORSD_S_BELOW:
539 r = snprintf(&buf[n], len - n, "%s",
546 switch(limit->astatus){
547 case SENSOR_S_UNSPEC:
562 r = snprintf(&buf[n], len - n, "%s",
567 r = snprintf(&buf[n], len - n, "%s",
568 print_sensor(limit->type,
572 r = snprintf(&buf[n], len - n, "%s",
573 print_sensor(limit->type,
577 r = snprintf(&buf[n], len - n, "%s",
578 print_sensor(limit->type,
582 r = snprintf(&buf[n], len - n, "%%%c",
586 if (r < 0 || (r >= len - n)) {
587 syslog(LOG_CRIT, "could not parse "
600 const char *drvstat[] = {
601 NULL, "empty", "ready", "powerup", "online", "idle", "active",
602 "rebuild", "powerdown", "fail", "pfail"
606 print_sensor(enum sensor_type type, int64_t value)
608 static char rfbuf[RFBUFCNT][RFBUFSIZ]; /* ring buffer */
618 snprintf(fbuf, RFBUFSIZ, "%.2f degC",
619 (value - 273150000) / 1000000.0);
622 snprintf(fbuf, RFBUFSIZ, "%"PRId64" RPM", value);
624 case SENSOR_VOLTS_DC:
625 snprintf(fbuf, RFBUFSIZ, "%.2f V DC", value / 1000000.0);
628 snprintf(fbuf, RFBUFSIZ, "%.2f A", value / 1000000.0);
630 case SENSOR_WATTHOUR:
631 snprintf(fbuf, RFBUFSIZ, "%.2f Wh", value / 1000000.0);
634 snprintf(fbuf, RFBUFSIZ, "%.2f Ah", value / 1000000.0);
636 case SENSOR_INDICATOR:
637 snprintf(fbuf, RFBUFSIZ, "%s", value? "On" : "Off");
640 snprintf(fbuf, RFBUFSIZ, "%"PRId64, value);
643 snprintf(fbuf, RFBUFSIZ, "%.2f%%", value / 1000.0);
646 snprintf(fbuf, RFBUFSIZ, "%.2f lx", value / 1000000.0);
649 if (0 < value && value < (int64_t)NELEM(drvstat))
650 snprintf(fbuf, RFBUFSIZ, "%s", drvstat[value]);
652 snprintf(fbuf, RFBUFSIZ, "%"PRId64" ???", value);
654 case SENSOR_TIMEDELTA:
655 snprintf(fbuf, RFBUFSIZ, "%.6f secs", value / 1000000000.0);
658 snprintf(fbuf, RFBUFSIZ, "%"PRId64" ???", value);
665 parse_config(char *cf)
667 struct sdlim_t *sdlim;
669 TAILQ_FOREACH(sdlim, &sdlims, entries)
670 parse_config_sdlim(sdlim, cf);
674 parse_config_sdlim(struct sdlim_t *sdlim, char *cf)
677 char *buf = NULL, *ebuf = NULL;
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);
688 if (cgetent(&buf, cfa, node) != 0)
689 if (cgetent(&buf, cfa, sensor_type_s[p->type]) != 0)
691 if (cgetcap(buf, "istatus", ':'))
692 p->flags |= SENSORSD_L_ISTATUS;
693 if (cgetstr(buf, "low", &ebuf) < 0)
695 p->lower = get_val(ebuf, 0, p->type);
696 if (cgetstr(buf, "high", &ebuf) < 0)
698 p->upper = get_val(ebuf, 1, p->type);
699 if (cgetstr(buf, "command", &ebuf) < 0)
702 asprintf(&(p->command), "%s", ebuf);
705 if (p->lower != LLONG_MIN || p->upper != LLONG_MAX)
706 p->flags |= SENSORSD_L_USERLIMIT;
711 get_val(char *buf, int upper, enum sensor_type type)
724 val = strtod(buf, &p);
726 err(1, "incorrect value: %s", buf);
733 rval = val * 1000 * 1000 + 273150000;
737 rval = (val * 1000 * 1000 + 459670000) / 9 * 5;
740 errx(1, "unknown unit %s for temp sensor", p);
746 case SENSOR_VOLTS_DC:
748 errx(1, "unknown unit %s for voltage sensor", p);
749 rval = val * 1000 * 1000;
754 case SENSOR_INDICATOR:
760 case SENSOR_WATTHOUR:
763 rval = val * 1000 * 1000;
765 case SENSOR_TIMEDELTA:
766 rval = val * 1000 * 1000 * 1000;
769 errx(1, "unsupported sensor type");
778 reparse_cfg(__unused int signo)