Hardware sensors framework originally developed in OpenBSD and ported to
authorHasso Tepper <hasso@dragonflybsd.org>
Tue, 2 Oct 2007 12:57:01 +0000 (12:57 +0000)
committerHasso Tepper <hasso@dragonflybsd.org>
Tue, 2 Oct 2007 12:57:01 +0000 (12:57 +0000)
FreeBSD by Constantine A. Murenin <mureninc at gmail.com>.

Obtained-from: OpenBSD via FreeBSD GSoC 2007 project

25 files changed:
etc/Makefile
etc/defaults/rc.conf
etc/rc.d/Makefile
etc/rc.d/sensorsd [new file with mode: 0644]
etc/sensorsd.conf [new file with mode: 0644]
lib/libc/gen/sysctl.3
sbin/sysctl/sysctl.8
sbin/sysctl/sysctl.c
share/man/man5/rc.conf.5
share/man/man9/Makefile
share/man/man9/sensor_attach.9 [new file with mode: 0644]
sys/conf/files
sys/kern/kern_sensors.c [new file with mode: 0644]
sys/sys/sensors.h [new file with mode: 0644]
sys/sys/sysctl.h
usr.bin/systat/Makefile
usr.bin/systat/cmdtab.c
usr.bin/systat/extern.h
usr.bin/systat/sensors.c [new file with mode: 0644]
usr.bin/systat/systat.1
usr.sbin/Makefile
usr.sbin/sensorsd/Makefile [new file with mode: 0644]
usr.sbin/sensorsd/sensorsd.8 [new file with mode: 0644]
usr.sbin/sensorsd/sensorsd.c [new file with mode: 0644]
usr.sbin/sensorsd/sensorsd.conf.5 [new file with mode: 0644]

index ac46a65..15bdb04 100644 (file)
@@ -1,6 +1,6 @@
 #      from: @(#)Makefile      5.11 (Berkeley) 5/21/91
 # $FreeBSD: src/etc/Makefile,v 1.219.2.38 2003/03/04 09:49:00 ru Exp $
-# $DragonFly: src/etc/Makefile,v 1.188 2007/09/14 19:08:59 swildner Exp $
+# $DragonFly: src/etc/Makefile,v 1.189 2007/10/02 12:57:00 hasso Exp $
 
 .if !defined(NO_SENDMAIL)
 SUBDIR=        sendmail
@@ -31,7 +31,7 @@ BIN1= amd.map auth.conf \
        inetd.conf login.access login.conf \
        motd modems networks newsyslog.conf \
        pf.conf phones printcap profile \
-       remote \
+       remote sensorsd.conf \
        shells sysctl.conf syslog.conf usbd.conf \
        etc.${MACHINE_ARCH}/ttys
 .if defined(BINARY_UPGRADE) # location of these depends on upgrade method
index 422808b..b3032e0 100644 (file)
@@ -14,7 +14,7 @@
 # All arguments must be in double or single quotes.
 #
 # $FreeBSD: src/etc/defaults/rc.conf,v 1.180 2003/06/26 09:50:50 smkelly Exp $
-# $DragonFly: src/etc/defaults/rc.conf,v 1.42 2007/08/10 21:01:05 swildner Exp $
+# $DragonFly: src/etc/defaults/rc.conf,v 1.43 2007/10/02 12:57:00 hasso Exp $
 
 ##############################################################
 ###  Important initial Boot-time options  ####################
@@ -32,6 +32,8 @@ battd_flags=""                # Flags to battd (if enabled).
 kldxref_enable="NO"    # Build linker.hints files with kldxref(8).
 kldxref_clobber="NO"   # Overwrite old linker.hints at boot.
 kldxref_module_path="" # Override kern.module_path. A ';'-delimited list.
+sensorsd_enable="NO"   # Run sensorsd to monitor and log sensor state changes.
+sensorsd_flags=""      # additional flags for sensorsd(8).
 pccard_ifconfig="NO"   # Specialized pccard ethernet configuration (or NO).
 pccard_ether_delay="5" # Delay before trying to start dhclient in pccard_ether
 removable_interfaces=""        # Removable network interfaces for /etc/pccard_ether.
index b84ea9b..eecc384 100644 (file)
@@ -1,6 +1,6 @@
 # $NetBSD: Makefile,v 1.16 2001/01/14 15:37:22 minoura Exp $
 # $FreeBSD: src/etc/rc.d/Makefile,v 1.20 2003/06/29 05:15:57 mtm Exp $
