Import OpenBSD's sysctl hardware sensors framework.

This commit includes the following core components:

 * sample configuration file for sensorsd
 * rc(8) script and glue code for sensorsd(8)
 * sysctl(3) doc fixes for CTL_HW tree
 * sysctl(3) documentation for hardware sensors
 * sysctl(8) documentation for hardware sensors
 * support for the sensor structure for sysctl(8)
 * rc.conf(5) documentation for starting sensorsd(8)
 * sensor_attach(9) et al documentation
 * /sys/kern/kern_sensors.c
   o sensor_attach(9) API for drivers to register ksensors
   o sensor_task_register(9) API for the update task
   o sysctl(3) glue code
   o hw.sensors shadow tree for sysctl(8) internal magic
 * <sys/sensors.h>
 * HW_SENSORS definition for <sys/sysctl.h>
 * sensors display for systat(1), including documentation
 * sensorsd(8) and all applicable documentation

The userland part of the framework is entirely source-code
compatible with OpenBSD 4.1, 4.2 and  -current as of today.

All sensor readings can be viewed with `sysctl hw.sensors`,
monitored in semi-realtime with `systat -sensors` and also
logged with `sensorsd`.

Submitted by:	Constantine A. Murenin <cnst@FreeBSD.org>
Sponsored by:	Google Summer of Code 2007 (GSoC2007/cnst-sensors)
Mentored by:	syrinx
Tested by:	many
OKed by:	kensmith
Obtained from:	OpenBSD (parts)
This commit is contained in:
Alexander Leidinger 2007-10-14 10:45:31 +00:00
parent ce0d9ca3db
commit 99f6b270e3
25 changed files with 2232 additions and 11 deletions

View File

@ -17,7 +17,7 @@ BIN1= amd.map apmd.conf auth.conf \
portsnap.conf pf.conf pf.os phones profile protocols \
rc rc.bsdextended rc.firewall rc.firewall6 rc.initdiskless \
rc.sendmail rc.shutdown \
rc.subr remote rpc services shells \
rc.subr remote rpc sensorsd.conf services shells \
snmpd.config sysctl.conf syslog.conf \
etc.${MACHINE_ARCH}/ttys \
${.CURDIR}/../gnu/usr.bin/man/manpath/manpath.config \

View File

@ -40,6 +40,8 @@ kldxref_clobber="NO" # Overwrite old linker.hints at boot.
kldxref_module_path="" # Override kern.module_path. A ';'-delimited list.
powerd_enable="NO" # Run powerd to lower our power usage.
powerd_flags="" # Flags to powerd (if enabled).
sensorsd_enable="NO" # Run sensorsd to monitor and log sensor state changes
sensorsd_flags="" # additional flags for sensorsd(8)
tmpmfs="AUTO" # Set to YES to always create an mfs /tmp, NO to never
tmpsize="20m" # Size of mfs /tmp if created
tmpmfs_flags="-S" # Extra mdmfs options for the mfs /tmp

View File

@ -33,7 +33,7 @@ FILES= DAEMON FILESYSTEMS LOGIN NETWORKING SERVERS \
random rarpd resolv root \
route6d routed routing rpcbind rtadvd rwho \
savecore sdpd securelevel sendmail \
serial sppp statd swap1 \
sensorsd serial sppp statd swap1 \
syscons sysctl syslogd \
timed tmp \
ugidfw \

17
etc/rc.d/sensorsd Normal file
View File

@ -0,0 +1,17 @@
#!/bin/sh
#
# $FreeBSD$
#
# PROVIDE: sensorsd
# REQUIRE: syslogd
#
. /etc/rc.subr
name="sensorsd"
rcvar=`set_rcvar`
command="/usr/sbin/${name}"
load_rc_config $name
run_rc_command "$1"

50
etc/sensorsd.conf Normal file
View File

@ -0,0 +1,50 @@
# $FreeBSD$
# $OpenBSD: sensorsd.conf,v 1.8 2007/08/14 19:02:02 cnst Exp $
#
# Sample sensorsd.conf file. See sensorsd.conf(5) for details.
#
# +5 voltage (volts)
#hw.sensors.lm0.volt3:low=4.8V:high=5.2V
# +12 voltage (volts)
#hw.sensors.lm0.volt4:low=11.5V:high=12.5V
# Chipset temperature (degrees Celsius)
#hw.sensors.lm0.temp0:high=50C
# CPU temperature (degrees Celsius)
#hw.sensors.lm0.temp1:high=60C
# CPU fan (RPM)
#hw.sensors.lm0.fan1:low=3000
# ignore certain indicators on ipmi(4)
#hw.sensors.ipmi0.indicator1:istatus
# Warn if any temperature sensor is over 70 degC.
# This entry will match only those temperature sensors
# that don't have their own entry.
#temp:high=70C
# By default, sensorsd(8) reports status changes of all sensors that
# keep their state. Uncomment the following lines if you want to
# suppress reports about status changes of specific sensor types.
#temp:istatus
#fan:istatus
#volt:istatus
#acvolt:istatus
#resistance:istatus
#power:istatus
#current:istatus
#watthour:istatus
#amphour:istatus
#indicator:istatus
#raw:istatus
#percentage:istatus
#illuminance:istatus
#drive:istatus
#timedelta:istatus

View File

@ -280,10 +280,10 @@ privilege may change the value.
.It "HW_PHYSMEM integer no"
.It "HW_USERMEM integer no"
.It "HW_PAGESIZE integer no"
.It "HW_FLOATINGPOINT integer no"
.It "HW_FLOATINGPT integer no"
.It "HW_MACHINE_ARCH string no"
.\".It "HW_DISKNAMES integer no"
.\".It "HW_DISKSTATS integer no"
.It "HW_REALMEM integer no"
.It "HW_SENSORS node not applicable"
.El
.Pp
.Bl -tag -width 6n
@ -301,12 +301,40 @@ The bytes of physical memory.
The bytes of non-kernel memory.
.It Li HW_PAGESIZE
The software page size.
.It Li HW_FLOATINGPOINT
.It Li HW_FLOATINGPT
Nonzero if the floating point support is in hardware.
.It Li HW_MACHINE_ARCH
The machine dependent architecture type.
.\".It Fa HW_DISKNAMES
.\".It Fa HW_DISKSTATS
.It Li HW_REALMEM
The bytes of real memory.
.It Li HW_SENSORS
Third level comprises an array of
.Li struct sensordev
structures containing information about devices
that may attach hardware monitoring sensors.
.Pp
Third, fourth and fifth levels together comprise an array of
.Li struct sensor
structures containing snapshot readings of hardware monitoring sensors.
In such usage, third level indicates the numerical representation
of the sensor device name to which the sensor is attached
(device's xname and number shall be matched with the help of
.Li struct sensordev
structure above),
fourth level indicates sensor type and
fifth level is an ordinal sensor number (unique to
the specified sensor type on the specified sensor device).
.Pp
The
.Sy sensordev
and
.Sy sensor
structures
and
.Sy sensor_type
enumeration
are defined in
.In sys/sensors.h .
.El
.Ss CTL_KERN
The string and integer information available for the CTL_KERN level

View File

@ -215,6 +215,7 @@ denote
.It "hw.floatingpoint integer no
.It "hw.machine_arch string no
.It "hw.realmem integer no
.It "hw.sensors.<xname>.<type><numt> struct no
.It "machdep.console_device dev_t no
.It "machdep.adjkerntz integer yes
.It "machdep.disable_rtc_set integer yes

View File

