summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authornetchild <netchild@FreeBSD.org>2007-10-14 10:45:31 +0000
committernetchild <netchild@FreeBSD.org>2007-10-14 10:45:31 +0000
commit4af9918bc0e8f388ffda416ed716c9b17ca6c0fd (patch)
treee7d76e2d64fce20db3bf8837259e3d37e4f7d3ec
parent57a6302920d738a0f943ec56aa7d87683e443246 (diff)
downloadFreeBSD-src-4af9918bc0e8f388ffda416ed716c9b17ca6c0fd.zip
FreeBSD-src-4af9918bc0e8f388ffda416ed716c9b17ca6c0fd.tar.gz
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)
-rw-r--r--etc/Makefile2
-rw-r--r--etc/defaults/rc.conf2
-rwxr-xr-xetc/rc.d/Makefile2
-rw-r--r--etc/rc.d/sensorsd17
-rw-r--r--etc/sensorsd.conf50
-rw-r--r--lib/libc/gen/sysctl.340
-rw-r--r--sbin/sysctl/sysctl.81
-rw-r--r--sbin/sysctl/sysctl.c140
-rw-r--r--share/man/man5/rc.conf.519
-rw-r--r--share/man/man9/Makefile8
-rw-r--r--share/man/man9/sensor_attach.9155
-rw-r--r--sys/conf/files1
-rw-r--r--sys/kern/kern_sensors.c421
-rw-r--r--sys/sys/sensors.h168
-rw-r--r--sys/sys/sysctl.h5
-rw-r--r--usr.bin/systat/Makefile2
-rw-r--r--usr.bin/systat/cmdtab.c3
-rw-r--r--usr.bin/systat/extern.h6
-rw-r--r--usr.bin/systat/sensors.c261
-rw-r--r--usr.bin/systat/systat.17
-rw-r--r--usr.sbin/Makefile1
-rw-r--r--usr.sbin/sensorsd/Makefile9
-rw-r--r--usr.sbin/sensorsd/sensorsd.893
-rw-r--r--usr.sbin/sensorsd/sensorsd.c644
-rw-r--r--usr.sbin/sensorsd/sensorsd.conf.5186
25 files changed, 2232 insertions, 11 deletions
diff --git a/etc/Makefile b/etc/Makefile
index fb0e3e8..725dd24 100644
--- a/etc/Makefile
+++ b/etc/Makefile
@@ -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 \
diff --git a/etc/defaults/rc.conf b/etc/defaults/rc.conf
index 7a18b9c..f941870 100644
--- a/etc/defaults/rc.conf
+++ b/etc/defaults/rc.conf
@@ -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
diff --git a/etc/rc.d/Makefile b/etc/rc.d/Makefile
index 56aa598..5f58a6f 100755
--- a/etc/rc.d/Makefile
+++ b/etc/rc.d/Makefile
@@ -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 \
diff --git a/etc/rc.d/sensorsd b/etc/rc.d/sensorsd
new file mode 100644
index 0000000..51bd970
--- /dev/null
+++ b/etc/rc.d/sensorsd
@@ -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"
diff --git a/etc/sensorsd.conf b/etc/sensorsd.conf
new file mode 100644
index 0000000..3b41b28
--- /dev/null
+++ b/etc/sensorsd.conf
@@ -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
diff --git a/lib/libc/gen/sysctl.3 b/lib/libc/gen/sysctl.3
index 1f0807b..8386678 100644
--- a/lib/libc/gen/sysctl.3
+++ b/lib/libc/gen/sysctl.3
@@ -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
diff --git a/sbin/sysctl/sysctl.8 b/sbin/sysctl/sysctl.8
index 942fd7c..1e656ad 100644
--- a/sbin/sysctl/sysctl.8
+++ b/sbin/sysctl/sysctl.8
@@ -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
diff --git a/sbin/sysctl/sysctl.c b/sbin/sysctl/sysctl.c
index 9dcfcb0..e4aa86b 100644
--- a/sbin/sysctl/sysctl.c
+++ b/sbin/sysctl/sysctl.c
@@ -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>
@@ -408,6 +409,143 @@ S_vmtotal(int l2, void *p)
}
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)
{
dev_t *d = (dev_t *)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
diff --git a/share/man/man5/rc.conf.5 b/share/man/man5/rc.conf.5
index 02c6432..9415fce 100644
--- a/share/man/man5/rc.conf.5
+++ b/share/man/man5/rc.conf.5
@@ -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 ,
diff --git a/share/man/man9/Makefile b/share/man/man9/Makefile
index bf6bcad..68a6ffe 100644
--- a/share/man/man9/Makefile
+++ b/share/man/man9/Makefile
@@ -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 \
diff --git a/share/man/man9/sensor_attach.9 b/share/man/man9/sensor_attach.9
new file mode 100644
index 0000000..ce4bb20
--- /dev/null
+++ b/share/man/man9/sensor_attach.9
@@ -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.
diff --git a/sys/conf/files b/sys/conf/files
index 34d5235..5089ef3 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -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
diff --git a/sys/kern/kern_sensors.c b/sys/kern/kern_sensors.c
new file mode 100644
index 0000000..2ffbad2
--- /dev/null
+++ b/sys/kern/kern_sensors.c
@@ -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));
+}
diff --git a/sys/sys/sensors.h b/sys/sys/sensors.h
new file mode 100644
index 0000000..15306ad
--- /dev/null
+++ b/sys/sys/sensors.h
@@ -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_ */
diff --git a/sys/sys/sysctl.h b/sys/sys/sysctl.h
index 5d07d6b..a861a4b 100644
--- a/sys/sys/sysctl.h
+++ b/sys/sys/sysctl.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}, \
}
/*
diff --git a/usr.bin/systat/Makefile b/usr.bin/systat/Makefile
index 4f5f229..2c7592a 100644
--- a/usr.bin/systat/Makefile
+++ b/usr.bin/systat/Makefile
@@ -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
diff --git a/usr.bin/systat/cmdtab.c b/usr.bin/systat/cmdtab.c
index a8daa13..d2e6190 100644
--- a/usr.bin/systat/cmdtab.c
+++ b/usr.bin/systat/cmdtab.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];
diff --git a/usr.bin/systat/extern.h b/usr.bin/systat/extern.h
index 84af89c..29220c0 100644
--- a/usr.bin/systat/extern.h
+++ b/usr.bin/systat/extern.h
@@ -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);
diff --git a/usr.bin/systat/sensors.c b/usr.bin/systat/sensors.c
new file mode 100644
index 0000000..8af57c2
--- /dev/null
+++ b/usr.bin/systat/sensors.c
@@ -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;
+}
diff --git a/usr.bin/systat/systat.1 b/usr.bin/systat/systat.1
index 0c5c4b5..409d8bf 100644
--- a/usr.bin/systat/systat.1
+++ b/usr.bin/systat/systat.1
@@ -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
diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile
index f6a2fcc..61a7f0f 100644
--- a/usr.sbin/Makefile
+++ b/usr.sbin/Makefile
@@ -158,6 +158,7 @@ SUBDIR= ac \
${_sendmail} \
setfmac \
setpmac \
+ sensorsd \
${_sicontrol} \
sliplogin \
slstat \
diff --git a/usr.sbin/sensorsd/Makefile b/usr.sbin/sensorsd/Makefile
new file mode 100644
index 0000000..153f542
--- /dev/null
+++ b/usr.sbin/sensorsd/Makefile
@@ -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>
diff --git a/usr.sbin/sensorsd/sensorsd.8 b/usr.sbin/sensorsd/sensorsd.8
new file mode 100644
index 0000000..a1f15d6
--- /dev/null
+++ b/usr.sbin/sensorsd/sensorsd.8
@@ -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.
diff --git a/usr.sbin/sensorsd/sensorsd.c b/usr.sbin/sensorsd/sensorsd.c
new file mode 100644
index 0000000..46c056e
--- /dev/null
+++ b/usr.sbin/sensorsd/sensorsd.c
@@ -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;
+}
diff --git a/usr.sbin/sensorsd/sensorsd.conf.5 b/usr.sbin/sensorsd/sensorsd.conf.5
new file mode 100644
index 0000000..96aa3d7
--- /dev/null
+++ b/usr.sbin/sensorsd/sensorsd.conf.5
@@ -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.
OpenPOWER on IntegriCloud