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
)