-# $DragonFly: src/etc/rc.d/Makefile,v 1.22 2007/08/12 14:37:48 swildner Exp $
+# $DragonFly: src/etc/rc.d/Makefile,v 1.23 2007/10/02 12:57:00 hasso Exp $
 
 .include <bsd.own.mk>
 
@@ -22,7 +22,7 @@ FILES=        DAEMON LOGIN NETWORKING SERVERS abi accounting addswap adjkerntz \
        dntpd othermta pf pflog ppp ppp-user pppoed pwcheck \
        quota random rarpd rcconf.sh resident rndcontrol root route6d routed \
        routing rpcbind rtadvd rtsold rwho sysdb savecore securelevel sendmail \
-       serial sppp sshd swap1 syscons sysctl syslogd timed ttys usbd \
+       sensorsd serial sppp sshd swap1 syscons sysctl syslogd timed ttys usbd \
        varsym vinum virecover watchdogd wpa_supplicant \
        ypbind yppasswdd ypserv ypset ypupdated ypxfrd
 FILESDIR=      /etc/rc.d
diff --git a/etc/rc.d/sensorsd b/etc/rc.d/sensorsd
new file mode 100644 (file)
index 0000000..564f678
--- /dev/null
@@ -0,0 +1,15 @@
+#!/bin/sh
+#
+# $DragonFly: src/etc/rc.d/sensorsd,v 1.1 2007/10/02 12:57:00 hasso Exp $
+
+# 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 (file)
index 0000000..a9eaa11
--- /dev/null
@@ -0,0 +1,50 @@
+# $OpenBSD: sensorsd.conf,v 1.8 2007/08/14 19:02:02 cnst Exp $
+# $DragonFly: src/etc/sensorsd.conf,v 1.1 2007/10/02 12:57:00 hasso 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
index 8065ffb..f61907e 100644 (file)
@@ -31,7 +31,7 @@
 .\"
 .\"    @(#)sysctl.3    8.4 (Berkeley) 5/9/95
 .\" $FreeBSD: src/lib/libc/gen/sysctl.3,v 1.33.2.13 2002/04/07 04:57:14 dd Exp $
-.\" $DragonFly: src/lib/libc/gen/sysctl.3,v 1.6 2007/02/01 10:33:26 corecode Exp $
+.\" $DragonFly: src/lib/libc/gen/sysctl.3,v 1.7 2007/10/02 12:57:00 hasso Exp $
 .\"
 .Dd January 23, 2001
 .Dt SYSCTL 3
@@ -285,6 +285,7 @@ privilege may change the value.
 .It "HW\_MACHINE\_ARCH string  no"
 .\".It "HW\_DISKNAMES  integer no"
 .\".It "HW\_DISKSTATS  integer no"
+.It "HW_SENSORS        node    not applicable"
 .El
 .Pp
 .Bl -tag -width 6n
@@ -308,6 +309,34 @@ Nonzero if the floating point support is in hardware.
 The machine dependent architecture type.
 .\".It Fa HW_DISKNAMES
 .\".It Fa HW_DISKSTATS
+.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
+.Aq Pa sys/sensors.h .
 .El
 .Ss CTL_KERN
 The string and integer information available for the CTL_KERN level
index 83cac7f..0d1566d 100644 (file)
@@ -31,7 +31,7 @@
 .\"
 .\"    From: @(#)sysctl.8      8.1 (Berkeley) 6/6/93
 .\" $FreeBSD: src/sbin/sysctl/sysctl.8,v 1.23.2.17 2003/05/19 07:49:34 brueffer Exp $
-.\" $DragonFly: src/sbin/sysctl/sysctl.8,v 1.6 2006/09/21 16:16:07 dillon Exp $
+.\" $DragonFly: src/sbin/sysctl/sysctl.8,v 1.7 2007/10/02 12:57:00 hasso Exp $
 .\"
 .Dd March 10, 2002
 .Dt SYSCTL 8
@@ -203,6 +203,7 @@ denote
 .It "hw.pagesize       integer no
 .It "hw.floatingpoint  integer no
 .It "hw.machine_arch   string  no
+.It "hw.sensors.<xname>.<type><numt>   struct  no
 .It "machdep.console_device    udev_t  no
 .It "machdep.adjkerntz integer yes
 .It "machdep.disable_rtc_set   integer yes
