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:
parent
ce0d9ca3db
commit
99f6b270e3
@ -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 \
|
||||
|
@ -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
|
||||
|
@ -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
17
etc/rc.d/sensorsd
Normal 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
50
etc/sensorsd.conf
Normal 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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 ,
|
||||
|
@ -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 \
|
||||
|
155
share/man/man9/sensor_attach.9
Normal file
155
share/man/man9/sensor_attach.9
Normal 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.
|
@ -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
421
sys/kern/kern_sensors.c
Normal 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
168
sys/sys/sensors.h
Normal 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_ */
|
@ -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}, \
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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
|
||||
|
||||
|
@ -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];
|
||||
|
@ -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
261
usr.bin/systat/sensors.c
Normal 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;
|
||||
}
|
@ -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
|
||||
|
@ -158,6 +158,7 @@ SUBDIR= ac \
|
||||
${_sendmail} \
|
||||
setfmac \
|
||||
setpmac \
|
||||
sensorsd \
|
||||
${_sicontrol} \
|
||||
sliplogin \
|
||||
slstat \
|
||||
|
9
usr.sbin/sensorsd/Makefile
Normal file
9
usr.sbin/sensorsd/Makefile
Normal 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>
|
93
usr.sbin/sensorsd/sensorsd.8
Normal file
93
usr.sbin/sensorsd/sensorsd.8
Normal 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.
|
644
usr.sbin/sensorsd/sensorsd.c
Normal file
644
usr.sbin/sensorsd/sensorsd.c
Normal 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;
|
||||
}
|
186
usr.sbin/sensorsd/sensorsd.conf.5
Normal file
186
usr.sbin/sensorsd/sensorsd.conf.5
Normal 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.
|
Loading…
Reference in New Issue
Block a user