@ -44,6 +44,7 @@ static const char rcsid[] =
#include <sys/param.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/sensors.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
#include <sys/vmmeter.h>
@ -407,6 +408,143 @@ S_vmtotal(int l2, void *p)
return (0);
}
static int
S_sensor(int l2, void *p)
{
struct sensor *s = (struct sensor *)p;
if (l2 != sizeof(*s)) {
warnx("S_sensor %d != %d", l2, sizeof(*s));
return (1);
}
if (s->flags & SENSOR_FINVALID) {
/*
* XXX: with this flag, the node should be entirely ignored,
* but as the magic-based sysctl(8) is not too flexible, we
* simply have to print out that the sensor is invalid.
*/
printf("invalid");
return (0);
}
if (s->flags & SENSOR_FUNKNOWN)
printf("unknown");
else {
switch (s->type) {
case SENSOR_TEMP:
printf("%.2f degC",
(s->value - 273150000) / 1000000.0);
break;
case SENSOR_FANRPM:
printf("%lld RPM", s->value);
break;
case SENSOR_VOLTS_DC:
printf("%.2f VDC", s->value / 1000000.0);
break;
case SENSOR_AMPS:
printf("%.2f A", s->value / 1000000.0);
break;
case SENSOR_WATTHOUR:
printf("%.2f Wh", s->value / 1000000.0);
break;
case SENSOR_AMPHOUR:
printf("%.2f Ah", s->value / 1000000.0);
break;
case SENSOR_INDICATOR:
printf("%s", s->value ? "On" : "Off");
break;
case SENSOR_INTEGER:
printf("%lld", s->value);
break;
case SENSOR_PERCENT:
printf("%.2f%%", s->value / 1000.0);
break;
case SENSOR_LUX:
printf("%.2f lx", s->value / 1000000.0);
break;
case SENSOR_DRIVE:
{
const char *name;
switch (s->value) {
case SENSOR_DRIVE_EMPTY:
name = "empty";
break;
case SENSOR_DRIVE_READY:
name = "ready";
break;
case SENSOR_DRIVE_POWERUP:
name = "powering up";
break;
case SENSOR_DRIVE_ONLINE:
name = "online";
break;
case SENSOR_DRIVE_IDLE:
name = "idle";
break;
case SENSOR_DRIVE_ACTIVE:
name = "active";
break;
case SENSOR_DRIVE_REBUILD:
name = "rebuilding";
break;
case SENSOR_DRIVE_POWERDOWN:
name = "powering down";
break;
case SENSOR_DRIVE_FAIL:
name = "failed";
break;
case SENSOR_DRIVE_PFAIL:
name = "degraded";
break;
default:
name = "unknown";
break;
}
printf(name);
break;
}
case SENSOR_TIMEDELTA:
printf("%.6f secs", s->value / 1000000000.0);
break;
default:
printf("unknown");
}
}
if (s->desc[0] != '\0')
printf(" (%s)", s->desc);
switch (s->status) {
case SENSOR_S_UNSPEC:
break;
case SENSOR_S_OK:
printf(", OK");
break;
case SENSOR_S_WARN:
printf(", WARNING");
break;
case SENSOR_S_CRIT:
printf(", CRITICAL");
break;
case SENSOR_S_UNKNOWN:
printf(", UNKNOWN");
break;
}
if (s->tv.tv_sec) {
time_t t = s->tv.tv_sec;
char ct[26];
ctime_r(&t, ct);
ct[19] = '\0';
printf(", %s.%03ld", ct, s->tv.tv_usec / 1000);
}
return (0);
}
static int
T_dev_t(int l2, void *p)
{
@ -678,6 +816,8 @@ show_var(int *oid, int nlen)
func = S_loadavg;
else if (strcmp(fmt, "S,vmtotal") == 0)
func = S_vmtotal;
else if (strcmp(fmt, "S,sensor") == 0)
func = S_sensor;
else if (strcmp(fmt, "T,dev_t") == 0)
func = T_dev_t;
else

View File

@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd September 3, 2007
.Dd September 13, 2007
.Dt RC.CONF 5
.Os
.Sh NAME
@ -222,6 +222,22 @@ is set to
these are the flags to pass to the
.Xr powerd 8
daemon.
.It Va sensorsd_enable
.Pq Vt bool
Set to
.Dq Li NO
by default.
Setting this to
.Dq Li YES
enables
.Xr sensorsd 8 ,
a sensors monitoring and logging daemon.
.It Va sensorsd_flags
.Pq Vt str
Empty by default.
This variable contains additional flags passed to the
.Xr sensorsd 8
program.
.It Va tmpmfs
Controls the creation of a
.Pa /tmp
@ -3886,6 +3902,7 @@ enable support for sound mixer.
.Xr rpc.statd 8 ,
.Xr rwhod 8 ,
.Xr savecore 8 ,
.Xr sensorsd 8 ,
.Xr sshd 8 ,
.Xr swapon 8 ,
.Xr sysctl 8 ,

View File

@ -209,6 +209,7 @@ MAN= accept_filter.9 \
securelevel_gt.9 \
selrecord.9 \
sema.9 \
sensor_attach.9 \
sf_buf.9 \
signal.9 \
sleep.9 \
@ -975,6 +976,13 @@ MLINKS+=sema.9 sema_destroy.9 \
sema.9 sema_trywait.9 \
sema.9 sema_value.9 \
sema.9 sema_wait.9
MLINKS+=sensor_attach.9 sensordev_install.9 \
sensor_attach.9 sensordev_deinstall.9 \
sensor_attach.9 sensor_detach.9 \
sensor_attach.9 ksensordev.9 \
sensor_attach.9 ksensor.9 \
sensor_attach.9 sensor_task_register.9 \
sensor_attach.9 sensor_task_unregister.9
MLINKS+=sf_buf.9 sf_buf_alloc.9 \
sf_buf.9 sf_buf_free.9 \
sf_buf.9 sf_buf_kva.9 \

View File

@ -0,0 +1,155 @@
.\" $FreeBSD$
.\" $OpenBSD: sensor_attach.9,v 1.4 2007/03/22 16:55:31 deraadt Exp $
.\"
.\" Copyright (c) 2006 Michael Knudsen <mk@openbsd.org>
.\" Copyright (c) 2006 Constantine A. Murenin <cnst+openbsd@bugmail.mojo.ru>
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\"
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. The name of the author may not be used to endorse or promote products
.\" derived from this software without specific prior written permission.
.\"
.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
.\" INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
.\" AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
.\" THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
.\" EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
.\" PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
.\" OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
.\" WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
.\" OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
.\" ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.Dd 19 August 2007
.Dt SENSOR_ATTACH 9
.Os
.Sh NAME
.Nm sensor_attach ,
.Nm sensor_detach ,
.Nm sensordev_install ,
.Nm sensordev_deinstall ,
.Nm sensor_task_register ,
.Nm sensor_task_unregister
.Nd sensors framework
.Sh SYNOPSIS
.In sys/sensors.h
.Ft void
.Fn "sensordev_install" "struct ksensordev *sensdev"
.Ft void
.Fn "sensordev_deinstall" "struct ksensordev *sensdev"
.Pp
.Ft void
.Fn "sensor_attach" "struct ksensordev *sensdev" "struct ksensor *sens"
.Ft void
.Fn "sensor_detach" "struct ksensordev *sensdev" "struct ksensor *sens"
.Pp
.Ft int
.Fn "sensor_task_register" "void *arg" "void (*func)(void *)" "int period"
.Ft void
.Fn "sensor_task_unregister" "void *arg"
.Sh DESCRIPTION
The
sensors
framework API provides a mechanism for manipulation of hardware sensors
that are available under the
.Va hw.sensors
.Xr sysctl 8
tree.
.Pp
.Fn sensor_attach
adds the sensor specified by the
.Pa sens
argument to the sensor device specified by the
.Pa sensdev
argument.
.Fn sensor_detach
can be used to remove sensors previously added by
.Fn sensor_attach .
.Pp
.Fn sensordev_install
registers the sensor device specified by the
.Pa sensdev
argument so that all sensors that are attached to the device become
accessible via the sysctl interface.
.Fn sensordev_deinstall
can be used to remove sensor devices previously registered by
.Fn sensordev_install .
.Pp
Drivers are responsible for retrieving, interpreting and normalising
sensor values and updating the sensor struct periodically.
If the driver needs process context, for example to sleep, it can
register a task with the sensor framework.
.Pp
.Fn sensor_task_register
is used to register a periodic task to update sensors.
The
.Fa func
argument is a pointer to the function to run with an interval of
.Fa period
seconds.
The
.Fa arg
parameter is the argument given to the
.Fa func
function.
The
.Fn sensor_task_unregister
removes all tasks previously registered with
.Fn sensor_task_register
with an argument of
.Fa arg .
.Sh COMPATIBILITY
.Ss sensor_task
The
.Fn sensor_task_register
and
.Fn sensor_task_unregister
functions that are included in
.Ox 4.2
and later
are not compatible with
.Fx .
.Fx
includes an implementation that is similar and compatible
with an earlier version of
these
.Va sensor_task
functions that was available from
.Ox 3.9
until
.Ox 4.1 .
.Pp
Drivers that only call
.Fn sensor_task_register
and don't check its return value are not affected by this
.Va sensor_task
compatibility notice.
.Sh SEE ALSO
.Xr systat 1 ,
.Xr sysctl 3 ,
.Xr sensorsd 8 ,
.Xr sysctl 8
.Sh HISTORY
The sensor framework was written by
.An Alexander Yurchenko Aq grange@openbsd.org
and first appeared in
.Ox 3.4 .
.An David Gwynne Aq dlg@openbsd.org
later extended it for
.Ox 3.8 .
.An Constantine A. Murenin Aq cnst+openbsd@bugmail.mojo.ru
extended it even further by introducing the concept of sensor devices in
.Ox 4.1 .
.Pp
The framework was ported to
.Fx
by
.An Constantine A. Murenin Aq cnst@FreeBSD.org
as a Google Summer of Code 2007 project,
and first appeared in
.Fx 7.XXX.

View File

@ -1446,6 +1446,7 @@ kern/kern_prot.c standard
kern/kern_resource.c standard
kern/kern_rwlock.c standard
kern/kern_sema.c standard
kern/kern_sensors.c standard
kern/kern_shutdown.c standard
kern/kern_sig.c standard
kern/kern_subr.c standard

421
sys/kern/kern_sensors.c Normal file
View File

@ -0,0 +1,421 @@
/* $FreeBSD$ */
/* $OpenBSD: kern_sensors.c,v 1.19 2007/06/04 18:42:05 deraadt Exp $ */
/* $OpenBSD: kern_sysctl.c,v 1.154 2007/06/01 17:29:10 beck Exp $ */
/*-
* Copyright (c) 2005 David Gwynne <dlg@openbsd.org>
* Copyright (c) 2006 Constantine A. Murenin <cnst+openbsd@bugmail.mojo.ru>
* Copyright (c) 2007 Constantine A. Murenin <cnst+GSoC2007@FreeBSD.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/kthread.h>
#include <sys/queue.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/sysctl.h>
#include <sys/sensors.h>
int sensordev_count = 0;
SLIST_HEAD(, ksensordev) sensordev_list = SLIST_HEAD_INITIALIZER(sensordev_list);
struct ksensordev *sensordev_get(int);
struct ksensor *sensor_find(struct ksensordev *, enum sensor_type, int);
struct sensor_task {
void *arg;
void (*func)(void *);
int period;
time_t nextrun;
volatile int running;
TAILQ_ENTRY(sensor_task) entry;
};
void sensor_task_thread(void *);
void sensor_task_schedule(struct sensor_task *);
TAILQ_HEAD(, sensor_task) tasklist = TAILQ_HEAD_INITIALIZER(tasklist);
#ifndef NOSYSCTL8HACK
void sensor_sysctl8magic_install(struct ksensordev *);
void sensor_sysctl8magic_deinstall(struct ksensordev *);
#endif
void
sensordev_install(struct ksensordev *sensdev)
{
struct ksensordev *v, *nv;
mtx_lock(&Giant);
if (sensordev_count == 0) {
sensdev->num = 0;
SLIST_INSERT_HEAD(&sensordev_list, sensdev, list);
} else {
for (v = SLIST_FIRST(&sensordev_list);
(nv = SLIST_NEXT(v, list)) != NULL; v = nv)
if (nv->num - v->num > 1)
break;
sensdev->num = v->num + 1;
SLIST_INSERT_AFTER(v, sensdev, list);
}
sensordev_count++;
mtx_unlock(&Giant);
#ifndef NOSYSCTL8HACK
sensor_sysctl8magic_install(sensdev);
#endif
}
void
sensor_attach(struct ksensordev *sensdev, struct ksensor *sens)
{
struct ksensor *v, *nv;
struct ksensors_head *sh;
int i;
mtx_lock(&Giant);
sh = &sensdev->sensors_list;
if (sensdev->sensors_count == 0) {
for (i = 0; i < SENSOR_MAX_TYPES; i++)
sensdev->maxnumt[i] = 0;
sens->numt = 0;
SLIST_INSERT_HEAD(sh, sens, list);
} else {
for (v = SLIST_FIRST(sh);
(nv = SLIST_NEXT(v, list)) != NULL; v = nv)
if (v->type == sens->type && (v->type != nv->type ||
(v->type == nv->type && nv->numt - v->numt > 1)))
break;
/* sensors of the same type go after each other */
if (v->type == sens->type)
sens->numt = v->numt + 1;
else
sens->numt = 0;
SLIST_INSERT_AFTER(v, sens, list);
}
/* we only increment maxnumt[] if the sensor was added
* to the last position of sensors of this type
*/
if (sensdev->maxnumt[sens->type] == sens->numt)
sensdev->maxnumt[sens->type]++;
sensdev->sensors_count++;
mtx_unlock(&Giant);
}
void
sensordev_deinstall(struct ksensordev *sensdev)
{
mtx_lock(&Giant);
sensordev_count--;
SLIST_REMOVE(&sensordev_list, sensdev, ksensordev, list);
mtx_unlock(&Giant);
#ifndef NOSYSCTL8HACK
sensor_sysctl8magic_deinstall(sensdev);
#endif
}
void
sensor_detach(struct ksensordev *sensdev, struct ksensor *sens)
{
struct ksensors_head *sh;
mtx_lock(&Giant);
sh = &sensdev->sensors_list;
sensdev->sensors_count--;
SLIST_REMOVE(sh, sens, ksensor, list);
/* we only decrement maxnumt[] if this is the tail
* sensor of this type
*/
if (sens->numt == sensdev->maxnumt[sens->type] - 1)
sensdev->maxnumt[sens->type]--;
mtx_unlock(&Giant);
}
struct ksensordev *
sensordev_get(int num)
{
struct ksensordev *sd;
SLIST_FOREACH(sd, &sensordev_list, list)
if (sd->num == num)
return (sd);
return (NULL);
}
struct ksensor *
sensor_find(struct ksensordev *sensdev, enum sensor_type type, int numt)
{
struct ksensor *s;
struct ksensors_head *sh;
sh = &sensdev->sensors_list;
SLIST_FOREACH(s, sh, list)
if (s->type == type && s->numt == numt)
return (s);
return (NULL);
}
int
sensor_task_register(void *arg, void (*func)(void *), int period)
{
struct sensor_task *st;
int create_thread = 0;
st = malloc(sizeof(struct sensor_task), M_DEVBUF, M_NOWAIT);
if (st == NULL)
return (1);
st->arg = arg;
st->func = func;
st->period = period;
st->running = 1;
if (TAILQ_EMPTY(&tasklist))
create_thread = 1;
st->nextrun = 0;
TAILQ_INSERT_HEAD(&tasklist, st, entry);
if (create_thread)
if (kthread_create(sensor_task_thread, NULL, NULL, 0, 0,
"sensors") != 0)
panic("sensors kthread");
wakeup(&tasklist);
return (0);
}
void
sensor_task_unregister(void *arg)
{
struct sensor_task *st;
TAILQ_FOREACH(st, &tasklist, entry)
if (st->arg == arg)
st->running = 0;
}
void
sensor_task_thread(void *arg)
{
struct sensor_task *st, *nst;
time_t now;
while (!TAILQ_EMPTY(&tasklist)) {
while ((nst = TAILQ_FIRST(&tasklist))->nextrun >
(now = time_uptime))
tsleep(&tasklist, PWAIT, "timeout",
(nst->nextrun - now) * hz);
while ((st = nst) != NULL) {
nst = TAILQ_NEXT(st, entry);
if (st->nextrun > now)
break;
/* take it out while we work on it */
TAILQ_REMOVE(&tasklist, st, entry);
if (!st->running) {
free(st, M_DEVBUF);
continue;
}
/* run the task */
st->func(st->arg);
/* stick it back in the tasklist */
sensor_task_schedule(st);
}
}
kthread_exit(0);
}
void
sensor_task_schedule(struct sensor_task *st)
{
struct sensor_task *cst;
st->nextrun = time_uptime + st->period;
TAILQ_FOREACH(cst, &tasklist, entry) {
if (cst->nextrun > st->nextrun) {
TAILQ_INSERT_BEFORE(cst, st, entry);
return;
}
}
/* must be an empty list, or at the end of the list */
TAILQ_INSERT_TAIL(&tasklist, st, entry);
}
/*
* sysctl glue code
*/
int sysctl_handle_sensordev(SYSCTL_HANDLER_ARGS);
int sysctl_handle_sensor(SYSCTL_HANDLER_ARGS);
int sysctl_sensors_handler(SYSCTL_HANDLER_ARGS);
#ifndef NOSYSCTL8HACK
SYSCTL_NODE(_hw, OID_AUTO, sensors, CTLFLAG_RD, NULL,
"Hardware Sensors sysctl internal magic");
SYSCTL_NODE(_hw, HW_SENSORS, _sensors, CTLFLAG_RD, sysctl_sensors_handler,
"Hardware Sensors XP MIB interface");
#else /* NOSYSCTL8HACK */
SYSCTL_NODE(_hw, HW_SENSORS, sensors, CTLFLAG_RD, sysctl_sensors_handler,
"Hardware Sensors");
#endif /* !NOSYSCTL8HACK */
#ifndef NOSYSCTL8HACK
/*
* XXX:
* FreeBSD's sysctl(9) .oid_handler functionality is not accustomed
* for the CTLTYPE_NODE handler to handle the undocumented sysctl
* magic calls. As soon as such functionality is developed,
* sysctl_sensors_handler() should be converted to handle all such
* calls, and these sysctl_add_oid(9) calls should be removed
* "with a big axe". This whole sysctl_add_oid(9) business is solely
* to please sysctl(8).
*/
void
sensor_sysctl8magic_install(struct ksensordev *sensdev)
{
struct sysctl_oid_list *ol;
struct sysctl_ctx_list *cl = &sensdev->clist;
struct ksensor *s;
struct ksensors_head *sh = &sensdev->sensors_list;
sysctl_ctx_init(cl);
ol = SYSCTL_CHILDREN(SYSCTL_ADD_NODE(cl, &SYSCTL_NODE_CHILDREN(_hw,
sensors), sensdev->num, sensdev->xname, CTLFLAG_RD, NULL, ""));
SLIST_FOREACH(s, sh, list) {
char n[32];
snprintf(n, sizeof(n), "%s%d", sensor_type_s[s->type], s->numt);
SYSCTL_ADD_PROC(cl, ol, OID_AUTO, n, CTLTYPE_STRUCT |
CTLFLAG_RD, s, 0, sysctl_handle_sensor, "S,sensor", "");
}
}
void
sensor_sysctl8magic_deinstall(struct ksensordev *sensdev)
{
struct sysctl_ctx_list *cl = &sensdev->clist;
sysctl_ctx_free(cl);
}
#endif /* !NOSYSCTL8HACK */
int
sysctl_handle_sensordev(SYSCTL_HANDLER_ARGS)
{
struct ksensordev *ksd = arg1;
struct sensordev *usd;
int error;
if (req->newptr)
return (EPERM);
/* Grab a copy, to clear the kernel pointers */
usd = malloc(sizeof(*usd), M_TEMP, M_WAITOK);
bzero(usd, sizeof(*usd));
usd->num = ksd->num;
strlcpy(usd->xname, ksd->xname, sizeof(usd->xname));
memcpy(usd->maxnumt, ksd->maxnumt, sizeof(usd->maxnumt));
usd->sensors_count = ksd->sensors_count;
error = SYSCTL_OUT(req, usd, sizeof(struct sensordev));
free(usd, M_TEMP);
return (error);
}
int
sysctl_handle_sensor(SYSCTL_HANDLER_ARGS)
{
struct ksensor *ks = arg1;
struct sensor *us;
int error;
if (req->newptr)
return (EPERM);
/* Grab a copy, to clear the kernel pointers */
us = malloc(sizeof(*us), M_TEMP, M_WAITOK);
bzero(us, sizeof(*us));
memcpy(us->desc, ks->desc, sizeof(ks->desc));
us->tv = ks->tv;
us->value = ks->value;
us->type = ks->type;
us->status = ks->status;
us->numt = ks->numt;
us->flags = ks->flags;
error = SYSCTL_OUT(req, us, sizeof(struct sensor));
free(us, M_TEMP);
return (error);
}
int
sysctl_sensors_handler(SYSCTL_HANDLER_ARGS)
{
int *name = arg1;
u_int namelen = arg2;
struct ksensordev *ksd;
struct ksensor *ks;
int dev, numt;
enum sensor_type type;
if (namelen != 1 && namelen != 3)
return (ENOTDIR);
dev = name[0];
if ((ksd = sensordev_get(dev)) == NULL)
return (ENOENT);
if (namelen == 1)
return (sysctl_handle_sensordev(NULL, ksd, 0, req));
type = name[1];
numt = name[2];
if ((ks = sensor_find(ksd, type, numt)) == NULL)
return (ENOENT);
return (sysctl_handle_sensor(NULL, ks, 0, req));
}

