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
;
77 static void usage(void) __dead2
;
78 static void create(void);
79 static struct sdlim_t
*create_sdlim(struct sensordev
*);
80 static void destroy_sdlim(struct sdlim_t
*);
81 static void check(time_t);
82 static void check_sdlim(struct sdlim_t
*, time_t);
83 static void execute(char *);
84 static void report(time_t);
85 static void report_sdlim(struct sdlim_t
*, time_t);
86 static char *print_sensor(enum sensor_type
, int64_t);
87 static void parse_config(char *);
88 static void parse_config_sdlim(struct sdlim_t
*, char *);
89 static int64_t get_val(char *, int, enum sensor_type
);
90 static void reparse_cfg(int);
92 TAILQ_HEAD(sdlimhead_t
, sdlim_t
);
93 static struct sdlimhead_t sdlims
= TAILQ_HEAD_INITIALIZER(sdlims
);
95 static char *configfile
;
96 static 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
);
190 static struct sdlim_t
*
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 static 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
" Hz", value
);
644 snprintf(fbuf
, RFBUFSIZ
, "%"PRId64
, value
);
647 snprintf(fbuf
, RFBUFSIZ
, "%.2f%%", value
/ 1000.0);
650 snprintf(fbuf
, RFBUFSIZ
, "%.2f lx", value
/ 1000000.0);
653 if (0 < value
&& value
< (int64_t)NELEM(drvstat
))
654 snprintf(fbuf
, RFBUFSIZ
, "%s", drvstat
[value
]);
656 snprintf(fbuf
, RFBUFSIZ
, "%"PRId64
" ???", value
);
658 case SENSOR_TIMEDELTA
:
659 snprintf(fbuf
, RFBUFSIZ
, "%.6f secs", value
/ 1000000000.0);
662 snprintf(fbuf
, RFBUFSIZ
, "%"PRId64
" ???", value
);
669 parse_config(char *cf
)
671 struct sdlim_t
*sdlim
;
673 TAILQ_FOREACH(sdlim
, &sdlims
, entries
)
674 parse_config_sdlim(sdlim
, cf
);
678 parse_config_sdlim(struct sdlim_t
*sdlim
, char *cf
)
681 char *buf
= NULL
, *ebuf
= NULL
;
688 TAILQ_FOREACH(p
, &sdlim
->limits
, entries
) {
689 snprintf(node
, sizeof(node
), "hw.sensors.%s.%s%d",
690 sdlim
->dxname
, sensor_type_s
[p
->type
], p
->numt
);
692 if (cgetent(&buf
, cfa
, node
) != 0)
693 if (cgetent(&buf
, cfa
, sensor_type_s
[p
->type
]) != 0)
695 if (cgetcap(buf
, "istatus", ':'))
696 p
->flags
|= SENSORSD_L_ISTATUS
;
697 if (cgetstr(buf
, "low", &ebuf
) < 0)
699 p
->lower
= get_val(ebuf
, 0, p
->type
);
700 if (cgetstr(buf
, "high", &ebuf
) < 0)
702 p
->upper
= get_val(ebuf
, 1, p
->type
);
703 if (cgetstr(buf
, "command", &ebuf
) < 0)
706 asprintf(&(p
->command
), "%s", ebuf
);
709 if (p
->lower
!= LLONG_MIN
|| p
->upper
!= LLONG_MAX
)
710 p
->flags
|= SENSORSD_L_USERLIMIT
;
715 get_val(char *buf
, int upper
, enum sensor_type type
)
728 val
= strtod(buf
, &p
);
730 err(1, "incorrect value: %s", buf
);
737 rval
= val
* 1000 * 1000 + 273150000;
741 rval
= (val
* 1000 * 1000 + 459670000) / 9 * 5;
744 errx(1, "unknown unit %s for temp sensor", p
);
750 case SENSOR_VOLTS_DC
:
752 errx(1, "unknown unit %s for voltage sensor", p
);
753 rval
= val
* 1000 * 1000;
760 case SENSOR_INDICATOR
:
766 case SENSOR_WATTHOUR
:
769 rval
= val
* 1000 * 1000;
771 case SENSOR_TIMEDELTA
:
772 rval
= val
* 1000 * 1000 * 1000;
775 errx(1, "unsupported sensor type");
784 reparse_cfg(__unused
int signo
)