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 $ */
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/cdefs.h>
23 #include <sys/param.h>
24 #include <sys/sysctl.h>
25 #include <sys/sensors.h>
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 */
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 */
50 TAILQ_ENTRY(limits_t) entries;
51 enum sensor_type type; /* sensor type */
52 int numt; /* sensor number */
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 */
71 TAILQ_ENTRY(sdlim_t) entries;
72 char dxname[16]; /* device unix name */
73 int dev; /* device number */
75 TAILQ_HEAD(, limits_t) limits;
79 struct sdlim_t *create_sdlim(struct sensordev *);
81 void check_sdlim(struct sdlim_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);
91 TAILQ_HEAD(, sdlim_t) sdlims = TAILQ_HEAD_INITIALIZER(sdlims);
94 volatile sig_atomic_t reload = 0;
100 extern char *__progname;
101 fprintf(stderr, "usage: %s [-d]\n", __progname);
106 main(int argc, char *argv[])
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;
113 int sleeptime, sensor_cnt = 0, ch;
115 while ((ch = getopt(argc, argv, "d")) != -1) {
128 for (dev = 0; dev < MAXSENSORDEVICES; dev++) {
130 if (sysctl(mib, 3, &sensordev, &sdlen, NULL, 0) == -1) {
135 sdlim = create_sdlim(&sensordev);
136 TAILQ_INSERT_TAIL(&sdlims, sdlim, entries);
137 sensor_cnt += sdlim->sensor_cnt;
141 errx(1, "no sensors found");
143 openlog("sensorsd", LOG_PID | LOG_NDELAY, LOG_DAEMON);
145 if (configfile == NULL)
146 if (asprintf(&configfile, "/etc/sensorsd.conf") == -1)
147 err(1, "out of memory");
148 parse_config(configfile);
150 if (debug == 0 && daemon(0, 0) == -1)
151 err(1, "unable to fork");
153 signal(SIGHUP, reparse_cfg);
154 signal(SIGCHLD, SIG_IGN);
156 syslog(LOG_INFO, "startup, system has %d sensors", sensor_cnt);
158 next_check = next_report = time(NULL);
162 parse_config(configfile);
163 syslog(LOG_INFO, "configuration reloaded");
166 if (next_check <= time(NULL)) {
168 next_check = time(NULL) + CHECK_PERIOD;
170 if (next_report <= time(NULL)) {
172 last_report = next_report;
173 next_report = time(NULL) + REPORT_PERIOD;
175 if (next_report < next_check)
176 sleeptime = next_report - time(NULL);
178 sleeptime = next_check - time(NULL);
185 create_sdlim(struct sensordev *snsrdev)
187 struct sensor sensor;
188 struct sdlim_t *sdlim;
189 struct limits_t *limit;
190 size_t slen = sizeof(sensor);
192 enum sensor_type type;
194 if ((sdlim = calloc(1, sizeof(struct sdlim_t))) == NULL)
197 strlcpy(sdlim->dxname, snsrdev->xname, sizeof(sdlim->dxname));
201 mib[2] = sdlim->dev = snsrdev->num;
203 TAILQ_INIT(&sdlim->limits);
205 for (type = 0; type < SENSOR_MAX_TYPES; type++) {
207 for (numt = 0; numt < snsrdev->maxnumt[type]; numt++) {
209 if (sysctl(mib, 5, &sensor, &slen, NULL, 0) == -1) {
214 if ((limit = calloc(1, sizeof(struct limits_t))) ==
219 TAILQ_INSERT_TAIL(&sdlim->limits, limit, entries);
230 struct sdlim_t *sdlim;
232 TAILQ_FOREACH(sdlim, &sdlims, entries)
237 check_sdlim(struct sdlim_t *sdlim)
239 struct sensor sensor;
240 struct limits_t *limit;
247 len = sizeof(sensor);
249 TAILQ_FOREACH(limit, &sdlim->limits, entries) {
250 if ((limit->flags & SENSORSD_L_ISTATUS) &&
251 !(limit->flags & SENSORSD_L_USERLIMIT))
254 mib[3] = limit->type;
255 mib[4] = limit->numt;
256 if (sysctl(mib, 5, &sensor, &len, NULL, 0) == -1)
259 if (!(limit->flags & SENSORSD_L_ISTATUS)) {
260 enum sensor_status newastatus = sensor.status;
262 if (limit->astatus != newastatus) {
263 if (limit->astatus2 != newastatus) {
264 limit->astatus2 = newastatus;
266 } else if (++limit->acount >= 3) {
267 limit->last_val = sensor.value;
269 limit->astatus = newastatus;
270 limit->astatus_changed = time(NULL);
275 if (limit->flags & SENSORSD_L_USERLIMIT) {
276 enum sensorsd_s_status newustatus;
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;
284 newustatus = SENSORSD_S_WITHIN;
286 if (limit->ustatus != newustatus) {
287 if (limit->ustatus2 != newustatus) {
288 limit->ustatus2 = newustatus;
290 } else if (++limit->ucount >= 3) {
291 limit->last_val = sensor.value;
293 limit->ustatus = newustatus;
294 limit->ustatus_changed = time(NULL);
302 execute(char *command)
304 char *argp[] = {"sh", "-c", command, NULL};
308 syslog(LOG_CRIT, "execute: fork() failed");
311 execv("/bin/sh", argp);
320 report(time_t last_report)
322 struct sdlim_t *sdlim;
324 TAILQ_FOREACH(sdlim, &sdlims, entries)
325 report_sdlim(sdlim, last_report);
329 report_sdlim(struct sdlim_t *sdlim, time_t last_report)
331 struct limits_t *limit;
333 TAILQ_FOREACH(limit, &sdlim->limits, entries) {
334 if ((limit->astatus_changed <= last_report) &&
335 (limit->ustatus_changed <= last_report))
338 if (limit->astatus_changed > last_report) {
339 const char *as = NULL;
341 switch (limit->astatus) {
342 case SENSOR_S_UNSPEC:
354 case SENSOR_S_UNKNOWN:
358 syslog(LOG_ALERT, "%s.%s%d: %s%s",
359 sdlim->dxname, sensor_type_s[limit->type],
361 print_sensor(limit->type, limit->last_val), as);
364 if (limit->ustatus_changed > last_report) {
367 switch (limit->ustatus) {
368 case SENSORSD_S_UNSPEC:
369 snprintf(us, sizeof(us),
370 "ustatus uninitialised");
372 case SENSORSD_S_INVALID:
373 snprintf(us, sizeof(us), "marked invalid");
375 case SENSORSD_S_WITHIN:
376 snprintf(us, sizeof(us), "within limits: %s",
377 print_sensor(limit->type, limit->last_val));
379 case SENSORSD_S_OUTSIDE:
380 snprintf(us, sizeof(us), "exceeds limits: %s",
381 print_sensor(limit->type, limit->last_val));
384 syslog(LOG_ALERT, "%s.%s%d: %s",
385 sdlim->dxname, sensor_type_s[limit->type],
389 if (limit->command) {
391 char *cmd = limit->command;
393 int len = sizeof(buf);
396 for (i = n = 0; n < len; ++i) {
397 if (cmd[i] == '\0') {
402 buf[n++] = limit->command[i];
406 if (cmd[i] == '\0') {
413 r = snprintf(&buf[n], len - n, "%s",
417 r = snprintf(&buf[n], len - n, "%s",
418 sensor_type_s[limit->type]);
421 r = snprintf(&buf[n], len - n, "%d",
425 r = snprintf(&buf[n], len - n, "%s",
426 print_sensor(limit->type,
430 r = snprintf(&buf[n], len - n, "%s",
431 print_sensor(limit->type,
435 r = snprintf(&buf[n], len - n, "%s",
436 print_sensor(limit->type,
440 r = snprintf(&buf[n], len - n, "%%%c",
444 if (r < 0 || (r >= len - n)) {
445 syslog(LOG_CRIT, "could not parse "
458 const char *drvstat[] = {
459 NULL, "empty", "ready", "powerup", "online", "idle", "active",
460 "rebuild", "powerdown", "fail", "pfail"
464 print_sensor(enum sensor_type type, int64_t value)
466 static char rfbuf[RFBUFCNT][RFBUFSIZ]; /* ring buffer */
476 snprintf(fbuf, RFBUFSIZ, "%.2f degC",
477 (value - 273150000) / 1000000.0);
480 snprintf(fbuf, RFBUFSIZ, "%lld RPM", value);
482 case SENSOR_VOLTS_DC:
483 snprintf(fbuf, RFBUFSIZ, "%.2f V DC", value / 1000000.0);
486 snprintf(fbuf, RFBUFSIZ, "%.2f A", value / 1000000.0);
488 case SENSOR_WATTHOUR:
489 snprintf(fbuf, RFBUFSIZ, "%.2f Wh", value / 1000000.0);
492 snprintf(fbuf, RFBUFSIZ, "%.2f Ah", value / 1000000.0);
494 case SENSOR_INDICATOR:
495 snprintf(fbuf, RFBUFSIZ, "%s", value? "On" : "Off");
498 snprintf(fbuf, RFBUFSIZ, "%lld", value);
501 snprintf(fbuf, RFBUFSIZ, "%.2f%%", value / 1000.0);
504 snprintf(fbuf, RFBUFSIZ, "%.2f lx", value / 1000000.0);
507 if (0 < value && value < sizeof(drvstat)/sizeof(drvstat[0]))
508 snprintf(fbuf, RFBUFSIZ, "%s", drvstat[value]);
510 snprintf(fbuf, RFBUFSIZ, "%lld ???", value);
512 case SENSOR_TIMEDELTA:
513 snprintf(fbuf, RFBUFSIZ, "%.6f secs", value / 1000000000.0);
516 snprintf(fbuf, RFBUFSIZ, "%lld ???", value);
523 parse_config(char *cf)
525 struct sdlim_t *sdlim;
528 if ((cfa = calloc(2, sizeof(char *))) == NULL)
533 TAILQ_FOREACH(sdlim, &sdlims, entries)
534 parse_config_sdlim(sdlim, cfa);
539 parse_config_sdlim(struct sdlim_t *sdlim, char **cfa)
542 char *buf = NULL, *ebuf = NULL;
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);
549 if (cgetent(&buf, cfa, node) != 0)
550 if (cgetent(&buf, cfa, sensor_type_s[p->type]) != 0)
552 if (cgetcap(buf, "istatus", ':'))
553 p->flags |= SENSORSD_L_ISTATUS;
554 if (cgetstr(buf, "low", &ebuf) < 0)
556 p->lower = get_val(ebuf, 0, p->type);
557 if (cgetstr(buf, "high", &ebuf) < 0)
559 p->upper = get_val(ebuf, 1, p->type);
560 if (cgetstr(buf, "command", &ebuf) < 0)
563 asprintf(&(p->command), "%s", ebuf);
566 if (p->lower != LLONG_MIN || p->upper != LLONG_MAX)
567 p->flags |= SENSORSD_L_USERLIMIT;
572 get_val(char *buf, int upper, enum sensor_type type)
585 val = strtod(buf, &p);
587 err(1, "incorrect value: %s", buf);
594 rval = (val + 273.16) * 1000 * 1000;
598 rval = ((val - 32.0) / 9 * 5 + 273.16) * 1000 * 1000;
601 errx(1, "unknown unit %s for temp sensor", p);
607 case SENSOR_VOLTS_DC:
609 errx(1, "unknown unit %s for voltage sensor", p);
610 rval = val * 1000 * 1000;
615 case SENSOR_INDICATOR:
621 case SENSOR_WATTHOUR:
624 rval = val * 1000 * 1000;
626 case SENSOR_TIMEDELTA:
627 rval = val * 1000 * 1000 * 1000;
630 errx(1, "unsupported sensor type");
639 reparse_cfg(int signo)