168
sys/sys/sensors.h Normal file
View File

@ -0,0 +1,168 @@
/* $FreeBSD$ */
/* $OpenBSD: sensors.h,v 1.23 2007/03/22 16:55:31 deraadt Exp $ */
/*-
* Copyright (c) 2003, 2004 Alexander Yurchenko <grange@openbsd.org>
* Copyright (c) 2006 Constantine A. Murenin <cnst+openbsd@bugmail.mojo.ru>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _SYS_SENSORS_H_
#define _SYS_SENSORS_H_
/* Sensor types */
enum sensor_type {
SENSOR_TEMP, /* temperature (muK) */
SENSOR_FANRPM, /* fan revolution speed */
SENSOR_VOLTS_DC, /* voltage (muV DC) */
SENSOR_VOLTS_AC, /* voltage (muV AC) */
SENSOR_OHMS, /* resistance */
SENSOR_WATTS, /* power */
SENSOR_AMPS, /* current (muA) */
SENSOR_WATTHOUR, /* power capacity */
SENSOR_AMPHOUR, /* power capacity */
SENSOR_INDICATOR, /* boolean indicator */
SENSOR_INTEGER, /* generic integer value */
SENSOR_PERCENT, /* percent */
SENSOR_LUX, /* illuminance (mulx) */
SENSOR_DRIVE, /* disk */
SENSOR_TIMEDELTA, /* system time error (nSec) */
SENSOR_MAX_TYPES
};
static const char * const sensor_type_s[SENSOR_MAX_TYPES + 1] = {
"temp",
"fan",
"volt",
"acvolt",
"resistance",
"power",
"current",
"watthour",
"amphour",
"indicator",
"raw",
"percent",
"illuminance",
"drive",
"timedelta",
"undefined"
};
#define SENSOR_DRIVE_EMPTY 1
#define SENSOR_DRIVE_READY 2
#define SENSOR_DRIVE_POWERUP 3
#define SENSOR_DRIVE_ONLINE 4
#define SENSOR_DRIVE_IDLE 5
#define SENSOR_DRIVE_ACTIVE 6
#define SENSOR_DRIVE_REBUILD 7
#define SENSOR_DRIVE_POWERDOWN 8
#define SENSOR_DRIVE_FAIL 9
#define SENSOR_DRIVE_PFAIL 10
/* Sensor states */
enum sensor_status {
SENSOR_S_UNSPEC, /* status is unspecified */
SENSOR_S_OK, /* status is ok */
SENSOR_S_WARN, /* status is warning */
SENSOR_S_CRIT, /* status is critical */
SENSOR_S_UNKNOWN /* status is unknown */
};
/* Sensor data:
* New fields should be added at the end to encourage backwards compat
*/
struct sensor {
char desc[32]; /* sensor description, may be empty */
struct timeval tv; /* sensor value last change time */
int64_t value; /* current value */
enum sensor_type type; /* sensor type */
enum sensor_status status; /* sensor status */
int numt; /* sensor number of .type type */
int flags; /* sensor flags */
#define SENSOR_FINVALID 0x0001 /* sensor is invalid */
#define SENSOR_FUNKNOWN 0x0002 /* sensor value is unknown */
};
/* Sensor device data:
* New fields should be added at the end to encourage backwards compat
*/
struct sensordev {
int num; /* sensordev number */
char xname[16]; /* unix device name */
int maxnumt[SENSOR_MAX_TYPES];
int sensors_count;
};
#define MAXSENSORDEVICES 32
#ifdef _KERNEL
#include <sys/queue.h>
#ifndef NOSYSCTL8HACK
#include <sys/sysctl.h>
#endif
/* Sensor data */
struct ksensor {
SLIST_ENTRY(ksensor) list; /* device-scope list */
char desc[32]; /* sensor description, may be empty */
struct timeval tv; /* sensor value last change time */
int64_t value; /* current value */
enum sensor_type type; /* sensor type */
enum sensor_status status; /* sensor status */
int numt; /* sensor number of .type type */
int flags; /* sensor flags, ie. SENSOR_FINVALID */
};
SLIST_HEAD(ksensors_head, ksensor);
/* Sensor device data */
struct ksensordev {
SLIST_ENTRY(ksensordev) list;
int num; /* sensordev number */
char xname[16]; /* unix device name */
int maxnumt[SENSOR_MAX_TYPES];
int sensors_count;
struct ksensors_head sensors_list;
#ifndef NOSYSCTL8HACK
struct sysctl_ctx_list clist; /* XXX: sysctl(9) .oid_handler() for
* CTLTYPE_NODE type doesn't support
* the undocumented sysctl magic.
*/
#endif /* !NOSYSCTL8HACK */
};
/* struct ksensordev */
void sensordev_install(struct ksensordev *);
void sensordev_deinstall(struct ksensordev *);
/* struct ksensor */
void sensor_attach(struct ksensordev *, struct ksensor *);
void sensor_detach(struct ksensordev *, struct ksensor *);
/* task scheduling */
int sensor_task_register(void *, void (*)(void *), int);
void sensor_task_unregister(void *);
#endif /* _KERNEL */
#endif /* !_SYS_SENSORS_H_ */

