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 $ */
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>
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.
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.
22 #include <sys/param.h>
23 #include <sys/sysctl.h>
24 #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 extern char *__progname;
103 fprintf(stderr, "usage: %s [-d] [-c check]\n", __progname);
108 main(int argc, char *argv[])
110 time_t last_report = 0, this_check;
111 int ch, check_period = CHECK_PERIOD;
114 while ((ch = getopt(argc, argv, "c:d")) != -1) {
117 check_period = strtonum(optarg, 1, 600, &errstr);
119 errx(1, "check %s", errstr);
134 openlog("sensorsd", LOG_PID | LOG_NDELAY, LOG_DAEMON);
138 if (configfile == NULL)
139 if (asprintf(&configfile, "/etc/sensorsd.conf") == -1)
140 err(1, "out of memory");
141 parse_config(configfile);
143 if (debug == 0 && daemon(0, 0) == -1)
144 err(1, "unable to fork");
146 signal(SIGHUP, reparse_cfg);
147 signal(SIGCHLD, SIG_IGN);
151 parse_config(configfile);
152 syslog(LOG_INFO, "configuration reloaded");
155 this_check = time(NULL);
156 if (!(last_report < this_check))
157 this_check = last_report + 1;
160 last_report = this_check;
168 struct sensordev sensordev;
169 struct sdlim_t *sdlim;
170 size_t sdlen = sizeof(sensordev);
171 int mib[3], dev, sensor_cnt = 0;
176 for (dev = 0; dev < MAXSENSORDEVICES; dev++) {
178 if (sysctl(mib, 3, &sensordev, &sdlen, NULL, 0) == -1) {
183 sdlim = create_sdlim(&sensordev);
184 TAILQ_INSERT_TAIL(&sdlims, sdlim, entries);
185 sensor_cnt += sdlim->sensor_cnt;
188 syslog(LOG_INFO, "startup, system has %d sensors", sensor_cnt);
192 create_sdlim(struct sensordev *snsrdev)
194 struct sensor sensor;
195 struct sdlim_t *sdlim;
196 struct limits_t *limit;
197 size_t slen = sizeof(sensor);
199 enum sensor_type type;
201 if ((sdlim = calloc(1, sizeof(struct sdlim_t))) == NULL)
204 strlcpy(sdlim->dxname, snsrdev->xname, sizeof(sdlim->dxname));
208 mib[2] = sdlim->dev = snsrdev->num;
210 TAILQ_INIT(&sdlim->limits);
212 for (type = 0; type < SENSOR_MAX_TYPES; type++) {
214 for (numt = 0; numt < snsrdev->maxnumt[type]; numt++) {
216 if (sysctl(mib, 5, &sensor, &slen, NULL, 0) == -1) {
221 if ((limit = calloc(1, sizeof(struct limits_t))) ==
226 TAILQ_INSERT_TAIL(&sdlim->limits, limit, entries);
235 destroy_sdlim(struct sdlim_t *sdlim)
237 struct limits_t *limit;
239 while((limit = TAILQ_FIRST(&sdlim->limits)) != NULL) {
240 TAILQ_REMOVE(&sdlim->limits, limit, entries);
241 if (limit->command != NULL)
242 free(limit->command);
249 check(time_t this_check)
251 struct sensordev sensordev;
252 struct sdlim_t *sdlim, *next;
255 size_t sdlen = sizeof(sensordev);
257 if (TAILQ_EMPTY(&sdlims)) {
261 h = TAILQ_FIRST(&sdlims)->dev;
262 t = TAILQ_LAST(&sdlims, sdlimhead_t)->dev;
264 sdlim = TAILQ_FIRST(&sdlims);
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");
275 if (sysctl(mib, 3, &sensordev, &sdlen, NULL, 0) == -1) {
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",
283 destroy_sdlim(sdlim);
288 if (sdlim != NULL && i == sdlim->dev) {
289 if (strcmp(sdlim->dxname, sensordev.xname) == 0) {
290 check_sdlim(sdlim, this_check);
293 next = TAILQ_NEXT(sdlim, entries);
294 TAILQ_REMOVE(&sdlims, sdlim, entries);
295 syslog(LOG_INFO, "%s has been replaced",
297 destroy_sdlim(sdlim);
301 next = create_sdlim(&sensordev);
302 /* inserting next before sdlim */
304 TAILQ_INSERT_BEFORE(sdlim, next, entries);
306 TAILQ_INSERT_TAIL(&sdlims, next, entries);
307 syslog(LOG_INFO, "%s has appeared", next->dxname);
309 parse_config_sdlim(sdlim, configfile);
310 check_sdlim(sdlim, this_check);
313 if (TAILQ_EMPTY(&sdlims))
315 /* Ensure that our queue is consistent. */
316 for (sdlim = TAILQ_FIRST(&sdlims);
317 (next = TAILQ_NEXT(sdlim, entries)) != NULL;
319 if (sdlim->dev > next->dev)
320 syslog(LOG_ALERT, "inconsistent sdlims queue");
324 check_sdlim(struct sdlim_t *sdlim, time_t this_check)
326 struct sensor sensor;
327 struct limits_t *limit;
334 len = sizeof(sensor);
336 TAILQ_FOREACH(limit, &sdlim->limits, entries) {
337 if ((limit->flags & SENSORSD_L_ISTATUS) &&
338 !(limit->flags & SENSORSD_L_USERLIMIT))
341 mib[3] = limit->type;
342 mib[4] = limit->numt;
343 if (sysctl(mib, 5, &sensor, &len, NULL, 0) == -1)
346 if (!(limit->flags & SENSORSD_L_ISTATUS)) {
347 enum sensor_status newastatus = sensor.status;
349 if (limit->astatus != newastatus) {
350 if (limit->astatus2 != newastatus) {
351 limit->astatus2 = newastatus;
353 } else if (++limit->acount >= 3) {
354 limit->last_val = sensor.value;
356 limit->astatus = newastatus;
357 limit->astatus_changed = this_check;
362 if (limit->flags & SENSORSD_L_USERLIMIT) {
363 enum sensorsd_s_status newustatus;
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;
372 newustatus = SENSORSD_S_WITHIN;
374 if (limit->ustatus != newustatus) {
375 if (limit->ustatus2 != newustatus) {
376 limit->ustatus2 = newustatus;
378 } else if (++limit->ucount >= 3) {
379 limit->last_val = sensor.value;
381 limit->ustatus = newustatus;
382 limit->ustatus_changed = this_check;
390 execute(char *command)
392 char *argp[] = {"sh", "-c", command, NULL};
396 syslog(LOG_CRIT, "execute: fork() failed");
399 execv("/bin/sh", argp);
408 report(time_t last_report)
410 struct sdlim_t *sdlim;
412 TAILQ_FOREACH(sdlim, &sdlims, entries)
413 report_sdlim(sdlim, last_report);
417 report_sdlim(struct sdlim_t *sdlim, time_t last_report)
419 struct limits_t *limit;
421 TAILQ_FOREACH(limit, &sdlim->limits, entries) {
422 if ((limit->astatus_changed <= last_report) &&
423 (limit->ustatus_changed <= last_report))
426 if (limit->astatus_changed > last_report) {
427 const char *as = NULL;
429 switch (limit->astatus) {
430 case SENSOR_S_UNSPEC:
442 case SENSOR_S_UNKNOWN:
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],
450 print_sensor(limit->type, limit->last_val), as);
453 if (limit->ustatus_changed > last_report) {
456 switch (limit->ustatus) {
457 case SENSORSD_S_UNSPEC:
458 snprintf(us, sizeof(us),
459 "ustatus uninitialised");
461 case SENSORSD_S_INVALID:
462 snprintf(us, sizeof(us), "marked invalid");
464 case SENSORSD_S_WITHIN:
465 snprintf(us, sizeof(us), "within limits: %s",
466 print_sensor(limit->type, limit->last_val));
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));
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));
479 syslog(limit->ustatus == SENSORSD_S_WITHIN ? LOG_INFO :
480 LOG_ALERT, "%s.%s%d: %s",
481 sdlim->dxname, sensor_type_s[limit->type],
485 if (limit->command) {
487 char *cmd = limit->command;
489 int len = sizeof(buf);
492 for (i = n = 0; n < len; ++i) {
493 if (cmd[i] == '\0') {
498 buf[n++] = limit->command[i];
502 if (cmd[i] == '\0') {
509 r = snprintf(&buf[n], len - n, "%s",
513 r = snprintf(&buf[n], len - n, "%s",
514 sensor_type_s[limit->type]);
517 r = snprintf(&buf[n], len - n, "%d",
523 switch(limit->ustatus){
524 case SENSORSD_S_UNSPEC:
527 case SENSORSD_S_INVALID:
530 case SENSORSD_S_WITHIN:
533 case SENSORSD_S_ABOVE:
536 case SENSORSD_S_BELOW:
540 r = snprintf(&buf[n], len - n, "%s",
547 switch(limit->astatus){
548 case SENSOR_S_UNSPEC:
563 r = snprintf(&buf[n], len - n, "%s",
568 r = snprintf(&buf[n], len - n, "%s",
569 print_sensor(limit->type,
573 r = snprintf(&buf[n], len - n, "%s",
574 print_sensor(limit->type,
578 r = snprintf(&buf[n], len - n, "%s",
579 print_sensor(limit->type,
583 r = snprintf(&buf[n], len - n, "%%%c",
587 if (r < 0 || (r >= len - n)) {
588 syslog(LOG_CRIT, "could not parse "
601 const char *drvstat[] = {
602 NULL, "empty", "ready", "powerup", "online", "idle", "active",
603 "rebuild", "powerdown", "fail", "pfail"
607 print_sensor(enum sensor_type type, int64_t value)
609 static char rfbuf[RFBUFCNT][RFBUFSIZ]; /* ring buffer */
619 snprintf(fbuf, RFBUFSIZ, "%.2f degC",
620 (value - 273150000) / 1000000.0);
623 snprintf(fbuf, RFBUFSIZ, "%lld RPM", value);
625 case SENSOR_VOLTS_DC:
626 snprintf(fbuf, RFBUFSIZ, "%.2f V DC", value / 1000000.0);
629 snprintf(fbuf, RFBUFSIZ, "%.2f A", value / 1000000.0);
631 case SENSOR_WATTHOUR:
632 snprintf(fbuf, RFBUFSIZ, "%.2f Wh", value / 1000000.0);
635 snprintf(fbuf, RFBUFSIZ, "%.2f Ah", value / 1000000.0);
637 case SENSOR_INDICATOR:
638 snprintf(fbuf, RFBUFSIZ, "%s", value? "On" : "Off");
641 snprintf(fbuf, RFBUFSIZ, "%lld", value);
644 snprintf(fbuf, RFBUFSIZ, "%.2f%%", value / 1000.0);
647 snprintf(fbuf, RFBUFSIZ, "%.2f lx", value / 1000000.0);
650 if (0 < value && value < sizeof(drvstat)/sizeof(drvstat[0]))
651 snprintf(fbuf, RFBUFSIZ, "%s", drvstat[value]);
653 snprintf(fbuf, RFBUFSIZ, "%lld ???", value);
655 case SENSOR_TIMEDELTA:
656 snprintf(fbuf, RFBUFSIZ, "%.6f secs", value / 1000000000.0);
659 snprintf(fbuf, RFBUFSIZ, "%lld ???", value);
666 parse_config(char *cf)
668 struct sdlim_t *sdlim;
670 TAILQ_FOREACH(sdlim, &sdlims, entries)
671 parse_config_sdlim(sdlim, cf);
675 parse_config_sdlim(struct sdlim_t *sdlim, char *cf)
678 char *buf = NULL, *ebuf = NULL;
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);
689 if (cgetent(&buf, cfa, node) != 0)
690 if (cgetent(&buf, cfa, sensor_type_s[p->type]) != 0)
692 if (cgetcap(buf, "istatus", ':'))
693 p->flags |= SENSORSD_L_ISTATUS;
694 if (cgetstr(buf, "low", &ebuf) < 0)
696 p->lower = get_val(ebuf, 0, p->type);
697 if (cgetstr(buf, "high", &ebuf) < 0)
699 p->upper = get_val(ebuf, 1, p->type);
700 if (cgetstr(buf, "command", &ebuf) < 0)
703 asprintf(&(p->command), "%s", ebuf);
706 if (p->lower != LLONG_MIN || p->upper != LLONG_MAX)
707 p->flags |= SENSORSD_L_USERLIMIT;
712 get_val(char *buf, int upper, enum sensor_type type)
725 val = strtod(buf, &p);
727 err(1, "incorrect value: %s", buf);
734 rval = val * 1000 * 1000 + 273150000;
738 rval = (val * 1000 * 1000 + 459670000) / 9 * 5;
741 errx(1, "unknown unit %s for temp sensor", p);
747 case SENSOR_VOLTS_DC:
749 errx(1, "unknown unit %s for voltage sensor", p);
750 rval = val * 1000 * 1000;
755 case SENSOR_INDICATOR:
761 case SENSOR_WATTHOUR:
764 rval = val * 1000 * 1000;
766 case SENSOR_TIMEDELTA:
767 rval = val * 1000 * 1000 * 1000;
770 errx(1, "unsupported sensor type");
779 reparse_cfg(int signo)