index 18fe56e..831be6d 100644 (file)
@@ -33,7 +33,7 @@
  * @(#) Copyright (c) 1993 The Regents of the University of California.  All rights reserved.
  * @(#)from: sysctl.c  8.1 (Berkeley) 6/6/93
  * $FreeBSD: src/sbin/sysctl/sysctl.c,v 1.25.2.11 2003/05/01 22:48:08 trhodes Exp $
- * $DragonFly: src/sbin/sysctl/sysctl.c,v 1.15 2007/09/24 06:16:19 hasso Exp $
+ * $DragonFly: src/sbin/sysctl/sysctl.c,v 1.16 2007/10/02 12:57:00 hasso Exp $
  */
 
 #ifdef __i386__
@@ -44,6 +44,7 @@
 #include <sys/stat.h>
 #include <sys/sysctl.h>
 #include <sys/resource.h>
+#include <sys/sensors.h>
 #include <sys/param.h>
 
 #include <machine/inttypes.h>
@@ -352,6 +353,143 @@ S_timeval(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)
 {
@@ -619,6 +757,8 @@ show_var(int *oid, size_t nlen)
                                func = S_timeval;
                        else if (strcmp(fmt, "S,loadavg") == 0)
                                func = S_loadavg;
+                       else if (strcmp(fmt, "S,sensor") == 0)
+                               func = S_sensor;
                        else if (strcmp(fmt, "T,dev_t") == 0)
                                func = T_dev_t;
                        else if (strcmp(fmt, "T,udev_t") == 0)
index 6aeade0..0812cdd 100644 (file)
@@ -23,7 +23,7 @@
 .\" SUCH DAMAGE.
 .\"
 .\" $FreeBSD: src/share/man/man5/rc.conf.5,v 1.197 2003/07/28 13:56:00 mbr Exp $
-.\" $DragonFly: src/share/man/man5/rc.conf.5,v 1.45 2007/08/12 17:14:26 swildner Exp $
+.\" $DragonFly: src/share/man/man5/rc.conf.5,v 1.46 2007/10/02 12:57:00 hasso Exp $
 .Dd August 10, 2007
 .Dt RC.CONF 5
 .Os
@@ -128,6 +128,22 @@ is set to
 these are the flags to pass to the
 .Xr battd 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 pccard_ifconfig
 .Pq Vt str
 List of arguments to be passed to
@@ -2789,6 +2805,7 @@ has not completed within the specified time (in seconds).
 .Xr rtsold 8 ,
 .Xr rwhod 8 ,
 .Xr savecore 8 ,
+.Xr sensorsd 8 ,
 .Xr sshd 8 ,
 .Xr swapon 8 ,
 .Xr sysctl 8 ,
index 99eb8ea..8d94e3f 100644 (file)
@@ -1,5 +1,5 @@
 # $FreeBSD: src/share/man/man9/Makefile,v 1.60.2.26 2003/06/13 01:04:17 hmp Exp $
-# $DragonFly: src/share/man/man9/Makefile,v 1.61 2007/09/14 22:53:56 swildner Exp $
+# $DragonFly: src/share/man/man9/Makefile,v 1.62 2007/10/02 12:57:00 hasso Exp $
 
 MAN=   accept_filter.9 \
        accf_data.9 \
@@ -109,6 +109,7 @@ MAN=        accept_filter.9 \
        rtalloc.9 \
        rtentry.9 \
        sbuf.9 \
+       sensor_attach.9 \
        serializer.9 \
        sleep.9 \
        spinlock.9 \
@@ -422,6 +423,13 @@ MLINKS+=sbuf.9 sbuf_bcat.9 \
        sbuf.9 sbuf_setpos.9 \
        sbuf.9 sbuf_trim.9 \
        sbuf.9 sbuf_vprintf.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+=serializer.9 ASSERT_SERIALIZED.9 \
        serializer.9 lwkt_serialize_enter.9 \
        serializer.9 lwkt_serialize_exit.9 \
diff --git a/share/man/man9/sensor_attach.9 b/share/man/man9/sensor_attach.9
new file mode 100644 (file)
index 0000000..809e322
--- /dev/null
@@ -0,0 +1,153 @@
+.\" $OpenBSD: sensor_attach.9,v 1.4 2007/03/22 16:55:31 deraadt Exp $
+.\" $DragonFly: src/share/man/man9/sensor_attach.9,v 1.1 2007/10/02 12:57:00 hasso 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
+.Fd #include <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
+as a Google Summer of Code 2007 project.
index 3347ee4..9054252 100644 (file)
@@ -1,5 +1,5 @@
 # $FreeBSD: src/sys/conf/files,v 1.340.2.137 2003/06/04 17:10:30 sam Exp $
-# $DragonFly: src/sys/conf/files,v 1.181 2007/09/12 08:31:43 hasso Exp $
+# $DragonFly: src/sys/conf/files,v 1.182 2007/10/02 12:57:00 hasso Exp $
 #
 # The long compile-with and dependency lines are required because of
 # limitations in config: backslash-newline doesn't work in strings, and
@@ -562,6 +562,7 @@ kern/lwkt_token.c   standard
 kern/lwkt_msgport.c    standard
 kern/lwkt_serialize.c  standard
 kern/lwkt_caps.c       standard
+kern/kern_sensors.c    standard
 kern/kern_spinlock.c   standard
 kern/kern_synch.c      standard
 kern/kern_syscalls.c   standard
diff --git a/sys/kern/kern_sensors.c b/sys/kern/kern_sensors.c
new file mode 100644 (file)
index 0000000..3b2cda0
--- /dev/null
@@ -0,0 +1,420 @@
+/* $OpenBSD: kern_sensors.c,v 1.19 2007/06/04 18:42:05 deraadt Exp $ */
+/* $DragonFly: src/sys/kern/kern_sensors.c,v 1.1 2007/10/02 12:57:01 hasso Exp $ */
+
+/*
+ * Copyright (c) 2005 David Gwynne <dlg@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>
+#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/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 = kmalloc(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_second))
+                       tsleep(&tasklist, 0, "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) {
+                               kfree(st, M_DEVBUF);
+                               continue;
+                       }
+
+                       /* run the task */
+                       st->func(st->arg);
+                       /* stick it back in the tasklist */
+                       sensor_task_schedule(st);
+               }
+       }
+
+       kthread_exit();
+}
+
+void
+sensor_task_schedule(struct sensor_task *st)
+{
+       struct sensor_task      *cst;
+
+       st->nextrun = time_second + 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");
+int sensors_debug = 1;
+SYSCTL_INT(_hw_sensors, OID_AUTO, debug, CTLFLAG_RD, &sensors_debug, 0, "sensors debug");
+
+#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];
+
+               ksnprintf(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 = kmalloc(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));
+
+       kfree(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 = kmalloc(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));
+
+       kfree(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 (file)
index 0000000..554cfc2
--- /dev/null
@@ -0,0 +1,170 @@
+/* $OpenBSD: sensors.h,v 1.23 2007/03/22 16:55:31 deraadt Exp $ */
+/* $DragonFly: src/sys/sys/sensors.h,v 1.1 2007/10/02 12:57:01 hasso 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_
+
+#define SENSORS_DEBUG
+
+/* 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_ */
index e1321a3..b746e1a 100644 (file)
@@ -35,7 +35,7 @@
  *
  *     @(#)sysctl.h    8.1 (Berkeley) 6/2/93
  * $FreeBSD: src/sys/sys/sysctl.h,v 1.81.2.10 2003/05/01 22:48:09 trhodes Exp $
- * $DragonFly: src/sys/sys/sysctl.h,v 1.24 2007/09/23 13:38:07 hasso Exp $
+ * $DragonFly: src/sys/sys/sysctl.h,v 1.25 2007/10/02 12:57:01 hasso Exp $
  */
 
 #ifndef _SYS_SYSCTL_H_
@@ -462,8 +462,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_MACHINE_PLATFORM 12         /* string: platform architecture */
-#define HW_MACHINE_RES13 13            /* no longer used (was machine_uname) */
-#define        HW_MAXID        14              /* number of valid hw ids */
+#define HW_SENSORS     13              /* node: hardware sensors */
+#define HW_MAXID       14              /* number of valid hw ids */
 
 #define CTL_HW_NAMES { \
        { 0, 0 }, \
@@ -477,6 +477,9 @@ TAILQ_HEAD(sysctl_ctx_list, sysctl_ctx_entry);
        { "disknames", CTLTYPE_STRUCT }, \
        { "diskstats", CTLTYPE_STRUCT }, \
        { "floatingpoint", CTLTYPE_INT }, \
+       { "arch", CTLTYPE_STRING }, \
+       { "platform", CTLTYPE_STRING }, \
+       { "sensors", CTLTYPE_NODE }, \
 }
 
 /*
index 86a5ae9..6946066 100644 (file)
@@ -1,11 +1,11 @@
 #      @(#)Makefile    8.1 (Berkeley) 6/6/93
-#      $DragonFly: src/usr.bin/systat/Makefile,v 1.5 2007/08/27 16:50:59 pavalos Exp $
+#      $DragonFly: src/usr.bin/systat/Makefile,v 1.6 2007/10/02 12:57:01 hasso Exp $
 
 PROG=  systat
 CFLAGS+=-DINET6 -I${.CURDIR}/../../sys
 SRCS=  cmds.c cmdtab.c convtbl.c devs.c fetch.c ifcmds.c ifstat.c iostat.c \
-       keyboard.c main.c mbufs.c netcmds.c netstat.c pigs.c swap.c icmp.c \
-       mode.c ip.c tcp.c vmstat.c ip6.c icmp6.c
+       keyboard.c main.c mbufs.c netcmds.c netstat.c pigs.c sensors.c swap.c \
+       icmp.c mode.c ip.c tcp.c vmstat.c ip6.c icmp6.c
 DPADD= ${LIBCURSES} ${LIBTERMCAP} ${LIBM} ${LIBKVM} ${LIBDEVSTAT} ${LIBKINFO}
 LDADD= -lcurses -ltermcap -lm -lkvm -ldevstat -lkinfo
 BINGRP=        kmem
index 6133374..e5e9f7b 100644 (file)
@@ -32,7 +32,7 @@
  *
  * @(#)cmdtab.c        8.1 (Berkeley) 6/6/93
  * $FreeBSD: src/usr.bin/systat/cmdtab.c,v 1.5 1999/08/28 01:05:59 peter Exp $
- * $DragonFly: src/usr.bin/systat/cmdtab.c,v 1.4 2007/05/31 11:38:36 hasso Exp $
+ * $DragonFly: src/usr.bin/systat/cmdtab.c,v 1.5 2007/10/02 12:57:01 hasso Exp $
  */
 
 #include "systat.h"
@@ -76,6 +76,9 @@ struct        cmdtab cmdtab[] = {
        { "icmp6",      showicmp6,      fetchicmp6,     labelicmp6,
          initicmp6,    openicmp6,      closeicmp6,     cmdmode,
          reseticmp6,   CF_LOADAV },
+       { "sensors",    showsensors,    fetchsensors,   labelsensors,
+         initsensors,  opensensors,    closesensors,   0,
+         0,            CF_LOADAV },
         { 0 }
 };
 struct  cmdtab *curcmd = &cmdtab[0];
index a8df8ed..119e963 100644 (file)
@@ -31,7 +31,7 @@
  * SUCH DAMAGE.
  *
  *      @(#)extern.h   8.1 (Berkeley) 6/6/93
- * $DragonFly: src/usr.bin/systat/extern.h,v 1.6 2007/05/31 11:38:36 hasso Exp $
+ * $DragonFly: src/usr.bin/systat/extern.h,v 1.7 2007/10/02 12:57:01 hasso Exp $
  */
 
 #include <sys/cdefs.h>
@@ -81,6 +81,7 @@ void   closekre(WINDOW *);
 void    closembufs(WINDOW *);
 void    closenetstat(WINDOW *);
 void    closepigs(WINDOW *);
+void    closesensors(WINDOW *);
 void    closeswap(WINDOW *);
 void    closetcp(WINDOW *);
 int     cmdiostat(char *, char *);
@@ -104,6 +105,7 @@ void         fetchkre(void);
 void    fetchmbufs(void);
 void    fetchnetstat(void);
 void    fetchpigs(void);
+void    fetchsensors(void);
 void    fetchswap(void);
 void    fetchtcp(void);
 int     initicmp(void);
@@ -116,6 +118,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);
@@ -131,6 +134,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);
@@ -146,6 +150,7 @@ WINDOW      *openkre(void);
 WINDOW *openmbufs(void);
 WINDOW *opennetstat(void);
 WINDOW *openpigs(void);
+WINDOW *opensensors(void);
 WINDOW *openswap(void);
 WINDOW *opentcp(void);
 int     prefix(char *, char *);
@@ -164,6 +169,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 (file)
index 0000000..551a5d6
--- /dev/null
@@ -0,0 +1,258 @@
+/* $OpenBSD: sensors.c,v 1.11 2007/03/23 14:48:22 ckuethe Exp $ */
+/* $DragonFly: src/usr.bin/systat/sensors.c,v 1.1 2007/10/02 12:57:01 hasso 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>
+#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-5-1, 0, 5, 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;
+}
index 9186eaa..0df871e 100644 (file)
@@ -31,7 +31,7 @@
 .\"
 .\"    @(#)systat.1    8.2 (Berkeley) 12/30/93
 .\" $FreeBSD: src/usr.bin/systat/systat.1,v 1.23.2.9 2002/12/29 16:35:40 schweikh Exp $
-.\" $DragonFly: src/usr.bin/systat/systat.1,v 1.5 2007/05/31 11:38:37 hasso Exp $
+.\" $DragonFly: src/usr.bin/systat/systat.1,v 1.6 2007/10/02 12:57:01 hasso Exp $
 .\"
 .Dd May 31, 2007
 .Dt SYSTAT 1
@@ -97,6 +97,7 @@ to be one of:
 .Ic mbufs ,
 .Ic netstat ,
 .Ic pigs ,
+.Ic sensors ,
 .Ic swap ,
 .Ic tcp ,
 or
@@ -259,6 +260,11 @@ statistics in bar graph form (default).
 Toggle the display of kilobytes per transaction.
 (the default is to
 not display kilobytes per transaction).
+.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 .
 .El
 .It Ic swap
 Show information about swap space usage on all the
@@ -532,6 +538,7 @@ For port names.
 .Xr tcp 4 ,
 .Xr udp 4 ,
 .Xr iostat 8 ,
+.Xr sysctl 8 ,
 .Xr vmstat 8
 .Sh HISTORY
 The
index 6ed1953..4257792 100644 (file)
@@ -1,6 +1,6 @@
 #      From: @(#)Makefile      5.20 (Berkeley) 6/12/93
 # $FreeBSD: src/usr.sbin/Makefile,v 1.183.2.14 2003/04/16 11:01:51 ru Exp $
-# $DragonFly: src/usr.sbin/Makefile,v 1.34 2007/05/14 19:28:39 dillon Exp $
+# $DragonFly: src/usr.sbin/Makefile,v 1.35 2007/10/02 12:57:01 hasso Exp $
 
 .include "../sys/platform/${MACHINE_PLATFORM}/Makefile.inc"
 
@@ -108,6 +108,7 @@ SUBDIR= 802_11 \
        rtsold \
        rwhod \
        sa \
+       sensorsd \
        setkey \
        sliplogin \
        slstat \
diff --git a/usr.sbin/sensorsd/Makefile b/usr.sbin/sensorsd/Makefile
new file mode 100644 (file)
index 0000000..448b2a5
--- /dev/null
@@ -0,0 +1,9 @@
+# $OpenBSD: Makefile,v 1.1 2003/09/24 20:32:49 henning Exp $
+# $DragonFly: src/usr.sbin/sensorsd/Makefile,v 1.1 2007/10/02 12:57:01 hasso 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 (file)
index 0000000..a1f7407
--- /dev/null
@@ -0,0 +1,93 @@
+.\" $OpenBSD: sensorsd.8,v 1.16 2007/08/11 20:45:35 cnst Exp $
+.\" $DragonFly: src/usr.sbin/sensorsd/sensorsd.8,v 1.1 2007/10/02 12:57:01 hasso 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 (file)
index 0000000..31207f2
--- /dev/null
@@ -0,0 +1,642 @@
+/* $OpenBSD: sensorsd.c,v 1.34 2007/08/14 17:10:02 cnst Exp $ */
+/* $DragonFly: src/usr.sbin/sensorsd/sensorsd.c,v 1.1 2007/10/02 12:57:01 hasso 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>
+#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 (file)
index 0000000..aab1b97
--- /dev/null
@@ -0,0 +1,186 @@
+.\" $OpenBSD: sensorsd.conf.5,v 1.18 2007/08/14 17:10:02 cnst Exp $
+.\" $DragonFly: src/usr.sbin/sensorsd/sensorsd.conf.5,v 1.1 2007/10/02 12:57:01 hasso 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.