View File

@ -487,7 +487,8 @@ TAILQ_HEAD(sysctl_ctx_list, sysctl_ctx_entry);
#define HW_FLOATINGPT 10 /* int: has HW floating point? */
#define HW_MACHINE_ARCH 11 /* string: machine architecture */
#define HW_REALMEM 12 /* int: 'real' memory */
#define HW_MAXID 13 /* number of valid hw ids */
#define HW_SENSORS 13 /* node: hardware monitors */
#define HW_MAXID 14 /* number of valid hw ids */
#define CTL_HW_NAMES { \
{ 0, 0 }, \
@ -501,7 +502,9 @@ TAILQ_HEAD(sysctl_ctx_list, sysctl_ctx_entry);
{ "disknames", CTLTYPE_STRUCT }, \
{ "diskstats", CTLTYPE_STRUCT }, \
{ "floatingpoint", CTLTYPE_INT }, \
{ "machine_arch", CTLTYPE_STRING }, \
{ "realmem", CTLTYPE_ULONG }, \
{ "sensors", CTLTYPE_NODE}, \
}
/*

View File

@ -5,7 +5,7 @@
PROG= systat
SRCS= cmds.c cmdtab.c devs.c fetch.c iostat.c keyboard.c main.c \
mbufs.c netcmds.c netstat.c pigs.c swap.c icmp.c \
mbufs.c netcmds.c netstat.c pigs.c sensors.c swap.c icmp.c \
mode.c ip.c tcp.c \
vmstat.c convtbl.c ifcmds.c ifstat.c

View File

@ -82,6 +82,9 @@ struct cmdtab cmdtab[] = {
{ "ifstat", showifstat, fetchifstat, labelifstat,
initifstat, openifstat, closeifstat, cmdifstat,
0, CF_LOADAV },
{ "sensors", showsensors, fetchsensors, labelsensors,
initsensors, opensensors, closesensors, 0,
0, CF_LOADAV },
{ NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0 }
};
struct cmdtab *curcmd = &cmdtab[0];

View File

@ -82,6 +82,7 @@ void closekre(WINDOW *);
void closembufs(WINDOW *);
void closenetstat(WINDOW *);
void closepigs(WINDOW *);
void closesensors(WINDOW *);
void closeswap(WINDOW *);
void closetcp(WINDOW *);
int cmdifstat(const char *, const char *);
@ -105,6 +106,7 @@ void fetchkre(void);
void fetchmbufs(void);
void fetchnetstat(void);
void fetchpigs(void);
void fetchsensors(void);
void fetchswap(void);
void fetchtcp(void);
void getsysctl(const char *, void *, size_t);
@ -119,6 +121,7 @@ int initkre(void);
int initmbufs(void);
int initnetstat(void);
int initpigs(void);
int initsensors(void);
int initswap(void);
int inittcp(void);
int keyboard(void);
@ -134,6 +137,7 @@ void labelmbufs(void);
void labelnetstat(void);
void labelpigs(void);
void labels(void);
void labelsensors(void);
void labelswap(void);
void labeltcp(void);
void load(void);
@ -149,6 +153,7 @@ WINDOW *openkre(void);
WINDOW *openmbufs(void);
WINDOW *opennetstat(void);
WINDOW *openpigs(void);
WINDOW *opensensors(void);
WINDOW *openswap(void);
WINDOW *opentcp(void);
int prefix(const char *, const char *);
@ -167,6 +172,7 @@ void showkre(void);
void showmbufs(void);
void shownetstat(void);
void showpigs(void);
void showsensors(void);
void showswap(void);
void showtcp(void);
void status(void);

261
usr.bin/systat/sensors.c Normal file
View File

@ -0,0 +1,261 @@
/* $FreeBSD$ */
/* $OpenBSD: sensors.c,v 1.12 2007/07/29 04:51:59 cnst Exp $ */
/*-
* Copyright (c) 2007 Deanna Phillips <deanna@openbsd.org>
* Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
* Copyright (c) 2006 Constantine A. Murenin <cnst+openbsd@bugmail.mojo.ru>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/sensors.h>
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include "systat.h"
#include "extern.h"
struct sensor sensor;
struct sensordev sensordev;
int row, sensor_cnt;
void printline(void);
static char * fmttime(double);
WINDOW *
opensensors(void)
{
return (subwin(stdscr, LINES-3-1, 0, MAINWIN_ROW, 0));
}
void
closesensors(WINDOW *w)
{
if (w == NULL)
return;
wclear(w);
wrefresh(w);
delwin(w);
}
void
labelsensors(void)
{
wmove(wnd, 0, 0);
wclrtobot(wnd);
mvwaddstr(wnd, 0, 0, "Sensor");
mvwaddstr(wnd, 0, 34, "Value");
mvwaddstr(wnd, 0, 45, "Status");
mvwaddstr(wnd, 0, 58, "Description");
}
void
fetchsensors(void)
{
enum sensor_type type;
size_t slen, sdlen;
int mib[5], dev, numt;
mib[0] = CTL_HW;
mib[1] = HW_SENSORS;
slen = sizeof(struct sensor);
sdlen = sizeof(struct sensordev);
row = 1;
sensor_cnt = 0;
wmove(wnd, row, 0);
wclrtobot(wnd);
for (dev = 0; dev < MAXSENSORDEVICES; dev++) {
mib[2] = dev;
if (sysctl(mib, 3, &sensordev, &sdlen, NULL, 0) == -1) {
if (errno != ENOENT)
warn("sysctl");
continue;
}
for (type = 0; type < SENSOR_MAX_TYPES; type++) {
mib[3] = type;
for (numt = 0; numt < sensordev.maxnumt[type]; numt++) {
mib[4] = numt;
if (sysctl(mib, 5, &sensor, &slen, NULL, 0)
== -1) {
if (errno != ENOENT)
warn("sysctl");
continue;
}
if (sensor.flags & SENSOR_FINVALID)
continue;
sensor_cnt++;
printline();
}
}
}
}
const char *drvstat[] = {
NULL,
"empty", "ready", "powerup", "online", "idle", "active",
"rebuild", "powerdown", "fail", "pfail"
};
void
showsensors(void)
{
if (sensor_cnt == 0)
mvwaddstr(wnd, row, 0, "No sensors found.");
}
int
initsensors(void)
{
return (1);
}
void
printline(void)
{
mvwprintw(wnd, row, 0, "%s.%s%d", sensordev.xname,
sensor_type_s[sensor.type], sensor.numt);
switch (sensor.type) {
case SENSOR_TEMP:
mvwprintw(wnd, row, 24, "%10.2f degC",
(sensor.value - 273150000) / 1000000.0);
break;
case SENSOR_FANRPM:
mvwprintw(wnd, row, 24, "%11lld RPM", sensor.value);
break;
case SENSOR_VOLTS_DC:
mvwprintw(wnd, row, 24, "%10.2f V DC",
sensor.value / 1000000.0);
break;
case SENSOR_AMPS:
mvwprintw(wnd, row, 24, "%10.2f A", sensor.value / 1000000.0);
break;
case SENSOR_INDICATOR:
mvwprintw(wnd, row, 24, "%15s", sensor.value? "On" : "Off");
break;
case SENSOR_INTEGER:
mvwprintw(wnd, row, 24, "%11lld raw", sensor.value);
break;
case SENSOR_PERCENT:
mvwprintw(wnd, row, 24, "%14.2f%%", sensor.value / 1000.0);
break;
case SENSOR_LUX:
mvwprintw(wnd, row, 24, "%15.2f lx", sensor.value / 1000000.0);
break;
case SENSOR_DRIVE:
if (0 < sensor.value &&
sensor.value < sizeof(drvstat)/sizeof(drvstat[0])) {
mvwprintw(wnd, row, 24, "%15s", drvstat[sensor.value]);
break;
}
break;
case SENSOR_TIMEDELTA:
mvwprintw(wnd, row, 24, "%15s", fmttime(sensor.value / 1000000000.0));
break;
case SENSOR_WATTHOUR:
mvwprintw(wnd, row, 24, "%12.2f Wh", sensor.value / 1000000.0);
break;
case SENSOR_AMPHOUR:
mvwprintw(wnd, row, 24, "%10.2f Ah", sensor.value / 1000000.0);
break;
default:
mvwprintw(wnd, row, 24, "%10lld", sensor.value);
break;
}
if (sensor.desc[0] != '\0')
mvwprintw(wnd, row, 58, "(%s)", sensor.desc);
switch (sensor.status) {
case SENSOR_S_UNSPEC:
break;
case SENSOR_S_UNKNOWN:
mvwaddstr(wnd, row, 45, "unknown");
break;
case SENSOR_S_WARN:
mvwaddstr(wnd, row, 45, "WARNING");
break;
case SENSOR_S_CRIT:
mvwaddstr(wnd, row, 45, "CRITICAL");
break;
case SENSOR_S_OK:
mvwaddstr(wnd, row, 45, "OK");
break;
}
row++;
}
#define SECS_PER_DAY 86400
#define SECS_PER_HOUR 3600
#define SECS_PER_MIN 60
static char *
fmttime(double in)
{
int signbit = 1;
int tiny = 0;
char *unit;
#define LEN 32
static char outbuf[LEN];
if (in < 0){
signbit = -1;
in *= -1;
}
if (in >= SECS_PER_DAY ){
unit = "days";
in /= SECS_PER_DAY;
} else if (in >= SECS_PER_HOUR ){
unit = "hr";
in /= SECS_PER_HOUR;
} else if (in >= SECS_PER_MIN ){
unit = "min";
in /= SECS_PER_MIN;
} else if (in >= 1 ){
unit = "s";
/* in *= 1; */ /* no op */
} else if (in == 0 ){ /* direct comparisons to floats are scary */
unit = "s";
} else if (in >= 1e-3 ){
unit = "ms";
in *= 1e3;
} else if (in >= 1e-6 ){
unit = "us";
in *= 1e6;
} else if (in >= 1e-9 ){
unit = "ns";
in *= 1e9;
} else {
unit = "ps";
if (in < 1e-13)
tiny = 1;
in *= 1e12;
}
snprintf(outbuf, LEN,
tiny ? "%s%lf %s" : "%s%.3lf %s",
signbit == -1 ? "-" : "", in, unit);
return outbuf;
}

