summaryrefslogtreecommitdiffstats
path: root/usr.sbin
diff options
context:
space:
mode:
authornetchild <netchild@FreeBSD.org>2007-10-14 10:45:31 +0000
committernetchild <netchild@FreeBSD.org>2007-10-14 10:45:31 +0000
commit4af9918bc0e8f388ffda416ed716c9b17ca6c0fd (patch)
treee7d76e2d64fce20db3bf8837259e3d37e4f7d3ec /usr.sbin
parent57a6302920d738a0f943ec56aa7d87683e443246 (diff)
downloadFreeBSD-src-4af9918bc0e8f388ffda416ed716c9b17ca6c0fd.zip
FreeBSD-src-4af9918bc0e8f388ffda416ed716c9b17ca6c0fd.tar.gz
Import OpenBSD's sysctl hardware sensors framework.
This commit includes the following core components: * sample configuration file for sensorsd * rc(8) script and glue code for sensorsd(8) * sysctl(3) doc fixes for CTL_HW tree * sysctl(3) documentation for hardware sensors * sysctl(8) documentation for hardware sensors * support for the sensor structure for sysctl(8) * rc.conf(5) documentation for starting sensorsd(8) * sensor_attach(9) et al documentation * /sys/kern/kern_sensors.c o sensor_attach(9) API for drivers to register ksensors o sensor_task_register(9) API for the update task o sysctl(3) glue code o hw.sensors shadow tree for sysctl(8) internal magic * <sys/sensors.h> * HW_SENSORS definition for <sys/sysctl.h> * sensors display for systat(1), including documentation * sensorsd(8) and all applicable documentation The userland part of the framework is entirely source-code compatible with OpenBSD 4.1, 4.2 and -current as of today. All sensor readings can be viewed with `sysctl hw.sensors`, monitored in semi-realtime with `systat -sensors` and also logged with `sensorsd`. Submitted by: Constantine A. Murenin <cnst@FreeBSD.org> Sponsored by: Google Summer of Code 2007 (GSoC2007/cnst-sensors) Mentored by: syrinx Tested by: many OKed by: kensmith Obtained from: OpenBSD (parts)
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/Makefile1
-rw-r--r--usr.sbin/sensorsd/Makefile9
-rw-r--r--usr.sbin/sensorsd/sensorsd.893
-rw-r--r--usr.sbin/sensorsd/sensorsd.c644
-rw-r--r--usr.sbin/sensorsd/sensorsd.conf.5186
5 files changed, 933 insertions, 0 deletions
diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile
index f6a2fcc..61a7f0f 100644
--- a/usr.sbin/Makefile
+++ b/usr.sbin/Makefile
@@ -158,6 +158,7 @@ SUBDIR= ac \
${_sendmail} \
setfmac \
setpmac \
+ sensorsd \
${_sicontrol} \
sliplogin \
slstat \
diff --git a/usr.sbin/sensorsd/Makefile b/usr.sbin/sensorsd/Makefile
new file mode 100644
index 0000000..153f542
--- /dev/null
+++ b/usr.sbin/sensorsd/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+# $OpenBSD: Makefile,v 1.1 2003/09/24 20:32:49 henning Exp $
+
+PROG= sensorsd
+MAN= sensorsd.8 sensorsd.conf.5
+
+CFLAGS+= -Wall
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/sensorsd/sensorsd.8 b/usr.sbin/sensorsd/sensorsd.8
new file mode 100644
index 0000000..a1f15d6
--- /dev/null
+++ b/usr.sbin/sensorsd/sensorsd.8
@@ -0,0 +1,93 @@
+.\" $FreeBSD$
+.\" $OpenBSD: sensorsd.8,v 1.16 2007/08/11 20:45:35 cnst Exp $
+.\"
+.\" Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
+.\" Copyright (c) 2005 Matthew Gream <matthew.gream@pobox.com>
+.\" Copyright (c) 2007 Constantine A. Murenin <cnst@FreeBSD.org>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd August 11, 2007
+.Dt SENSORSD 8
+.Os
+.Sh NAME
+.Nm sensorsd
+.Nd hardware sensors monitor
+.Sh SYNOPSIS
+.Nm sensorsd
+.Op Fl d
+.Sh DESCRIPTION
+The
+.Nm
+utility retrieves sensor monitoring data like fan speed,
+temperature, voltage and
+.Xr ami 4
+logical disk status via
+.Xr sysctl 3 .
+When the state of any monitored sensor changes, an alert is sent using
+.Xr syslog 3
+and a command, if specified, is executed.
+.Pp
+By default,
+.Nm
+monitors status changes on all sensors that keep their state,
+thus sensors that automatically provide status do not require
+any additional configuration.
+In addition, for every sensor,
+no matter whether it automatically provides its state or not,
+custom low and high limits may be set,
+so that a local notion of sensor status can be computed by
+.Nm ,
+indicating whether the sensor is within or is exceeding its limits.
+.Pp
+Limit and command values for a particular sensor may be specified in the
+.Xr sensorsd.conf 5
+configuration file.
+This file is reloaded upon receiving
+.Dv SIGHUP .
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl d
+Do not daemonize.
+If this option is specified,
+.Nm
+will run in the foreground.
+.El
+.Sh FILES
+.Bl -tag -width "/etc/sensorsd.conf"
+.It /etc/sensorsd.conf
+Configuration file for
+.Nm .
+.El
+.Sh SEE ALSO
+.Xr sysctl 3 ,
+.Xr syslog 3 ,
+.Xr sensorsd.conf 5 ,
+.Xr syslogd 8
+.Sh HISTORY
+The
+.Nm
+program first appeared in
+.Ox 3.5 .
+.Sh CAVEATS
+Certain sensors may flip status from time to time.
+To guard against false reports,
+.Nm
+implements a state dumping mechanism.
+However, this inevitably introduces
+an additional delay in status reporting and command execution,
+e.g. one may notice that
+.Nm
+makes its initial report about the state of monitored sensors
+not immediately, but either 1 or 2 minutes after it is being started up.
diff --git a/usr.sbin/sensorsd/sensorsd.c b/usr.sbin/sensorsd/sensorsd.c
new file mode 100644
index 0000000..46c056e
--- /dev/null
+++ b/usr.sbin/sensorsd/sensorsd.c
@@ -0,0 +1,644 @@
+/* $FreeBSD$ */
+/* $OpenBSD: sensorsd.c,v 1.34 2007/08/14 17:10:02 cnst Exp $ */
+
+/*-
+ * Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
+ * Copyright (c) 2005 Matthew Gream <matthew.gream@pobox.com>
+ * Copyright (c) 2006 Constantine A. Murenin <cnst+openbsd@bugmail.mojo.ru>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/sensors.h>
+
+#include <err.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+
+#define RFBUFSIZ 28 /* buffer size for print_sensor */
+#define RFBUFCNT 4 /* ring buffers */
+#define REPORT_PERIOD 60 /* report every n seconds */
+#define CHECK_PERIOD 20 /* check every n seconds */
+
+enum sensorsd_s_status {
+ SENSORSD_S_UNSPEC, /* status is unspecified */
+ SENSORSD_S_INVALID, /* status is invalid, per SENSOR_FINVALID */
+ SENSORSD_S_WITHIN, /* status is within limits */
+ SENSORSD_S_OUTSIDE /* status is outside limits */
+};
+
+struct limits_t {
+ TAILQ_ENTRY(limits_t) entries;
+ enum sensor_type type; /* sensor type */
+ int numt; /* sensor number */
+ int64_t last_val;
+ int64_t lower; /* lower limit */
+ int64_t upper; /* upper limit */
+ char *command; /* failure command */
+ time_t astatus_changed;
+ time_t ustatus_changed;
+ enum sensor_status astatus; /* last automatic status */
+ enum sensor_status astatus2;
+ enum sensorsd_s_status ustatus; /* last user-limit status */
+ enum sensorsd_s_status ustatus2;
+ int acount; /* stat change counter */
+ int ucount; /* stat change counter */
+ u_int8_t flags; /* sensorsd limit flags */
+#define SENSORSD_L_USERLIMIT 0x0001 /* user specified limit */
+#define SENSORSD_L_ISTATUS 0x0002 /* ignore automatic status */
+};
+
+struct sdlim_t {
+ TAILQ_ENTRY(sdlim_t) entries;
+ char dxname[16]; /* device unix name */
+ int dev; /* device number */
+ int sensor_cnt;
+ TAILQ_HEAD(, limits_t) limits;
+};
+
+void usage(void);
+struct sdlim_t *create_sdlim(struct sensordev *);
+void check(void);
+void check_sdlim(struct sdlim_t *);
+void execute(char *);
+void report(time_t);
+void report_sdlim(struct sdlim_t *, time_t);
+static char *print_sensor(enum sensor_type, int64_t);
+void parse_config(char *);
+void parse_config_sdlim(struct sdlim_t *, char **);
+int64_t get_val(char *, int, enum sensor_type);
+void reparse_cfg(int);
+
+TAILQ_HEAD(, sdlim_t) sdlims = TAILQ_HEAD_INITIALIZER(sdlims);
+
+char *configfile;
+volatile sig_atomic_t reload = 0;
+int debug = 0;
+
+void
+usage(void)
+{
+ extern char *__progname;
+ fprintf(stderr, "usage: %s [-d]\n", __progname);
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct sensordev sensordev;
+ struct sdlim_t *sdlim;
+ size_t sdlen = sizeof(sensordev);
+ time_t next_report, last_report = 0, next_check;
+ int mib[3], dev;
+ int sleeptime, sensor_cnt = 0, ch;
+
+ while ((ch = getopt(argc, argv, "d")) != -1) {
+ switch (ch) {
+ case 'd':
+ debug = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ mib[0] = CTL_HW;
+ mib[1] = HW_SENSORS;
+
+ for (dev = 0; dev < MAXSENSORDEVICES; dev++) {
+ mib[2] = dev;
+ if (sysctl(mib, 3, &sensordev, &sdlen, NULL, 0) == -1) {
+ if (errno != ENOENT)
+ warn("sysctl");
+ continue;
+ }
+ sdlim = create_sdlim(&sensordev);
+ TAILQ_INSERT_TAIL(&sdlims, sdlim, entries);
+ sensor_cnt += sdlim->sensor_cnt;
+ }
+
+ if (sensor_cnt == 0)
+ errx(1, "no sensors found");
+
+ openlog("sensorsd", LOG_PID | LOG_NDELAY, LOG_DAEMON);
+
+ if (configfile == NULL)
+ if (asprintf(&configfile, "/etc/sensorsd.conf") == -1)
+ err(1, "out of memory");
+ parse_config(configfile);
+
+ if (debug == 0 && daemon(0, 0) == -1)
+ err(1, "unable to fork");
+
+ signal(SIGHUP, reparse_cfg);
+ signal(SIGCHLD, SIG_IGN);
+
+ syslog(LOG_INFO, "startup, system has %d sensors", sensor_cnt);
+
+ next_check = next_report = time(NULL);
+
+ for (;;) {
+ if (reload) {
+ parse_config(configfile);
+ syslog(LOG_INFO, "configuration reloaded");
+ reload = 0;
+ }
+ if (next_check <= time(NULL)) {
+ check();
+ next_check = time(NULL) + CHECK_PERIOD;
+ }
+ if (next_report <= time(NULL)) {
+ report(last_report);
+ last_report = next_report;
+ next_report = time(NULL) + REPORT_PERIOD;
+ }
+ if (next_report < next_check)
+ sleeptime = next_report - time(NULL);
+ else
+ sleeptime = next_check - time(NULL);
+ if (sleeptime > 0)
+ sleep(sleeptime);
+ }
+}
+
+struct sdlim_t *
+create_sdlim(struct sensordev *snsrdev)
+{
+ struct sensor sensor;
+ struct sdlim_t *sdlim;
+ struct limits_t *limit;
+ size_t slen = sizeof(sensor);
+ int mib[5], numt;
+ enum sensor_type type;
+
+ if ((sdlim = calloc(1, sizeof(struct sdlim_t))) == NULL)
+ err(1, "calloc");
+
+ strlcpy(sdlim->dxname, snsrdev->xname, sizeof(sdlim->dxname));
+
+ mib[0] = CTL_HW;
+ mib[1] = HW_SENSORS;
+ mib[2] = sdlim->dev = snsrdev->num;
+
+ TAILQ_INIT(&sdlim->limits);
+
+ for (type = 0; type < SENSOR_MAX_TYPES; type++) {
+ mib[3] = type;
+ for (numt = 0; numt < snsrdev->maxnumt[type]; numt++) {
+ mib[4] = numt;
+ if (sysctl(mib, 5, &sensor, &slen, NULL, 0) == -1) {
+ if (errno != ENOENT)
+ warn("sysctl");
+ continue;
+ }
+ if ((limit = calloc(1, sizeof(struct limits_t))) ==
+ NULL)
+ err(1, "calloc");
+ limit->type = type;
+ limit->numt = numt;
+ TAILQ_INSERT_TAIL(&sdlim->limits, limit, entries);
+ sdlim->sensor_cnt++;
+ }
+ }
+
+ return (sdlim);
+}
+
+void
+check(void)
+{
+ struct sdlim_t *sdlim;
+
+ TAILQ_FOREACH(sdlim, &sdlims, entries)
+ check_sdlim(sdlim);
+}
+
+void
+check_sdlim(struct sdlim_t *sdlim)
+{
+ struct sensor sensor;
+ struct limits_t *limit;
+ size_t len;
+ int mib[5];
+
+ mib[0] = CTL_HW;
+ mib[1] = HW_SENSORS;
+ mib[2] = sdlim->dev;
+ len = sizeof(sensor);
+
+ TAILQ_FOREACH(limit, &sdlim->limits, entries) {
+ if ((limit->flags & SENSORSD_L_ISTATUS) &&
+ !(limit->flags & SENSORSD_L_USERLIMIT))
+ continue;
+
+ mib[3] = limit->type;
+ mib[4] = limit->numt;
+ if (sysctl(mib, 5, &sensor, &len, NULL, 0) == -1)
+ err(1, "sysctl");
+
+ if (!(limit->flags & SENSORSD_L_ISTATUS)) {
+ enum sensor_status newastatus = sensor.status;
+
+ if (limit->astatus != newastatus) {
+ if (limit->astatus2 != newastatus) {
+ limit->astatus2 = newastatus;
+ limit->acount = 0;
+ } else if (++limit->acount >= 3) {
+ limit->last_val = sensor.value;
+ limit->astatus2 =
+ limit->astatus = newastatus;
+ limit->astatus_changed = time(NULL);
+ }
+ }
+ }
+
+ if (limit->flags & SENSORSD_L_USERLIMIT) {
+ enum sensorsd_s_status newustatus;
+
+ if (sensor.flags & SENSOR_FINVALID)
+ newustatus = SENSORSD_S_INVALID;
+ else if (sensor.value > limit->upper ||
+ sensor.value < limit->lower)
+ newustatus = SENSORSD_S_OUTSIDE;
+ else
+ newustatus = SENSORSD_S_WITHIN;
+
+ if (limit->ustatus != newustatus) {
+ if (limit->ustatus2 != newustatus) {
+ limit->ustatus2 = newustatus;
+ limit->ucount = 0;
+ } else if (++limit->ucount >= 3) {
+ limit->last_val = sensor.value;
+ limit->ustatus2 =
+ limit->ustatus = newustatus;
+ limit->ustatus_changed = time(NULL);
+ }
+ }
+ }
+ }
+}
+
+void
+execute(char *command)
+{
+ char *argp[] = {"sh", "-c", command, NULL};
+
+ switch (fork()) {
+ case -1:
+ syslog(LOG_CRIT, "execute: fork() failed");
+ break;
+ case 0:
+ execv("/bin/sh", argp);
+ _exit(1);
+ /* NOTREACHED */
+ default:
+ break;
+ }
+}
+
+void
+report(time_t last_report)
+{
+ struct sdlim_t *sdlim;
+
+ TAILQ_FOREACH(sdlim, &sdlims, entries)
+ report_sdlim(sdlim, last_report);
+}
+
+void
+report_sdlim(struct sdlim_t *sdlim, time_t last_report)
+{
+ struct limits_t *limit;
+
+ TAILQ_FOREACH(limit, &sdlim->limits, entries) {
+ if ((limit->astatus_changed <= last_report) &&
+ (limit->ustatus_changed <= last_report))
+ continue;
+
+ if (limit->astatus_changed > last_report) {
+ const char *as = NULL;
+
+ switch (limit->astatus) {
+ case SENSOR_S_UNSPEC:
+ as = "";
+ break;
+ case SENSOR_S_OK:
+ as = ", OK";
+ break;
+ case SENSOR_S_WARN:
+ as = ", WARN";
+ break;
+ case SENSOR_S_CRIT:
+ as = ", CRITICAL";
+ break;
+ case SENSOR_S_UNKNOWN:
+ as = ", UNKNOWN";
+ break;
+ }
+ syslog(LOG_ALERT, "%s.%s%d: %s%s",
+ sdlim->dxname, sensor_type_s[limit->type],
+ limit->numt,
+ print_sensor(limit->type, limit->last_val), as);
+ }
+
+ if (limit->ustatus_changed > last_report) {
+ char us[BUFSIZ];
+
+ switch (limit->ustatus) {
+ case SENSORSD_S_UNSPEC:
+ snprintf(us, sizeof(us),
+ "ustatus uninitialised");
+ break;
+ case SENSORSD_S_INVALID:
+ snprintf(us, sizeof(us), "marked invalid");
+ break;
+ case SENSORSD_S_WITHIN:
+ snprintf(us, sizeof(us), "within limits: %s",
+ print_sensor(limit->type, limit->last_val));
+ break;
+ case SENSORSD_S_OUTSIDE:
+ snprintf(us, sizeof(us), "exceeds limits: %s",
+ print_sensor(limit->type, limit->last_val));
+ break;
+ }
+ syslog(LOG_ALERT, "%s.%s%d: %s",
+ sdlim->dxname, sensor_type_s[limit->type],
+ limit->numt, us);
+ }
+
+ if (limit->command) {
+ int i = 0, n = 0, r;
+ char *cmd = limit->command;
+ char buf[BUFSIZ];
+ int len = sizeof(buf);
+
+ buf[0] = '\0';
+ for (i = n = 0; n < len; ++i) {
+ if (cmd[i] == '\0') {
+ buf[n++] = '\0';
+ break;
+ }
+ if (cmd[i] != '%') {
+ buf[n++] = limit->command[i];
+ continue;
+ }
+ i++;
+ if (cmd[i] == '\0') {
+ buf[n++] = '\0';
+ break;
+ }
+
+ switch (cmd[i]) {
+ case 'x':
+ r = snprintf(&buf[n], len - n, "%s",
+ sdlim->dxname);
+ break;
+ case 't':
+ r = snprintf(&buf[n], len - n, "%s",
+ sensor_type_s[limit->type]);
+ break;
+ case 'n':
+ r = snprintf(&buf[n], len - n, "%d",
+ limit->numt);
+ break;
+ case '2':
+ r = snprintf(&buf[n], len - n, "%s",
+ print_sensor(limit->type,
+ limit->last_val));
+ break;
+ case '3':
+ r = snprintf(&buf[n], len - n, "%s",
+ print_sensor(limit->type,
+ limit->lower));
+ break;
+ case '4':
+ r = snprintf(&buf[n], len - n, "%s",
+ print_sensor(limit->type,
+ limit->upper));
+ break;
+ default:
+ r = snprintf(&buf[n], len - n, "%%%c",
+ cmd[i]);
+ break;
+ }
+ if (r < 0 || (r >= len - n)) {
+ syslog(LOG_CRIT, "could not parse "
+ "command");
+ return;
+ }
+ if (r > 0)
+ n += r;
+ }
+ if (buf[0])
+ execute(buf);
+ }
+ }
+}
+
+const char *drvstat[] = {
+ NULL, "empty", "ready", "powerup", "online", "idle", "active",
+ "rebuild", "powerdown", "fail", "pfail"
+};
+
+static char *
+print_sensor(enum sensor_type type, int64_t value)
+{
+ static char rfbuf[RFBUFCNT][RFBUFSIZ]; /* ring buffer */
+ static int idx;
+ char *fbuf;
+
+ fbuf = rfbuf[idx++];
+ if (idx == RFBUFCNT)
+ idx = 0;
+
+ switch (type) {
+ case SENSOR_TEMP:
+ snprintf(fbuf, RFBUFSIZ, "%.2f degC",
+ (value - 273150000) / 1000000.0);
+ break;
+ case SENSOR_FANRPM:
+ snprintf(fbuf, RFBUFSIZ, "%lld RPM", value);
+ break;
+ case SENSOR_VOLTS_DC:
+ snprintf(fbuf, RFBUFSIZ, "%.2f V DC", value / 1000000.0);
+ break;
+ case SENSOR_AMPS:
+ snprintf(fbuf, RFBUFSIZ, "%.2f A", value / 1000000.0);
+ break;
+ case SENSOR_WATTHOUR:
+ snprintf(fbuf, RFBUFSIZ, "%.2f Wh", value / 1000000.0);
+ break;
+ case SENSOR_AMPHOUR:
+ snprintf(fbuf, RFBUFSIZ, "%.2f Ah", value / 1000000.0);
+ break;
+ case SENSOR_INDICATOR:
+ snprintf(fbuf, RFBUFSIZ, "%s", value? "On" : "Off");
+ break;
+ case SENSOR_INTEGER:
+ snprintf(fbuf, RFBUFSIZ, "%lld", value);
+ break;
+ case SENSOR_PERCENT:
+ snprintf(fbuf, RFBUFSIZ, "%.2f%%", value / 1000.0);
+ break;
+ case SENSOR_LUX:
+ snprintf(fbuf, RFBUFSIZ, "%.2f lx", value / 1000000.0);
+ break;
+ case SENSOR_DRIVE:
+ if (0 < value && value < sizeof(drvstat)/sizeof(drvstat[0]))
+ snprintf(fbuf, RFBUFSIZ, "%s", drvstat[value]);
+ else
+ snprintf(fbuf, RFBUFSIZ, "%lld ???", value);
+ break;
+ case SENSOR_TIMEDELTA:
+ snprintf(fbuf, RFBUFSIZ, "%.6f secs", value / 1000000000.0);
+ break;
+ default:
+ snprintf(fbuf, RFBUFSIZ, "%lld ???", value);
+ }
+
+ return (fbuf);
+}
+
+void
+parse_config(char *cf)
+{
+ struct sdlim_t *sdlim;
+ char **cfa;
+
+ if ((cfa = calloc(2, sizeof(char *))) == NULL)
+ err(1, "calloc");
+ cfa[0] = cf;
+ cfa[1] = NULL;
+
+ TAILQ_FOREACH(sdlim, &sdlims, entries)
+ parse_config_sdlim(sdlim, cfa);
+ free(cfa);
+}
+
+void
+parse_config_sdlim(struct sdlim_t *sdlim, char **cfa)
+{
+ struct limits_t *p;
+ char *buf = NULL, *ebuf = NULL;
+ char node[48];
+
+ TAILQ_FOREACH(p, &sdlim->limits, entries) {
+ snprintf(node, sizeof(node), "hw.sensors.%s.%s%d",
+ sdlim->dxname, sensor_type_s[p->type], p->numt);
+ p->flags = 0;
+ if (cgetent(&buf, cfa, node) != 0)
+ if (cgetent(&buf, cfa, sensor_type_s[p->type]) != 0)
+ continue;
+ if (cgetcap(buf, "istatus", ':'))
+ p->flags |= SENSORSD_L_ISTATUS;
+ if (cgetstr(buf, "low", &ebuf) < 0)
+ ebuf = NULL;
+ p->lower = get_val(ebuf, 0, p->type);
+ if (cgetstr(buf, "high", &ebuf) < 0)
+ ebuf = NULL;
+ p->upper = get_val(ebuf, 1, p->type);
+ if (cgetstr(buf, "command", &ebuf) < 0)
+ ebuf = NULL;
+ if (ebuf)
+ asprintf(&(p->command), "%s", ebuf);
+ free(buf);
+ buf = NULL;
+ if (p->lower != LLONG_MIN || p->upper != LLONG_MAX)
+ p->flags |= SENSORSD_L_USERLIMIT;
+ }
+}
+
+int64_t
+get_val(char *buf, int upper, enum sensor_type type)
+{
+ double val;
+ int64_t rval = 0;
+ char *p;
+
+ if (buf == NULL) {
+ if (upper)
+ return (LLONG_MAX);
+ else
+ return (LLONG_MIN);
+ }
+
+ val = strtod(buf, &p);
+ if (buf == p)
+ err(1, "incorrect value: %s", buf);
+
+ switch(type) {
+ case SENSOR_TEMP:
+ switch(*p) {
+ case 'C':
+ printf("C");
+ rval = (val + 273.16) * 1000 * 1000;
+ break;
+ case 'F':
+ printf("F");
+ rval = ((val - 32.0) / 9 * 5 + 273.16) * 1000 * 1000;
+ break;
+ default:
+ errx(1, "unknown unit %s for temp sensor", p);
+ }
+ break;
+ case SENSOR_FANRPM:
+ rval = val;
+ break;
+ case SENSOR_VOLTS_DC:
+ if (*p != 'V')
+ errx(1, "unknown unit %s for voltage sensor", p);
+ rval = val * 1000 * 1000;
+ break;
+ case SENSOR_PERCENT:
+ rval = val * 1000.0;
+ break;
+ case SENSOR_INDICATOR:
+ case SENSOR_INTEGER:
+ case SENSOR_DRIVE:
+ rval = val;
+ break;
+ case SENSOR_AMPS:
+ case SENSOR_WATTHOUR:
+ case SENSOR_AMPHOUR:
+ case SENSOR_LUX:
+ rval = val * 1000 * 1000;
+ break;
+ case SENSOR_TIMEDELTA:
+ rval = val * 1000 * 1000 * 1000;
+ break;
+ default:
+ errx(1, "unsupported sensor type");
+ /* not reached */
+ }
+ free(buf);
+ return (rval);
+}
+
+/* ARGSUSED */
+void
+reparse_cfg(int signo)
+{
+ reload = 1;
+}
diff --git a/usr.sbin/sensorsd/sensorsd.conf.5 b/usr.sbin/sensorsd/sensorsd.conf.5
new file mode 100644
index 0000000..96aa3d7
--- /dev/null
+++ b/usr.sbin/sensorsd/sensorsd.conf.5
@@ -0,0 +1,186 @@
+.\" $FreeBSD$
+.\" $OpenBSD: sensorsd.conf.5,v 1.18 2007/08/14 17:10:02 cnst Exp $
+.\"
+.\" Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
+.\" Copyright (c) 2005 Matthew Gream <matthew.gream@pobox.com>
+.\" Copyright (c) 2007 Constantine A. Murenin <cnst@FreeBSD.org>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd August 14, 2007
+.Dt SENSORSD.CONF 5
+.Os
+.Sh NAME
+.Nm sensorsd.conf
+.Nd configuration file for sensorsd
+.Sh DESCRIPTION
+The
+.Nm
+file is read by
+.Xr sensorsd 8
+to configure hardware sensor monitoring.
+Each sensor registered in the system
+is matched by at most one entry in
+.Nm ,
+which may specify high and low limits,
+and whether sensor status changes provided by the driver should be ignored.
+If the limits are crossed or if the status provided by the driver changes,
+.Xr sensorsd 8 's
+alert functionality is triggered and a command, if specified, is
+executed.
+.Pp
+.Nm
+follows the syntax of configuration databases as documented in
+.Xr getcap 3 .
+Sensors may be specified by their full
+.Va hw.sensors
+.Xr sysctl 8
+variable name or by type,
+with the full name taking precedence.
+For example, if an entry
+.Dq hw.sensors.lm0.temp1
+is not found, then an entry for
+.Dq temp
+will instead be looked for.
+.Pp
+The following attributes may be used:
+.Pp
+.Bl -tag -width "commandXX" -offset indent -compact
+.It Li command
+Specify a command to be executed on state change.
+.It Li high
+Specify an upper limit.
+.It Li low
+Specify a lower limit.
+.It Li istatus
+Ignore status provided by the driver.
+.El
+.Pp
+The values for temperature sensors can be given in degrees Celsius or
+Fahrenheit, for voltage sensors in volts, and fan speed sensors take a
+unit-less number representing RPM.
+Values for all other types of sensors can be specified
+in the same units as they appear under the
+.Xr sysctl 8
+.Va hw.sensors
+tree.
+.Pp
+Sensors that provide status (such as those from
+.Xr bio 4 ,
+.Xr esm 4 ,
+or
+.Xr ipmi 4 )
+do not require boundary values specified
+and simply trigger on status transitions.
+If boundaries are specified nonetheless,
+then they are used in addition to automatic status monitoring,
+unless the
+.Dq istatus
+attribute is specified to ignore status values that are provided by the drivers.
+.Pp
+The command is executed when there is any change in sensor state.
+Tokens in the command are substituted as follows:
+.Pp
+.Bl -tag -width Ds -offset indent -compact
+.It %x
+the xname of the device the sensor sits on
+.It %t
+the type of sensor
+.It %n
+the sensor number
+.It %2
+the sensor's current value
+.It %3
+the sensor's low limit
+.It %4
+the sensor's high limit
+.El
+.Pp
+By default,
+.Xr sensorsd 8
+monitors status changes on all sensors that keep their state.
+This behaviour may be altered by using the
+.Dq istatus
+attribute to ignore
+status changes of sensors of a certain type
+or individual sensors.
+.Sh FILES
+.Bl -tag -width "/etc/sensorsd.conf"
+.It /etc/sensorsd.conf
+Configuration file for
+.Xr sensorsd 8 .
+.El
+.Sh EXAMPLES
+In the following configuration file,
+if hw.sensors.ipmi0.temp0 transitions 80C or
+if its status as provided by
+.Xr ipmi 4
+changes, the command
+.Pa /etc/sensorsd/log_warning
+will be executed,
+with the sensor type, number and current value passed to it.
+Alerts will be sent
+if hw.sensors.lm0.volt3 transitions to being within or outside
+a range of 4.8V and 5.2V;
+if the speed of the fan attached to hw.sensors.lm0.fan1
+transitions to being below or above 1000RPM;
+if any RAID volume drive
+changes its status from, for example,
+.Dq OK ,
+such as in the case of drive failure, rebuild, or a complete failure,
+the command
+.Pa /etc/sensorsd/drive
+will be executed, with the sensor number passed to it; however,
+no alerts will be generated for status changes on timedelta sensors.
+For all other sensors whose drivers automatically provide
+sensor status updates, alerts will be generated
+each time those sensors undergo status transitions.
+.Bd -literal -offset indent
+# Comments are allowed
+hw.sensors.ipmi0.temp0:high=80C:command=/etc/sensorsd/log_warning %t %n %2
+hw.sensors.lm0.volt3:low=4.8V:high=5.2V
+hw.sensors.lm0.fan1:low=1000
+drive:command=/etc/sensorsd/drive %n
+timedelta:istatus #ignore status changes for timedelta
+.Ed
+.Sh SEE ALSO
+.Xr getcap 3 ,
+.Xr bio 4 ,
+.Xr esm 4 ,
+.Xr ipmi 4 ,
+.Xr sensorsd 8 ,
+.Xr sysctl 8
+.Sh HISTORY
+The
+.Nm
+file format first appeared in
+.Ox 3.5 .
+The format was altered in
+.Ox 4.1
+to accommodate hierarchical device-based sensor addressing.
+The
+.Dq istatus
+attribute was introduced in
+.Ox 4.2 .
+.Sh CAVEATS
+Alert functionality is triggered every time there is a change in sensor state;
+for example, when
+.Xr sensorsd 8
+is started,
+the status of each monitored sensor changes
+from undefined to whatever it is.
+One must keep this in mind when using commands
+that may unconditionally perform adverse actions (e.g.\&
+.Xr shutdown 8 ) ,
+as they will be executed even when all sensors perform to specification.
+If this is undesirable, then a wrapper shell script should be used instead.
OpenPOWER on IntegriCloud