From eb3a3472e35da0abd7f5cdeac778f403c9e517c5 Mon Sep 17 00:00:00 2001 From: Hasso Tepper Date: Tue, 2 Oct 2007 12:57:01 +0000 Subject: [PATCH] Hardware sensors framework originally developed in OpenBSD and ported to FreeBSD by Constantine A. Murenin . Obtained-from: OpenBSD via FreeBSD GSoC 2007 project --- etc/Makefile | 4 +- etc/defaults/rc.conf | 4 +- etc/rc.d/Makefile | 4 +- etc/rc.d/sensorsd | 15 + etc/sensorsd.conf | 50 +++ lib/libc/gen/sysctl.3 | 31 +- sbin/sysctl/sysctl.8 | 3 +- sbin/sysctl/sysctl.c | 142 ++++++- share/man/man5/rc.conf.5 | 19 +- share/man/man9/Makefile | 10 +- share/man/man9/sensor_attach.9 | 153 +++++++ sys/conf/files | 3 +- sys/kern/kern_sensors.c | 420 +++++++++++++++++++ sys/sys/sensors.h | 170 ++++++++ sys/sys/sysctl.h | 9 +- usr.bin/systat/Makefile | 6 +- usr.bin/systat/cmdtab.c | 5 +- usr.bin/systat/extern.h | 8 +- usr.bin/systat/sensors.c | 258 ++++++++++++ usr.bin/systat/systat.1 | 9 +- usr.sbin/Makefile | 3 +- usr.sbin/sensorsd/Makefile | 9 + usr.sbin/sensorsd/sensorsd.8 | 93 +++++ usr.sbin/sensorsd/sensorsd.c | 642 ++++++++++++++++++++++++++++++ usr.sbin/sensorsd/sensorsd.conf.5 | 186 +++++++++ 25 files changed, 2235 insertions(+), 21 deletions(-) create mode 100644 etc/rc.d/sensorsd create mode 100644 etc/sensorsd.conf create mode 100644 share/man/man9/sensor_attach.9 create mode 100644 sys/kern/kern_sensors.c create mode 100644 sys/sys/sensors.h create mode 100644 usr.bin/systat/sensors.c create mode 100644 usr.sbin/sensorsd/Makefile create mode 100644 usr.sbin/sensorsd/sensorsd.8 create mode 100644 usr.sbin/sensorsd/sensorsd.c create mode 100644 usr.sbin/sensorsd/sensorsd.conf.5 diff --git a/etc/Makefile b/etc/Makefile index ac46a65d8b..15bdb04a3f 100644 --- a/etc/Makefile +++ b/etc/Makefile @@ -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 diff --git a/etc/defaults/rc.conf b/etc/defaults/rc.conf index 422808bbee..b3032e0853 100644 --- a/etc/defaults/rc.conf +++ b/etc/defaults/rc.conf @@ -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. diff --git a/etc/rc.d/Makefile b/etc/rc.d/Makefile index b84ea9b66f..eecc3847dc 100644 --- a/etc/rc.d/Makefile +++ b/etc/rc.d/Makefile @@ -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 @@ -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 index 0000000000..564f678362 --- /dev/null +++ b/etc/rc.d/sensorsd @@ -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 index 0000000000..a9eaa11a15 --- /dev/null +++ b/etc/sensorsd.conf @@ -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 diff --git a/lib/libc/gen/sysctl.3 b/lib/libc/gen/sysctl.3 index 8065ffb56e..f61907eee0 100644 --- a/lib/libc/gen/sysctl.3 +++ b/lib/libc/gen/sysctl.3 @@ -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 diff --git a/sbin/sysctl/sysctl.8 b/sbin/sysctl/sysctl.8 index 83cac7f582..0d1566dbc0 100644 --- a/sbin/sysctl/sysctl.8 +++ b/sbin/sysctl/sysctl.8 @@ -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.. struct no .It "machdep.console_device udev_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 18fe56ed1b..831be6da0e 100644 --- a/sbin/sysctl/sysctl.c +++ b/sbin/sysctl/sysctl.c @@ -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 #include #include +#include #include #include @@ -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) diff --git a/share/man/man5/rc.conf.5 b/share/man/man5/rc.conf.5 index 6aeade0004..0812cdd7e4 100644 --- a/share/man/man5/rc.conf.5 +++ b/share/man/man5/rc.conf.5 @@ -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 , diff --git a/share/man/man9/Makefile b/share/man/man9/Makefile index 99eb8ea630..8d94e3f341 100644 --- a/share/man/man9/Makefile +++ b/share/man/man9/Makefile @@ -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 index 0000000000..809e3226c9 --- /dev/null +++ b/share/man/man9/sensor_attach.9 @@ -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 +.\" Copyright (c) 2006 Constantine A. Murenin +.\" 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 +.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. diff --git a/sys/conf/files b/sys/conf/files index 3347ee4ae8..9054252554 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -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 index 0000000000..3b2cda0240 --- /dev/null +++ b/sys/kern/kern_sensors.c @@ -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 + * Copyright (c) 2006 Constantine A. Murenin + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +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 index 0000000000..554cfc22b5 --- /dev/null +++ b/sys/sys/sensors.h @@ -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 + * Copyright (c) 2006 Constantine A. Murenin + * 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 +#ifndef NOSYSCTL8HACK + #include +#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 e1321a3b20..b746e1ab96 100644 --- a/sys/sys/sysctl.h +++ b/sys/sys/sysctl.h @@ -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 }, \ } /* diff --git a/usr.bin/systat/Makefile b/usr.bin/systat/Makefile index 86a5ae9601..6946066723 100644 --- a/usr.bin/systat/Makefile +++ b/usr.bin/systat/Makefile @@ -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 diff --git a/usr.bin/systat/cmdtab.c b/usr.bin/systat/cmdtab.c index 61333748a4..e5e9f7b33a 100644 --- a/usr.bin/systat/cmdtab.c +++ b/usr.bin/systat/cmdtab.c @@ -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]; diff --git a/usr.bin/systat/extern.h b/usr.bin/systat/extern.h index a8df8edc2e..119e963851 100644 --- a/usr.bin/systat/extern.h +++ b/usr.bin/systat/extern.h @@ -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 @@ -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 index 0000000000..551a5d6b43 --- /dev/null +++ b/usr.bin/systat/sensors.c @@ -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 + * Copyright (c) 2003 Henning Brauer + * Copyright (c) 2006 Constantine A. Murenin + * + * 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 +#include +#include +#include + +#include +#include +#include +#include + +#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; +} diff --git a/usr.bin/systat/systat.1 b/usr.bin/systat/systat.1 index 9186eaa9b8..0df871e507 100644 --- a/usr.bin/systat/systat.1 +++ b/usr.bin/systat/systat.1 @@ -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 diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index 6ed19530ee..4257792fe6 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -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 index 0000000000..448b2a50ba --- /dev/null +++ b/usr.sbin/sensorsd/Makefile @@ -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 diff --git a/usr.sbin/sensorsd/sensorsd.8 b/usr.sbin/sensorsd/sensorsd.8 new file mode 100644 index 0000000000..a1f7407884 --- /dev/null +++ b/usr.sbin/sensorsd/sensorsd.8 @@ -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 +.\" Copyright (c) 2005 Matthew Gream +.\" Copyright (c) 2007 Constantine A. Murenin +.\" +.\" 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 0000000000..31207f2ffc --- /dev/null +++ b/usr.sbin/sensorsd/sensorsd.c @@ -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 + * Copyright (c) 2005 Matthew Gream + * Copyright (c) 2006 Constantine A. Murenin + * + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 0000000000..aab1b977dd --- /dev/null +++ b/usr.sbin/sensorsd/sensorsd.conf.5 @@ -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 +.\" Copyright (c) 2005 Matthew Gream +.\" Copyright (c) 2007 Constantine A. Murenin +.\" +.\" 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. -- 2.41.0