View File

@ -101,6 +101,7 @@ to be one of:
.Ic mbufs ,
.Ic netstat ,
.Ic pigs ,
.Ic sensors ,
.Ic swap ,
.Ic tcp ,
or
@ -286,6 +287,11 @@ Areas known to the kernel, but not in use are shown as not available.
.It Ic mbufs
Display, in the lower window, the number of mbufs allocated
for particular uses, i.e., data, socket structures, etc.
.It Ic sensors
Display, in the lower window,
the current values of available hardware sensors,
in a format similar to that of
.Xr sysctl 8 .
.It Ic vmstat
Take over the entire display and show a (rather crowded) compendium
of statistics related to virtual memory usage, process scheduling,
@ -607,6 +613,7 @@ For port names.
.Xr udp 4 ,
.Xr gstat 8 ,
.Xr iostat 8 ,
.Xr sysctl 8 ,
.Xr vmstat 8
.Sh HISTORY
The

View File

@ -158,6 +158,7 @@ SUBDIR= ac \
${_sendmail} \
setfmac \
setpmac \
sensorsd \
${_sicontrol} \
sliplogin \
slstat \

View File

@ -0,0 +1,9 @@
# $FreeBSD$
# $OpenBSD: Makefile,v 1.1 2003/09/24 20:32:49 henning Exp $
PROG= sensorsd
MAN= sensorsd.8 sensorsd.conf.5
CFLAGS+= -Wall
.include <bsd.prog.mk>

