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
)