View File

@ -0,0 +1,93 @@
.\" $FreeBSD$
.\" $OpenBSD: sensorsd.8,v 1.16 2007/08/11 20:45:35 cnst Exp $
.\"
.\" Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
.\" Copyright (c) 2005 Matthew Gream <matthew.gream@pobox.com>
.\" Copyright (c) 2007 Constantine A. Murenin <cnst@FreeBSD.org>
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
.\" copyright notice and this permission notice appear in all copies.
.\"
.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.Dd August 11, 2007
.Dt SENSORSD 8
.Os
.Sh NAME
.Nm sensorsd
.Nd hardware sensors monitor
.Sh SYNOPSIS
.Nm sensorsd
.Op Fl d
.Sh DESCRIPTION
The
.Nm
utility retrieves sensor monitoring data like fan speed,
temperature, voltage and
.Xr ami 4
logical disk status via
.Xr sysctl 3 .
When the state of any monitored sensor changes, an alert is sent using
.Xr syslog 3
and a command, if specified, is executed.
.Pp
By default,
.Nm
monitors status changes on all sensors that keep their state,
thus sensors that automatically provide status do not require
any additional configuration.
In addition, for every sensor,
no matter whether it automatically provides its state or not,
custom low and high limits may be set,
so that a local notion of sensor status can be computed by
.Nm ,
indicating whether the sensor is within or is exceeding its limits.
.Pp
Limit and command values for a particular sensor may be specified in the
.Xr sensorsd.conf 5
configuration file.
This file is reloaded upon receiving
.Dv SIGHUP .
.Pp
The options are as follows:
.Bl -tag -width Ds
.It Fl d
Do not daemonize.
If this option is specified,
.Nm
will run in the foreground.
.El
.Sh FILES
.Bl -tag -width "/etc/sensorsd.conf"
.It /etc/sensorsd.conf
Configuration file for
.Nm .
.El
.Sh SEE ALSO
.Xr sysctl 3 ,
.Xr syslog 3 ,
.Xr sensorsd.conf 5 ,
.Xr syslogd 8
.Sh HISTORY
The
.Nm
program first appeared in
.Ox 3.5 .
.Sh CAVEATS
Certain sensors may flip status from time to time.
To guard against false reports,
.Nm
implements a state dumping mechanism.
However, this inevitably introduces
an additional delay in status reporting and command execution,
e.g. one may notice that
.Nm
makes its initial report about the state of monitored sensors
not immediately, but either 1 or 2 minutes after it is being started up.

View File

@ -0,0 +1,644 @@
/* $FreeBSD$ */
/* $OpenBSD: sensorsd.c,v 1.34 2007/08/14 17:10:02 cnst Exp $ */
/*-
* Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
* Copyright (c) 2005 Matthew Gream <matthew.gream@pobox.com>
* Copyright (c) 2006 Constantine A. Murenin <cnst+openbsd@bugmail.mojo.ru>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/sensors.h>
#include <err.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <time.h>
#include <unistd.h>
#define RFBUFSIZ 28 /* buffer size for print_sensor */
#define RFBUFCNT 4 /* ring buffers */
#define REPORT_PERIOD 60 /* report every n seconds */
#define CHECK_PERIOD 20 /* check every n seconds */
enum sensorsd_s_status {
SENSORSD_S_UNSPEC, /* status is unspecified */
SENSORSD_S_INVALID, /* status is invalid, per SENSOR_FINVALID */
SENSORSD_S_WITHIN, /* status is within limits */
SENSORSD_S_OUTSIDE /* status is outside limits */
};
struct limits_t {
TAILQ_ENTRY(limits_t) entries;
enum sensor_type type; /* sensor type */
int numt; /* sensor number */
int64_t last_val;
int64_t lower; /* lower limit */
int64_t upper; /* upper limit */
char *command; /* failure command */
time_t astatus_changed;
time_t ustatus_changed;
enum sensor_status astatus; /* last automatic status */
enum sensor_status astatus2;
enum sensorsd_s_status ustatus; /* last user-limit status */
enum sensorsd_s_status ustatus2;
int acount; /* stat change counter */
int ucount; /* stat change counter */
u_int8_t flags; /* sensorsd limit flags */
#define SENSORSD_L_USERLIMIT 0x0001 /* user specified limit */
#define SENSORSD_L_ISTATUS 0x0002 /* ignore automatic status */
};
struct sdlim_t {
TAILQ_ENTRY(sdlim_t) entries;
char dxname[16]; /* device unix name */
int dev; /* device number */
int sensor_cnt;
TAILQ_HEAD(, limits_t) limits;
};
void usage(void);
struct sdlim_t *create_sdlim(struct sensordev *);
void check(void);
void check_sdlim(struct sdlim_t *);
void execute(char *);
void report(time_t);
void report_sdlim(struct sdlim_t *, time_t);
static char *print_sensor(enum sensor_type, int64_t);
void parse_config(char *);
void parse_config_sdlim(struct sdlim_t *, char **);
int64_t get_val(char *, int, enum sensor_type);
void reparse_cfg(int);
TAILQ_HEAD(, sdlim_t) sdlims = TAILQ_HEAD_INITIALIZER(sdlims);
char *configfile;
volatile sig_atomic_t reload = 0;
int debug = 0;
void
usage(void)
{
extern char *__progname;
fprintf(stderr, "usage: %s [-d]\n", __progname);
exit(1);
}
int
main(int argc, char *argv[])
{
struct sensordev sensordev;
struct sdlim_t *sdlim;
size_t sdlen = sizeof(sensordev);
time_t next_report, last_report = 0, next_check;
int mib[3], dev;
int sleeptime, sensor_cnt = 0, ch;
while ((ch = getopt(argc, argv, "d")) != -1) {
switch (ch) {
case 'd':
debug = 1;
break;
default:
usage();
}
}
mib[0] = CTL_HW;
mib[1] = HW_SENSORS;
for (dev = 0; dev < MAXSENSORDEVICES; dev++) {
mib[2] = dev;
if (sysctl(mib, 3, &sensordev, &sdlen, NULL, 0) == -1) {
if (errno != ENOENT)
warn("sysctl");
continue;
}
sdlim = create_sdlim(&sensordev);
TAILQ_INSERT_TAIL(&sdlims, sdlim, entries);
sensor_cnt += sdlim->sensor_cnt;
}
if (sensor_cnt == 0)
errx(1, "no sensors found");
openlog("sensorsd", LOG_PID | LOG_NDELAY, LOG_DAEMON);
if (configfile == NULL)
if (asprintf(&configfile, "/etc/sensorsd.conf") == -1)
err(1, "out of memory");
parse_config(configfile);
if (debug == 0 && daemon(0, 0) == -1)
err(1, "unable to fork");
signal(SIGHUP, reparse_cfg);
signal(SIGCHLD, SIG_IGN);
syslog(LOG_INFO, "startup, system has %d sensors", sensor_cnt);
next_check = next_report = time(NULL);
for (;;) {
if (reload) {
parse_config(configfile);
syslog(LOG_INFO, "configuration reloaded");
reload = 0;
}
if (next_check <= time(NULL)) {
check();
next_check = time(NULL) + CHECK_PERIOD;
}
if (next_report <= time(NULL)) {
report(last_report);
last_report = next_report;
next_report = time(NULL) + REPORT_PERIOD;
}
if (next_report < next_check)
sleeptime = next_report - time(NULL);
else
sleeptime = next_check - time(NULL);
if (sleeptime > 0)
sleep(sleeptime);
}
}
struct sdlim_t *
create_sdlim(struct sensordev *snsrdev)
{
struct sensor sensor;
struct sdlim_t *sdlim;
struct limits_t *limit;
size_t slen = sizeof(sensor);
int mib[5], numt;
enum sensor_type type;
if ((sdlim = calloc(1, sizeof(struct sdlim_t))) == NULL)
err(1, "calloc");
strlcpy(sdlim->dxname, snsrdev->xname, sizeof(sdlim->dxname));
mib[0] = CTL_HW;
mib[1] = HW_SENSORS;
mib[2] = sdlim->dev = snsrdev->num;
TAILQ_INIT(&sdlim->limits);
for (type = 0; type < SENSOR_MAX_TYPES; type++) {
mib[3] = type;
for (numt = 0; numt < snsrdev->maxnumt[type]; numt++) {
mib[4] = numt;
if (sysctl(mib, 5, &sensor, &slen, NULL, 0) == -1) {
if (errno != ENOENT)
warn("sysctl");
continue;
}
if ((limit = calloc(1, sizeof(struct limits_t))) ==
NULL)
err(1, "calloc");
limit->type = type;
limit->numt = numt;
TAILQ_INSERT_TAIL(&sdlim->limits, limit, entries);
sdlim->sensor_cnt++;
}
}
return (sdlim);
}
void
check(void)
{
struct sdlim_t *sdlim;
TAILQ_FOREACH(sdlim, &sdlims, entries)
check_sdlim(sdlim);
}
void
check_sdlim(struct sdlim_t *sdlim)
{
struct sensor sensor;
struct limits_t *limit;
size_t len;
int mib[5];
mib[0] = CTL_HW;
mib[1] = HW_SENSORS;
mib[2] = sdlim->dev;
len = sizeof(sensor);
TAILQ_FOREACH(limit, &sdlim->limits, entries) {
if ((limit->flags & SENSORSD_L_ISTATUS) &&
!(limit->flags & SENSORSD_L_USERLIMIT))
continue;
mib[3] = limit->type;
mib[4] = limit->numt;
if (sysctl(mib, 5, &sensor, &len, NULL, 0) == -1)
err(1, "sysctl");
if (!(limit->flags & SENSORSD_L_ISTATUS)) {
enum sensor_status newastatus = sensor.status;
if (limit->astatus != newastatus) {
if (limit->astatus2 != newastatus) {
limit->astatus2 = newastatus;
limit->acount = 0;
} else if (++limit->acount >= 3) {
limit->last_val = sensor.value;
limit->astatus2 =
limit->astatus = newastatus;
limit->astatus_changed = time(NULL);
}
}
}
if (limit->flags & SENSORSD_L_USERLIMIT) {
enum sensorsd_s_status newustatus;
if (sensor.flags & SENSOR_FINVALID)
newustatus = SENSORSD_S_INVALID;
else if (sensor.value > limit->upper ||
sensor.value < limit->lower)
newustatus = SENSORSD_S_OUTSIDE;
else
newustatus = SENSORSD_S_WITHIN;
if (limit->ustatus != newustatus) {
if (limit->ustatus2 != newustatus) {
limit->ustatus2 = newustatus;
limit->ucount = 0;
} else if (++limit->ucount >= 3) {
limit->last_val = sensor.value;
limit->ustatus2 =
limit->ustatus = newustatus;
limit->ustatus_changed = time(NULL);
}
}
}
}
}
void
execute(char *command)
{
char *argp[] = {"sh", "-c", command, NULL};
switch (fork()) {
case -1:
syslog(LOG_CRIT, "execute: fork() failed");
break;
case 0:
execv("/bin/sh", argp);
_exit(1);
/* NOTREACHED */
default:
break;
}
}
void
report(time_t last_report)
{
struct sdlim_t *sdlim;
TAILQ_FOREACH(sdlim, &sdlims, entries)
report_sdlim(sdlim, last_report);
}
void
report_sdlim(struct sdlim_t *sdlim, time_t last_report)
{
struct limits_t *limit;
TAILQ_FOREACH(limit, &sdlim->limits, entries) {
if ((limit->astatus_changed <= last_report) &&
(limit->ustatus_changed <= last_report))
continue;
if (limit->astatus_changed > last_report) {
const char *as = NULL;
switch (limit->astatus) {
case SENSOR_S_UNSPEC:
as = "";
break;
case SENSOR_S_OK:
as = ", OK";
break;
case SENSOR_S_WARN:
as = ", WARN";
break;
case SENSOR_S_CRIT:
as = ", CRITICAL";
break;
case SENSOR_S_UNKNOWN:
as = ", UNKNOWN";
break;
}
syslog(LOG_ALERT, "%s.%s%d: %s%s",
sdlim->dxname, sensor_type_s[limit->type],
limit->numt,
print_sensor(limit->type, limit->last_val), as);
}
if (limit->ustatus_changed > last_report) {
char us[BUFSIZ];
switch (limit->ustatus) {
case SENSORSD_S_UNSPEC:
snprintf(us, sizeof(us),
"ustatus uninitialised");
break;
case SENSORSD_S_INVALID:
snprintf(us, sizeof(us), "marked invalid");
break;
case SENSORSD_S_WITHIN:
snprintf(us, sizeof(us), "within limits: %s",
print_sensor(limit->type, limit->last_val));
break;
case SENSORSD_S_OUTSIDE:
snprintf(us, sizeof(us), "exceeds limits: %s",
print_sensor(limit->type, limit->last_val));
break;
}
syslog(LOG_ALERT, "%s.%s%d: %s",
sdlim->dxname, sensor_type_s[limit->type],
limit->numt, us);
}
if (limit->command) {
int i = 0, n = 0, r;
char *cmd = limit->command;
char buf[BUFSIZ];
int len = sizeof(buf);
buf[0] = '\0';
for (i = n = 0; n < len; ++i) {
if (cmd[i] == '\0') {
buf[n++] = '\0';
break;
}
if (cmd[i] != '%') {
buf[n++] = limit->command[i];
continue;
}
i++;
if (cmd[i] == '\0') {
buf[n++] = '\0';
break;
}
switch (cmd[i]) {
case 'x':
r = snprintf(&buf[n], len - n, "%s",
sdlim->dxname);
break;
case 't':
r = snprintf(&buf[n], len - n, "%s",
sensor_type_s[limit->type]);
break;
case 'n':
r = snprintf(&buf[n], len - n, "%d",
limit->numt);
break;
case '2':
r = snprintf(&buf[n], len - n, "%s",
print_sensor(limit->type,
limit->last_val));
break;
case '3':
r = snprintf(&buf[n], len - n, "%s",
print_sensor(limit->type,
limit->lower));
break;
case '4':
r = snprintf(&buf[n], len - n, "%s",
print_sensor(limit->type,
limit->upper));
break;
default:
r = snprintf(&buf[n], len - n, "%%%c",
cmd[i]);
break;
}
if (r < 0 || (r >= len - n)) {
syslog(LOG_CRIT, "could not parse "
"command");
return;
}
if (r > 0)
n += r;
}
if (buf[0])
execute(buf);
}
}
}
const char *drvstat[] = {
NULL, "empty", "ready", "powerup", "online", "idle", "active",
"rebuild", "powerdown", "fail", "pfail"
};
static char *
print_sensor(enum sensor_type type, int64_t value)
{
static char rfbuf[RFBUFCNT][RFBUFSIZ]; /* ring buffer */
static int idx;
char *fbuf;
fbuf = rfbuf[idx++];
if (idx == RFBUFCNT)
idx = 0;
switch (type) {
case SENSOR_TEMP:
snprintf(fbuf, RFBUFSIZ, "%.2f degC",
(value - 273150000) / 1000000.0);
break;
case SENSOR_FANRPM:
snprintf(fbuf, RFBUFSIZ, "%lld RPM", value);
break;
case SENSOR_VOLTS_DC:
snprintf(fbuf, RFBUFSIZ, "%.2f V DC", value / 1000000.0);
break;
case SENSOR_AMPS:
snprintf(fbuf, RFBUFSIZ, "%.2f A", value / 1000000.0);
break;
case SENSOR_WATTHOUR:
snprintf(fbuf, RFBUFSIZ, "%.2f Wh", value / 1000000.0);
break;
case SENSOR_AMPHOUR:
snprintf(fbuf, RFBUFSIZ, "%.2f Ah", value / 1000000.0);
break;
case SENSOR_INDICATOR:
snprintf(fbuf, RFBUFSIZ, "%s", value? "On" : "Off");
break;
case SENSOR_INTEGER:
snprintf(fbuf, RFBUFSIZ, "%lld", value);
break;
case SENSOR_PERCENT:
snprintf(fbuf, RFBUFSIZ, "%.2f%%", value / 1000.0);
break;
case SENSOR_LUX:
snprintf(fbuf, RFBUFSIZ, "%.2f lx", value / 1000000.0);
break;
case SENSOR_DRIVE:
if (0 < value && value < sizeof(drvstat)/sizeof(drvstat[0]))
snprintf(fbuf, RFBUFSIZ, "%s", drvstat[value]);
else
snprintf(fbuf, RFBUFSIZ, "%lld ???", value);
break;
case SENSOR_TIMEDELTA:
snprintf(fbuf, RFBUFSIZ, "%.6f secs", value / 1000000000.0);
break;
default:
snprintf(fbuf, RFBUFSIZ, "%lld ???", value);
}
return (fbuf);
}
void
parse_config(char *cf)
{
struct sdlim_t *sdlim;
char **cfa;
if ((cfa = calloc(2, sizeof(char *))) == NULL)
err(1, "calloc");
cfa[0] = cf;
cfa[1] = NULL;
TAILQ_FOREACH(sdlim, &sdlims, entries)
parse_config_sdlim(sdlim, cfa);
free(cfa);
}
void
parse_config_sdlim(struct sdlim_t *sdlim, char **cfa)
{
struct limits_t *p;
char *buf = NULL, *ebuf = NULL;
char node[48];
TAILQ_FOREACH(p, &sdlim->limits, entries) {
snprintf(node, sizeof(node), "hw.sensors.%s.%s%d",
sdlim->dxname, sensor_type_s[p->type], p->numt);
p->flags = 0;
if (cgetent(&buf, cfa, node) != 0)
if (cgetent(&buf, cfa, sensor_type_s[p->type]) != 0)
continue;
if (cgetcap(buf, "istatus", ':'))
p->flags |= SENSORSD_L_ISTATUS;
if (cgetstr(buf, "low", &ebuf) < 0)
ebuf = NULL;
p->lower = get_val(ebuf, 0, p->type);
if (cgetstr(buf, "high", &ebuf) < 0)
ebuf = NULL;
p->upper = get_val(ebuf, 1, p->type);
if (cgetstr(buf, "command", &ebuf) < 0)
ebuf = NULL;
if (ebuf)
asprintf(&(p->command), "%s", ebuf);
free(buf);
buf = NULL;
if (p->lower != LLONG_MIN || p->upper != LLONG_MAX)
p->flags |= SENSORSD_L_USERLIMIT;
}
}
int64_t
get_val(char *buf, int upper, enum sensor_type type)
{
double val;
int64_t rval = 0;
char *p;
if (buf == NULL) {
if (upper)
return (LLONG_MAX);
else
return (LLONG_MIN);
}
val = strtod(buf, &p);
if (buf == p)
err(1, "incorrect value: %s", buf);
switch(type) {
case SENSOR_TEMP:
switch(*p) {
case 'C':
printf("C");
rval = (val + 273.16) * 1000 * 1000;
break;
case 'F':
printf("F");
rval = ((val - 32.0) / 9 * 5 + 273.16) * 1000 * 1000;
break;
default:
errx(1, "unknown unit %s for temp sensor", p);
}
break;
case SENSOR_FANRPM:
rval = val;
break;
case SENSOR_VOLTS_DC:
if (*p != 'V')
errx(1, "unknown unit %s for voltage sensor", p);
rval = val * 1000 * 1000;
break;
case SENSOR_PERCENT:
rval = val * 1000.0;
break;
case SENSOR_INDICATOR:
case SENSOR_INTEGER:
case SENSOR_DRIVE:
rval = val;
break;
case SENSOR_AMPS:
case SENSOR_WATTHOUR:
case SENSOR_AMPHOUR:
case SENSOR_LUX:
rval = val * 1000 * 1000;
break;
case SENSOR_TIMEDELTA:
rval = val * 1000 * 1000 * 1000;
break;
default:
errx(1, "unsupported sensor type");
/* not reached */
}
free(buf);
return (rval);
}
/* ARGSUSED */
void
reparse_cfg(int signo)
{
reload = 1;
}

View File

@ -0,0 +1,186 @@
.\" $FreeBSD$
.\" $OpenBSD: sensorsd.conf.5,v 1.18 2007/08/14 17:10:02 cnst Exp $
.\"
.\" Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
.\" Copyright (c) 2005 Matthew Gream <matthew.gream@pobox.com>
.\" Copyright (c) 2007 Constantine A. Murenin <cnst@FreeBSD.org>
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
.\" copyright notice and this permission notice appear in all copies.
.\"
.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.Dd August 14, 2007
.Dt SENSORSD.CONF 5
.Os
.Sh NAME
.Nm sensorsd.conf
.Nd configuration file for sensorsd
.Sh DESCRIPTION
The
.Nm
file is read by
.Xr sensorsd 8
to configure hardware sensor monitoring.
Each sensor registered in the system
is matched by at most one entry in
.Nm ,
which may specify high and low limits,
and whether sensor status changes provided by the driver should be ignored.
If the limits are crossed or if the status provided by the driver changes,
.Xr sensorsd 8 's
alert functionality is triggered and a command, if specified, is
executed.
.Pp
.Nm
follows the syntax of configuration databases as documented in
.Xr getcap 3 .
Sensors may be specified by their full
.Va hw.sensors
.Xr sysctl 8
variable name or by type,
with the full name taking precedence.
For example, if an entry
.Dq hw.sensors.lm0.temp1
is not found, then an entry for
.Dq temp
will instead be looked for.
.Pp
The following attributes may be used:
.Pp
.Bl -tag -width "commandXX" -offset indent -compact
.It Li command
Specify a command to be executed on state change.
.It Li high
Specify an upper limit.
.It Li low
Specify a lower limit.
.It Li istatus
Ignore status provided by the driver.
.El
.Pp
The values for temperature sensors can be given in degrees Celsius or
Fahrenheit, for voltage sensors in volts, and fan speed sensors take a
unit-less number representing RPM.
Values for all other types of sensors can be specified
in the same units as they appear under the
.Xr sysctl 8
.Va hw.sensors
tree.
.Pp
Sensors that provide status (such as those from
.Xr bio 4 ,
.Xr esm 4 ,
or
.Xr ipmi 4 )
do not require boundary values specified
and simply trigger on status transitions.
If boundaries are specified nonetheless,
then they are used in addition to automatic status monitoring,
unless the
.Dq istatus
attribute is specified to ignore status values that are provided by the drivers.
.Pp
The command is executed when there is any change in sensor state.
Tokens in the command are substituted as follows:
.Pp
.Bl -tag -width Ds -offset indent -compact
.It %x
the xname of the device the sensor sits on
.It %t
the type of sensor
.It %n
the sensor number
.It %2
the sensor's current value
.It %3
the sensor's low limit
.It %4
the sensor's high limit
.El
.Pp
By default,
.Xr sensorsd 8
monitors status changes on all sensors that keep their state.
This behaviour may be altered by using the
.Dq istatus
attribute to ignore
status changes of sensors of a certain type
or individual sensors.
.Sh FILES
.Bl -tag -width "/etc/sensorsd.conf"
.It /etc/sensorsd.conf
Configuration file for
.Xr sensorsd 8 .
.El
.Sh EXAMPLES
In the following configuration file,
if hw.sensors.ipmi0.temp0 transitions 80C or
if its status as provided by
.Xr ipmi 4
changes, the command
.Pa /etc/sensorsd/log_warning
will be executed,
with the sensor type, number and current value passed to it.
Alerts will be sent
if hw.sensors.lm0.volt3 transitions to being within or outside
a range of 4.8V and 5.2V;
if the speed of the fan attached to hw.sensors.lm0.fan1
transitions to being below or above 1000RPM;
if any RAID volume drive
changes its status from, for example,
.Dq OK ,
such as in the case of drive failure, rebuild, or a complete failure,
the command
.Pa /etc/sensorsd/drive
will be executed, with the sensor number passed to it; however,
no alerts will be generated for status changes on timedelta sensors.
For all other sensors whose drivers automatically provide
sensor status updates, alerts will be generated
each time those sensors undergo status transitions.
.Bd -literal -offset indent
# Comments are allowed
hw.sensors.ipmi0.temp0:high=80C:command=/etc/sensorsd/log_warning %t %n %2
hw.sensors.lm0.volt3:low=4.8V:high=5.2V
hw.sensors.lm0.fan1:low=1000
drive:command=/etc/sensorsd/drive %n
timedelta:istatus #ignore status changes for timedelta
.Ed
.Sh SEE ALSO
.Xr getcap 3 ,
.Xr bio 4 ,
.Xr esm 4 ,
.Xr ipmi 4 ,
.Xr sensorsd 8 ,
.Xr sysctl 8
.Sh HISTORY
The
.Nm
file format first appeared in
.Ox 3.5 .
The format was altered in
.Ox 4.1
to accommodate hierarchical device-based sensor addressing.
The
.Dq istatus
attribute was introduced in
.Ox 4.2 .
.Sh CAVEATS
Alert functionality is triggered every time there is a change in sensor state;
for example, when
.Xr sensorsd 8
is started,
the status of each monitored sensor changes
from undefined to whatever it is.
One must keep this in mind when using commands
that may unconditionally perform adverse actions (e.g.\&
.Xr shutdown 8 ) ,
as they will be executed even when all sensors perform to specification.
If this is undesirable, then a wrapper shell script should be used instead.