(Merlin) Added simple sensord sensory logging daemon. Not installed by

default. Now uses libsensors. Ripped the display formatting code from
sensors (with some modification) but needless to say I've only tested
a single chip. Doesn't support alarms yet. With a bit of work, could
share the display formatting code with sensors so just one source needs
updating. Compiles on my machine.


git-svn-id: http://lm-sensors.org/svn/lm-sensors/trunk@661 7894878c-1315-0410-8ee3-d5d059ff63e0
This commit is contained in:
Merlin Hughes committed 1999-12-03 00:48:25 +00:00
1 parent 7fe47c13ba
commit 8e5830460f
8 files changed
+1051 -1

No files matched your search

+2
View File
@@ -36,6 +36,8 @@ problems.
he also tested lots of things, and helped us out on several occasions.
* Fons Rademakers <Fons.Rademakers@cern.ch>
Author of the maxilife module (which could only be written under NDA)
* Merlin Hughes <merlin@merlin.org>
Author of the AMD756 module and sensord.
* Simon Vogl <simon@tk.uni-linz.ac.at>
Wrote the new i2c kernel driver. This driver is indispensable for the
+1 -1
View File
@@ -133,7 +133,7 @@ SRCDIRS := mkpatch
ifeq ($(COMPILE_KERNEL),1)
SRCDIRS += kernel kernel/busses kernel/chips kernel/include
endif
SRCDIRS += lib prog/sensors prog/dump prog/detect etc
SRCDIRS += lib prog/sensors prog/sensord prog/dump prog/detect etc
# Some often-used commands with default options
MKDIR := mkdir -p
+6
View File
@@ -51,6 +51,12 @@ this package.
* prog/mkdev/mkdev.sh (written in bash, not installed)
This script creates the /dev/i2c-* files. Please check the source before
you run it, as you may need to set some variables first.
* prog/sensord/sensord (written in C, installed by `make install-prog-sensord')
This daemon logs sensor information every 5 minutes to the log file
/var/log/sensors, in a similar format to sensors (below). This is a bit
more lightweight than executing sensors every 5 minutes, depending on how
you look at things. Automatically picks up configuration updates and
responds to SIGHUP.
* prog/sensors/sensors (written in C, installed by `make install')
This pretty-prints the information provided by the modules. It is a normal
console application, using stdin and stdout. It is essential, because
+51
View File
@@ -0,0 +1,51 @@
# Module.mk - Makefile for a Linux module for reading sensor data.
# Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
# Note that MODULE_DIR (the directory in which this file resides) is a
# 'simply expanded variable'. That means that its value is substituted
# verbatim in the rules, until it is redefined.
MODULE_DIR := prog/sensord
PROGSENSORDDIR := $(MODULE_DIR)
PROGSENSORDMAN1DIR := $(MANDIR)/man8
PROGSENSORDMAN1FILES := $(MODULE_DIR)/sensord.8
# Regrettably, even 'simply expanded variables' will not put their currently
# defined value verbatim into the command-list of rules...
PROGSENSORDTARGETS := $(MODULE_DIR)/sensord
PROGSENSORDSOURCES := $(MODULE_DIR)/sensord.c $(MODULE_DIR)/chips.c
# Include all dependency files. We use '.rd' to indicate this will create
# executables.
INCLUDEFILES += $(PROGSENSORDSOURCES:.c=.rd)
$(PROGSENSORDTARGETS): $(PROGSENSORDSOURCES:.c=.ro) lib/$(LIBSHBASENAME)
$(CC) -o $@ $(PROGSENSORDSOURCES:.c=.ro) -Llib -lsensors
all-prog-sensord: $(PROGSENSORDTARGETS)
all :: all-prog-sensord
install-prog-sensord: all-prog-sensord
mkdir -p $(SBINDIR) $(PROGSENSORDMAN1DIR)
$(INSTALL) -o root -g root -m 755 $(PROGSENSORDTARGETS) $(SBINDIR)
$(INSTALL) -o $(MANOWN) -g $(MANGRP) -m 644 $(PROGSENSORDMAN1FILES) $(PROGSENSORDMAN1DIR)
# install :: install-prog-sensord
clean-prog-sensord:
$(RM) $(PROGSENSORDDIR)/*.rd $(PROGSENSORDDIR)/*.ro
$(RM) $(PROGSENSORDTARGETS)
clean :: clean-prog-sensord
+465
View File
@@ -0,0 +1,465 @@
/*
* sensord
*
* A daemon that logs all sensor information to /var/log/sensors
* every 5 minutes.
*
* Copyright (c) 1999 Merlin Hughes <merlin@merlin.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "lib/sensors.h"
#include "lib/chips.h"
#include "sensord.h"
/* missing: "gl520sm", "thmc50", "adm1022" */
/* I don't support gl518sm-r00 - but neither does sensors */
/* Should the maxilife funky fan stuff be in sensors.conf? */
/* I don't support alarms. */
/** formatters **/
static char buff[4096];
static char *
fmtValu_0
(double values[]) {
sprintf (buff, "%.0f", values[0]);
return buff;
}
static char *
fmtTemps_0
(double values[]) {
sprintf (buff, "%.0f C (limit = %.0f C, hysteresis = %.0f C)", values[0], values[1], values[2]);
return buff;
}
static char *
fmtTemps_1
(double values[]) {
sprintf (buff, "%.1f C (limit = %.1f C, hysteresis = %.1f C)", values[0], values[1], values[2]);
return buff;
}
static char *
fmtVolt_2
(double values[]) {
sprintf (buff, "%+.2f V", values[0]);
return buff;
}
static char *
fmtVolts_2
(double values[]) {
sprintf (buff, "%+.2f V (min = %+.2f V, max = %+.2f V)", values[0], values[1], values[2]);
return buff;
}
static char *
fmtFans_0
(double values[]) {
sprintf (buff, "%.0f RPM (min = %.0f RPM, div = %.0f)", values[0], values[1], values[2]);
return buff;
}
static char *
fmtMHz_2
(double values[]) {
sprintf (buff, "%.2f MHz (min = %.2f MHz, max = %.2f MHz)", values[0], values[1], values[2]);
return buff;
}
/** LM75 **/
static char *lm75_names[] = {
SENSORS_LM75_PREFIX, NULL
};
static FeatureDescriptor lm75_features[] = {
{ fmtTemps_1, SENSORS_LM75_TEMP,
{ SENSORS_LM75_TEMP, SENSORS_LM75_TEMP_HYST, SENSORS_LM75_TEMP_OVER, -1 } },
{ NULL }
};
/** ADM1021 **/
static char *adm1021_names[] = {
SENSORS_ADM1021_PREFIX, NULL
};
static FeatureDescriptor adm1021_features[] = {
{ fmtTemps_0, SENSORS_ADM1021_TEMP,
{ SENSORS_ADM1021_TEMP, SENSORS_ADM1021_TEMP_OVER, SENSORS_ADM1021_TEMP_HYST, -1 } },
{ fmtTemps_0, SENSORS_ADM1021_REMOTE_TEMP,
{ SENSORS_ADM1021_REMOTE_TEMP, SENSORS_ADM1021_REMOTE_TEMP_OVER, SENSORS_ADM1021_REMOTE_TEMP_HYST, -1 } },
{ fmtValu_0, SENSORS_ADM1021_DIE_CODE,
{ SENSORS_ADM1021_DIE_CODE, -1 } },
{ NULL }
};
/** MAX1617 **/
static char *max1617_names[] = {
SENSORS_MAX1617_PREFIX, SENSORS_MAX1617A_PREFIX, SENSORS_THMC10_PREFIX, SENSORS_LM84_PREFIX, SENSORS_GL523_PREFIX, NULL
};
static FeatureDescriptor max1617_features[] = {
{ fmtTemps_0, SENSORS_ADM1021_TEMP,
{ SENSORS_ADM1021_TEMP, SENSORS_ADM1021_TEMP_OVER, SENSORS_ADM1021_TEMP_HYST, -1 } },
{ fmtTemps_0, SENSORS_ADM1021_REMOTE_TEMP,
{ SENSORS_ADM1021_REMOTE_TEMP, SENSORS_ADM1021_REMOTE_TEMP_OVER, SENSORS_ADM1021_REMOTE_TEMP_HYST, -1 } },
{ NULL }
};
/** ADM9240 **/
static char *adm9240_names[] = {
SENSORS_ADM9240_PREFIX, SENSORS_DS1780_PREFIX, SENSORS_LM81_PREFIX, NULL
};
static FeatureDescriptor adm9240_features[] = {
{ fmtVolts_2, SENSORS_ADM9240_IN0,
{ SENSORS_ADM9240_IN0, SENSORS_ADM9240_IN0_MIN, SENSORS_ADM9240_IN0_MAX, -1 } },
{ fmtVolts_2, SENSORS_ADM9240_IN1,
{ SENSORS_ADM9240_IN1, SENSORS_ADM9240_IN1_MIN, SENSORS_ADM9240_IN1_MAX, -1 } },
{ fmtVolts_2, SENSORS_ADM9240_IN2,
{ SENSORS_ADM9240_IN2, SENSORS_ADM9240_IN2_MIN, SENSORS_ADM9240_IN2_MAX, -1 } },
{ fmtVolts_2, SENSORS_ADM9240_IN3,
{ SENSORS_ADM9240_IN3, SENSORS_ADM9240_IN3_MIN, SENSORS_ADM9240_IN3_MAX, -1 } },
{ fmtVolts_2, SENSORS_ADM9240_IN4,
{ SENSORS_ADM9240_IN4, SENSORS_ADM9240_IN4_MIN, SENSORS_ADM9240_IN4_MAX, -1 } },
{ fmtVolts_2, SENSORS_ADM9240_IN5,
{ SENSORS_ADM9240_IN5, SENSORS_ADM9240_IN5_MIN, SENSORS_ADM9240_IN5_MAX, -1 } },
{ fmtFans_0, SENSORS_ADM9240_FAN1,
{ SENSORS_ADM9240_FAN1, SENSORS_ADM9240_FAN1_MIN, SENSORS_ADM9240_FAN1_DIV, -1 } },
{ fmtFans_0, SENSORS_ADM9240_FAN2,
{ SENSORS_ADM9240_FAN2, SENSORS_ADM9240_FAN2_MIN, SENSORS_ADM9240_FAN2_DIV, -1 } },
{ fmtTemps_0, SENSORS_ADM9240_TEMP,
{ SENSORS_ADM9240_TEMP, SENSORS_ADM9240_TEMP_OVER, SENSORS_ADM9240_TEMP_HYST, -1 } },
{ fmtVolt_2, SENSORS_ADM9240_VID,
{ SENSORS_ADM9240_VID, -1 } },
{ NULL }
};
/** SIS5595 **/
static char *sis5595_names[] = {
SENSORS_SIS5595_PREFIX, NULL
};
static FeatureDescriptor sis5595_features[] = {
{ fmtVolts_2, SENSORS_SIS5595_IN0,
{ SENSORS_SIS5595_IN0, SENSORS_SIS5595_IN0_MIN, SENSORS_SIS5595_IN0_MAX, -1 } },
{ fmtVolts_2, SENSORS_SIS5595_IN1,
{ SENSORS_SIS5595_IN1, SENSORS_SIS5595_IN1_MIN, SENSORS_SIS5595_IN1_MAX, -1 } },
{ fmtVolts_2, SENSORS_SIS5595_IN2,
{ SENSORS_SIS5595_IN2, SENSORS_SIS5595_IN2_MIN, SENSORS_SIS5595_IN2_MAX, -1 } },
{ fmtVolts_2, SENSORS_SIS5595_IN3,
{ SENSORS_SIS5595_IN3, SENSORS_SIS5595_IN3_MIN, SENSORS_SIS5595_IN3_MAX, -1 } },
{ fmtFans_0, SENSORS_SIS5595_FAN1,
{ SENSORS_SIS5595_FAN1, SENSORS_SIS5595_FAN1_MIN, SENSORS_SIS5595_FAN1_DIV, -1 } },
{ fmtFans_0, SENSORS_SIS5595_FAN2,
{ SENSORS_SIS5595_FAN2, SENSORS_SIS5595_FAN2_MIN, SENSORS_SIS5595_FAN2_DIV, -1 } },
{ fmtTemps_0, SENSORS_SIS5595_TEMP,
{ SENSORS_SIS5595_TEMP, SENSORS_SIS5595_TEMP_OVER, SENSORS_SIS5595_TEMP_HYST, -1 } },
{ NULL }
};
/** LM78 **/
static char *lm78_names[] = {
SENSORS_LM78_PREFIX, SENSORS_LM78J_PREFIX, SENSORS_LM79_PREFIX, /*"sis5595",*/ NULL
};
static FeatureDescriptor lm78_features[] = {
{ fmtVolts_2, SENSORS_LM78_IN0,
{ SENSORS_LM78_IN0, SENSORS_LM78_IN0_MIN, SENSORS_LM78_IN0_MAX, -1 } },
{ fmtVolts_2, SENSORS_LM78_IN1,
{ SENSORS_LM78_IN1, SENSORS_LM78_IN1_MIN, SENSORS_LM78_IN1_MAX, -1 } },
{ fmtVolts_2, SENSORS_LM78_IN2,
{ SENSORS_LM78_IN2, SENSORS_LM78_IN2_MIN, SENSORS_LM78_IN2_MAX, -1 } },
{ fmtVolts_2, SENSORS_LM78_IN3,
{ SENSORS_LM78_IN3, SENSORS_LM78_IN3_MIN, SENSORS_LM78_IN3_MAX, -1 } },
{ fmtVolts_2, SENSORS_LM78_IN4,
{ SENSORS_LM78_IN4, SENSORS_LM78_IN4_MIN, SENSORS_LM78_IN4_MAX, -1 } },
{ fmtVolts_2, SENSORS_LM78_IN5,
{ SENSORS_LM78_IN5, SENSORS_LM78_IN5_MIN, SENSORS_LM78_IN5_MAX, -1 } },
{ fmtVolts_2, SENSORS_LM78_IN6,
{ SENSORS_LM78_IN6, SENSORS_LM78_IN6_MIN, SENSORS_LM78_IN6_MAX, -1 } },
{ fmtFans_0, SENSORS_LM78_FAN1,
{ SENSORS_LM78_FAN1, SENSORS_LM78_FAN1_MIN, SENSORS_LM78_FAN1_DIV, -1 } },
{ fmtFans_0, SENSORS_LM78_FAN2,
{ SENSORS_LM78_FAN2, SENSORS_LM78_FAN2_MIN, SENSORS_LM78_FAN2_DIV, -1 } },
{ fmtFans_0, SENSORS_LM78_FAN3,
{ SENSORS_LM78_FAN3, SENSORS_LM78_FAN3_MIN, SENSORS_LM78_FAN3_DIV, -1 } },
{ fmtTemps_0, SENSORS_LM78_TEMP,
{ SENSORS_LM78_TEMP, SENSORS_LM78_TEMP_OVER, SENSORS_LM78_TEMP_HYST, -1 } },
{ fmtVolt_2, SENSORS_LM78_VID,
{ SENSORS_LM78_VID, -1 } },
{ NULL }
};
/** GL518 **/
static char *gl518_names[] = {
SENSORS_GL518_PREFIX, NULL
};
static FeatureDescriptor gl518_features[] = {
{ fmtVolts_2, SENSORS_GL518_VDD,
{ SENSORS_GL518_VDD, SENSORS_GL518_VDD_MIN, SENSORS_GL518_VDD_MAX, -1 } },
{ fmtVolts_2, SENSORS_GL518_VIN1,
{ SENSORS_GL518_VIN1, SENSORS_GL518_VIN1_MIN, SENSORS_GL518_VIN1_MAX, -1 } },
{ fmtVolts_2, SENSORS_GL518_VIN2,
{ SENSORS_GL518_VIN2, SENSORS_GL518_VIN2_MIN, SENSORS_GL518_VIN2_MAX, -1 } },
{ fmtVolts_2, SENSORS_GL518_VIN3,
{ SENSORS_GL518_VIN3, SENSORS_GL518_VIN3_MIN, SENSORS_GL518_VIN3_MAX, -1 } },
{ fmtFans_0, SENSORS_GL518_FAN1,
{ SENSORS_GL518_FAN1, SENSORS_GL518_FAN1_MIN, SENSORS_GL518_FAN1_DIV, -1 } },
{ fmtFans_0, SENSORS_GL518_FAN2,
{ SENSORS_GL518_FAN2, SENSORS_GL518_FAN2_MIN, SENSORS_GL518_FAN2_DIV, -1 } },
{ fmtTemps_0, SENSORS_GL518_TEMP,
{ SENSORS_GL518_TEMP, SENSORS_GL518_TEMP_OVER, SENSORS_GL518_TEMP_HYST, -1 } },
{ NULL }
};
/** LM80 **/
static char *
fmtTemps_LM80
(double values[]) {
sprintf (buff, "%.0f C (hot limit = %.0f C, hot hysteresis = %.0f C, os limit = %.0f C, os hysteresis = %.0f C)", values[0], values[1], values[2], values[3], values[4]);
return buff;
}
static char *lm80_names[] = {
SENSORS_LM80_PREFIX, NULL
};
static FeatureDescriptor lm80_features[] = {
{ fmtVolts_2, SENSORS_LM80_IN0,
{ SENSORS_LM80_IN0, SENSORS_LM80_IN0_MIN, SENSORS_LM80_IN0_MAX, -1 } },
{ fmtVolts_2, SENSORS_LM80_IN1,
{ SENSORS_LM80_IN1, SENSORS_LM80_IN1_MIN, SENSORS_LM80_IN1_MAX, -1 } },
{ fmtVolts_2, SENSORS_LM80_IN2,
{ SENSORS_LM80_IN2, SENSORS_LM80_IN2_MIN, SENSORS_LM80_IN2_MAX, -1 } },
{ fmtVolts_2, SENSORS_LM80_IN3,
{ SENSORS_LM80_IN3, SENSORS_LM80_IN3_MIN, SENSORS_LM80_IN3_MAX, -1 } },
{ fmtVolts_2, SENSORS_LM80_IN4,
{ SENSORS_LM80_IN4, SENSORS_LM80_IN4_MIN, SENSORS_LM80_IN4_MAX, -1 } },
{ fmtVolts_2, SENSORS_LM80_IN5,
{ SENSORS_LM80_IN5, SENSORS_LM80_IN5_MIN, SENSORS_LM80_IN5_MAX, -1 } },
{ fmtVolts_2, SENSORS_LM80_IN6,
{ SENSORS_LM80_IN6, SENSORS_LM80_IN6_MIN, SENSORS_LM80_IN6_MAX, -1 } },
{ fmtFans_0, SENSORS_LM80_FAN1,
{ SENSORS_LM80_FAN1, SENSORS_LM80_FAN1_MIN, SENSORS_LM80_FAN1_DIV, -1 } },
{ fmtFans_0, SENSORS_LM80_FAN2,
{ SENSORS_LM80_FAN2, SENSORS_LM80_FAN2_MIN, SENSORS_LM80_FAN2_DIV, -1 } },
{ fmtTemps_LM80, SENSORS_LM80_TEMP,
{ SENSORS_LM80_TEMP, SENSORS_LM80_TEMP_HOT_MAX, SENSORS_LM80_TEMP_HOT_HYST, SENSORS_LM80_TEMP_OS_MAX, SENSORS_LM80_TEMP_OS_HYST, -1 } },
{ NULL }
};
/** W83781D **/
static char *w83781d_names[] = {
SENSORS_W83781D_PREFIX, NULL
};
static FeatureDescriptor w83781d_features[] = {
{ fmtVolts_2, SENSORS_W83781D_IN0,
{ SENSORS_W83781D_IN0, SENSORS_W83781D_IN0_MIN, SENSORS_W83781D_IN0_MAX, -1 } },
{ fmtVolts_2, SENSORS_W83781D_IN1,
{ SENSORS_W83781D_IN1, SENSORS_W83781D_IN1_MIN, SENSORS_W83781D_IN1_MAX, -1 } },
{ fmtVolts_2, SENSORS_W83781D_IN2,
{ SENSORS_W83781D_IN2, SENSORS_W83781D_IN2_MIN, SENSORS_W83781D_IN2_MAX, -1 } },
{ fmtVolts_2, SENSORS_W83781D_IN3,
{ SENSORS_W83781D_IN3, SENSORS_W83781D_IN3_MIN, SENSORS_W83781D_IN3_MAX, -1 } },
{ fmtVolts_2, SENSORS_W83781D_IN4,
{ SENSORS_W83781D_IN4, SENSORS_W83781D_IN4_MIN, SENSORS_W83781D_IN4_MAX, -1 } },
{ fmtVolts_2, SENSORS_W83781D_IN5,
{ SENSORS_W83781D_IN5, SENSORS_W83781D_IN5_MIN, SENSORS_W83781D_IN5_MAX, -1 } },
{ fmtVolts_2, SENSORS_W83781D_IN6,
{ SENSORS_W83781D_IN6, SENSORS_W83781D_IN6_MIN, SENSORS_W83781D_IN6_MAX, -1 } },
{ fmtFans_0, SENSORS_W83781D_FAN1,
{ SENSORS_W83781D_FAN1, SENSORS_W83781D_FAN1_MIN, SENSORS_W83781D_FAN1_DIV, -1 } },
{ fmtFans_0, SENSORS_W83781D_FAN2,
{ SENSORS_W83781D_FAN2, SENSORS_W83781D_FAN2_MIN, SENSORS_W83781D_FAN2_DIV, -1 } },
{ fmtFans_0, SENSORS_W83781D_FAN3,
{ SENSORS_W83781D_FAN3, SENSORS_W83781D_FAN3_MIN, SENSORS_W83781D_FAN3_DIV, -1 } },
{ fmtTemps_1, SENSORS_W83781D_TEMP1,
{ SENSORS_W83781D_TEMP1, SENSORS_W83781D_TEMP1_OVER, SENSORS_W83781D_TEMP1_HYST, -1 } },
{ fmtTemps_1, SENSORS_W83781D_TEMP2,
{ SENSORS_W83781D_TEMP2, SENSORS_W83781D_TEMP2_OVER, SENSORS_W83781D_TEMP2_HYST, -1 } },
{ fmtTemps_1, SENSORS_W83781D_TEMP3,
{ SENSORS_W83781D_TEMP3, SENSORS_W83781D_TEMP3_OVER, SENSORS_W83781D_TEMP3_HYST, -1 } },
{ fmtVolt_2, SENSORS_W83781D_VID,
{ SENSORS_W83781D_VID, -1 } },
{ NULL }
};
/** W83782D **/
static char *
fmtTemps_W8378x
(double values[]) {
int sensorID = (int) values[3];
char *sensor = (sensorID == 1) ? "PII/Celeron diode" : (sensorID == 2) ? "3904 transistor" : "thermistor"; /* Is this still right? */
sprintf (buff, "%.1f C (limit = %.1f C, hysteresis = %.1f C, sensor = %s)", values[0], values[1], values[2], sensor);
return buff;
}
static char *w83782d_names[] = {
SENSORS_W83782D_PREFIX, SENSORS_W83627HF_PREFIX, SENSORS_AS99127F_PREFIX, NULL
};
static FeatureDescriptor w83782d_features[] = {
{ fmtVolts_2, SENSORS_W83782D_IN0,
{ SENSORS_W83782D_IN0, SENSORS_W83782D_IN0_MIN, SENSORS_W83782D_IN0_MAX, -1 } },
{ fmtVolts_2, SENSORS_W83782D_IN1,
{ SENSORS_W83782D_IN1, SENSORS_W83782D_IN1_MIN, SENSORS_W83782D_IN1_MAX, -1 } },
{ fmtVolts_2, SENSORS_W83782D_IN2,
{ SENSORS_W83782D_IN2, SENSORS_W83782D_IN2_MIN, SENSORS_W83782D_IN2_MAX, -1 } },
{ fmtVolts_2, SENSORS_W83782D_IN3,
{ SENSORS_W83782D_IN3, SENSORS_W83782D_IN3_MIN, SENSORS_W83782D_IN3_MAX, -1 } },
{ fmtVolts_2, SENSORS_W83782D_IN4,
{ SENSORS_W83782D_IN4, SENSORS_W83782D_IN4_MIN, SENSORS_W83782D_IN4_MAX, -1 } },
{ fmtVolts_2, SENSORS_W83782D_IN5,
{ SENSORS_W83782D_IN5, SENSORS_W83782D_IN5_MIN, SENSORS_W83782D_IN5_MAX, -1 } },
{ fmtVolts_2, SENSORS_W83782D_IN7,
{ SENSORS_W83782D_IN7, SENSORS_W83782D_IN7_MIN, SENSORS_W83782D_IN7_MAX, -1 } },
{ fmtVolts_2, SENSORS_W83782D_IN8,
{ SENSORS_W83782D_IN8, SENSORS_W83782D_IN8_MIN, SENSORS_W83782D_IN8_MAX, -1 } },
{ fmtFans_0, SENSORS_W83782D_FAN1,
{ SENSORS_W83782D_FAN1, SENSORS_W83782D_FAN1_MIN, SENSORS_W83782D_FAN1_DIV, -1 } },
{ fmtFans_0, SENSORS_W83782D_FAN2,
{ SENSORS_W83782D_FAN2, SENSORS_W83782D_FAN2_MIN, SENSORS_W83782D_FAN2_DIV, -1 } },
{ fmtFans_0, SENSORS_W83782D_FAN3,
{ SENSORS_W83782D_FAN3, SENSORS_W83782D_FAN3_MIN, SENSORS_W83782D_FAN3_DIV, -1 } },
{ fmtTemps_W8378x, SENSORS_W83782D_TEMP1,
{ SENSORS_W83782D_TEMP1, SENSORS_W83782D_TEMP1_OVER, SENSORS_W83782D_TEMP1_HYST, SENSORS_W83782D_SENS1, -1 } },
{ fmtTemps_W8378x, SENSORS_W83782D_TEMP2,
{ SENSORS_W83782D_TEMP2, SENSORS_W83782D_TEMP2_OVER, SENSORS_W83782D_TEMP2_HYST, SENSORS_W83782D_SENS2, -1 } },
{ fmtTemps_W8378x, SENSORS_W83782D_TEMP3,
{ SENSORS_W83782D_TEMP3, SENSORS_W83782D_TEMP3_OVER, SENSORS_W83782D_TEMP3_HYST, SENSORS_W83782D_SENS3, -1 } },
{ fmtVolt_2, SENSORS_W83782D_VID,
{ SENSORS_W83782D_VID, -1 } },
{ NULL }
};
/** W83783S **/
static char *w83783s_names[] = {
SENSORS_W83783S_PREFIX, NULL
};
static FeatureDescriptor w83783s_features[] = {
{ fmtVolts_2, SENSORS_W83783S_IN0,
{ SENSORS_W83783S_IN0, SENSORS_W83783S_IN0_MIN, SENSORS_W83783S_IN0_MAX, -1 } },
{ fmtVolts_2, SENSORS_W83783S_IN1,
{ SENSORS_W83783S_IN1, SENSORS_W83783S_IN1_MIN, SENSORS_W83783S_IN1_MAX, -1 } },
{ fmtVolts_2, SENSORS_W83783S_IN2,
{ SENSORS_W83783S_IN2, SENSORS_W83783S_IN2_MIN, SENSORS_W83783S_IN2_MAX, -1 } },
{ fmtVolts_2, SENSORS_W83783S_IN3,
{ SENSORS_W83783S_IN3, SENSORS_W83783S_IN3_MIN, SENSORS_W83783S_IN3_MAX, -1 } },
{ fmtVolts_2, SENSORS_W83783S_IN4,
{ SENSORS_W83783S_IN4, SENSORS_W83783S_IN4_MIN, SENSORS_W83783S_IN4_MAX, -1 } },
{ fmtVolts_2, SENSORS_W83783S_IN5,
{ SENSORS_W83783S_IN5, SENSORS_W83783S_IN5_MIN, SENSORS_W83783S_IN5_MAX, -1 } },
{ fmtFans_0, SENSORS_W83783S_FAN1,
{ SENSORS_W83783S_FAN1, SENSORS_W83783S_FAN1_MIN, SENSORS_W83783S_FAN1_DIV, -1 } },
{ fmtFans_0, SENSORS_W83783S_FAN2,
{ SENSORS_W83783S_FAN2, SENSORS_W83783S_FAN2_MIN, SENSORS_W83783S_FAN2_DIV, -1 } },
{ fmtFans_0, SENSORS_W83783S_FAN3,
{ SENSORS_W83783S_FAN3, SENSORS_W83783S_FAN3_MIN, SENSORS_W83783S_FAN3_DIV, -1 } },
{ fmtTemps_W8378x, SENSORS_W83783S_TEMP1,
{ SENSORS_W83783S_TEMP1, SENSORS_W83783S_TEMP1_OVER, SENSORS_W83783S_TEMP1_HYST, SENSORS_W83783S_SENS1, -1 } },
{ fmtTemps_W8378x, SENSORS_W83783S_TEMP2,
{ SENSORS_W83783S_TEMP2, SENSORS_W83783S_TEMP2_OVER, SENSORS_W83783S_TEMP2_HYST, SENSORS_W83783S_SENS2, -1 } },
{ fmtVolt_2, SENSORS_W83783S_VID,
{ SENSORS_W83783S_VID, -1 } },
{ NULL }
};
/** MAXILIFE **/
static char *
fmtFans_Maxilife
(double values[]) {
if (values[0] < 0) {
sprintf (buff, "Off (min = %.0f RPM, div = %.0f)", values[1], values[2]);
} else {
sprintf (buff, "%.0f RPM (min = %.0f RPM, div = %.0f)", values[0] / values[2], values[1] / values[2], values[2]);
}
return buff;
}
static char *maxilife_names[] = {
SENSORS_MAXI_CG_PREFIX, SENSORS_MAXI_CO_PREFIX, SENSORS_MAXI_AS_PREFIX, NULL
};
static FeatureDescriptor maxilife_features[] = {
{ fmtTemps_1, SENSORS_MAXI_CG_TEMP1,
{ SENSORS_MAXI_CG_TEMP1, SENSORS_MAXI_CG_TEMP1_MAX, SENSORS_MAXI_CG_TEMP1_HYST, -1 } },
{ fmtTemps_1, SENSORS_MAXI_CG_TEMP2,
{ SENSORS_MAXI_CG_TEMP2, SENSORS_MAXI_CG_TEMP2_MAX, SENSORS_MAXI_CG_TEMP2_HYST, -1 } },
{ fmtTemps_1, SENSORS_MAXI_CG_TEMP3,
{ SENSORS_MAXI_CG_TEMP3, SENSORS_MAXI_CG_TEMP3_MAX, SENSORS_MAXI_CG_TEMP3_HYST, -1 } },
{ fmtTemps_1, SENSORS_MAXI_CG_TEMP4,
{ SENSORS_MAXI_CG_TEMP4, SENSORS_MAXI_CG_TEMP4_MAX, SENSORS_MAXI_CG_TEMP4_HYST, -1 } },
{ fmtTemps_1, SENSORS_MAXI_CG_TEMP4,
{ SENSORS_MAXI_CG_TEMP5, SENSORS_MAXI_CG_TEMP5_MAX, SENSORS_MAXI_CG_TEMP4_HYST, -1 } },
{ fmtFans_Maxilife, SENSORS_MAXI_CG_FAN1,
{ SENSORS_MAXI_CG_FAN1, SENSORS_MAXI_CG_FAN1_MIN, SENSORS_MAXI_CG_FAN1_DIV, -1 } },
{ fmtFans_Maxilife, SENSORS_MAXI_CG_FAN2,
{ SENSORS_MAXI_CG_FAN2, SENSORS_MAXI_CG_FAN2_MIN, SENSORS_MAXI_CG_FAN2_DIV, -1 } },
{ fmtFans_Maxilife, SENSORS_MAXI_CG_FAN3,
{ SENSORS_MAXI_CG_FAN3, SENSORS_MAXI_CG_FAN3_MIN, SENSORS_MAXI_CG_FAN3_DIV, -1 } },
{ fmtMHz_2, SENSORS_MAXI_CG_PLL,
{ SENSORS_MAXI_CG_PLL, SENSORS_MAXI_CG_PLL_MIN, SENSORS_MAXI_CG_PLL_MAX, -1 } },
{ fmtVolts_2, SENSORS_MAXI_CG_VID1,
{ SENSORS_MAXI_CG_VID1, SENSORS_MAXI_CG_VID1_MIN, SENSORS_MAXI_CG_VID1_MAX, -1 } },
{ fmtVolts_2, SENSORS_MAXI_CG_VID2,
{ SENSORS_MAXI_CG_VID2, SENSORS_MAXI_CG_VID2_MIN, SENSORS_MAXI_CG_VID2_MAX, -1 } },
{ fmtVolts_2, SENSORS_MAXI_CG_VID3,
{ SENSORS_MAXI_CG_VID3, SENSORS_MAXI_CG_VID3_MIN, SENSORS_MAXI_CG_VID3_MAX, -1 } },
{ fmtVolts_2, SENSORS_MAXI_CG_VID4,
{ SENSORS_MAXI_CG_VID4, SENSORS_MAXI_CG_VID4_MIN, SENSORS_MAXI_CG_VID4_MAX, -1 } },
{ NULL }
};
/** ALL **/
ChipDescriptor knownChips[] = {
{ lm75_names, lm75_features },
{ adm1021_names, adm1021_features },
{ max1617_names, max1617_features },
{ adm9240_names, adm9240_features },
{ lm78_names, lm78_features },
{ sis5595_names, sis5595_features },
{ lm80_names, lm80_features },
{ gl518_names, gl518_features },
{ w83781d_names, w83781d_features },
{ w83782d_names, w83782d_features },
{ w83783s_names, w83783s_features },
{ maxilife_names, maxilife_features },
{ NULL }
};
+112
View File
@@ -0,0 +1,112 @@
.\" Copyright 1999 Merlin Hughes <merlin@merlin.org>
.\" sensord is distributed under the GPL
.\"
.\" Permission is granted to make and distribute verbatim copies of this
.\" manual provided the copyright notice and this permission notice are
.\" preserved on all copies.
.\"
.\" Permission is granted to copy and distribute modified versions of this
.\" manual under the conditions for verbatim copying, provided that the
.\" entire resulting derived work is distributed under the terms of a
.\" permission notice identical to this one
.\"
.\" Since the Linux kernel and libraries are constantly changing, this
.\" manual page may be incorrect or out-of-date. The author(s) assume no
.\" responsibility for errors or omissions, or for damages resulting from
.\" the use of the information contained herein. The author(s) may not
.\" have taken the same level of care in the production of this manual,
.\" which is licensed free of charge, as they might when working
.\" professionally.
.\"
.\" Formatted or processed versions of this manual, if unaccompanied by
.\" the source, must acknowledge the copyright and authors of this work.
.\"
.TH sensord 8 "December 2, 1999" "Version 0.2.0" "Linux System Administration"
.SH NAME
sensord \- Sensor information logging daemon.
.SH SYNOPSIS
.B sensord [
.I options
.B ]
.SH DESCRIPTION
.B Sensord
is a daemon that records in a log file the readings of all sensor chips at
an interval of five minutes, or as configured on the command line.
.B Sensord
knows about certain chips, and outputs nicely formatted readings for them; but
it can also display the information of unknown chips, as long as
.I libsensors
knows about them.
.SH OPTIONS
.IP "-c config-file"
Specify a configuration file. If no file is specified, the name `sensors.conf'
is used. If the name does not contain a leading directory slash, or this parameter
is not present, the following paths are examined:
.BR /etc ,
.BR /usr/lib/sensors ,
.BR /usr/local/lib/sensors ,
.BR /usr/lib ,
.BR /usr/local/lib .
.IP "-i interval"
Specifies the interval, in seconds, between sensor readings. The default
is 300 seconds (5 minutes).
.IP "-l log-file"
Specify the log file to which to append. If no file is specified, the
default log file `/var/log/sensors' is created. Note that this file is
interpreted relative to the root directory `/', not the current directory.
.IP -h
Prints a help message and exits.
.IP -v
Displays the program version and exits.
.SH SIGNALS
Upon receipt of a SIGHUP (see
.BR signal (7)
for details) this daemon closes and reopens its log file. Use this to rotate
log-files: Rename the old log file and then HUP this daemon; it will
commence writing to a new log file.
Upon receipt of a SIGTERM (see
.BR signal (7)
for details) this daemon should gracefully shut down.
.SH ERRORS
Errors encountered by this daemon are logged to
.BR syslogd (8)
under the program name `sensord', after which the daemon aborts
with a failure note in its own log file.
.SH NOTES
It is expected that all required modules are loaded prior to this daemon
being started. Typically, this is achieved with an entry in
`/etc/rc.d/...' containing
.BR modprobe (1)
commands to load the required modules before starting the daemon.
.SH BUGS
.B Sensord
does not currently support reading alarm events from sensor devices.
.SH FILES
.I /etc/sensors.conf
.RS
The system wide configuration file. See
.BR sensors.conf (5)
for further details.
.RE
.I /var/log/sensors
.RS
The default sensor log file.
.RE
.SH "CONFORMING TO"
lm_sensors-2.x
.SH SEE ALSO
sensors.conf(5)
.SH AUTHORS
.B Sensord
was written by Merlin Hughes <merlin@merlin.org>. Chip-specific formatting
code was ripped from
.BR sensors
which was written by Frodo Looijaard <frodol@dds.nl>.
+375
View File
@@ -0,0 +1,375 @@
/*
* sensord
*
* A daemon that logs all sensor information to /var/log/sensors
* every 5 minutes.
*
* Copyright (c) 1999 Merlin Hughes <merlin@merlin.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>
#include <errno.h>
#include <dirent.h>
#include <unistd.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include "lib/sensors.h"
#include "sensord.h"
static char *version = "0.2.0";
static char *sensorsCfgPaths[] = {
"/etc", "/usr/lib/sensors", "/usr/local/lib/sensors", "/usr/lib", "/usr/local/lib", NULL
};
static char *sensorsCfgFile = "sensors.conf";
static char *sensorsLogFile = "/var/log/sensors";
static int sleepTime = 5 * 60;
static int cfgLoaded = 0;
time_t cfgLastModified;
static FILE *sensorsLog = NULL;
static volatile sig_atomic_t done = 0;
static volatile sig_atomic_t rotate = 0;
static void
signalHandler
(int sig) {
signal (sig, signalHandler);
switch (sig) {
case SIGHUP:
rotate = 1;
break;
case SIGTERM:
done = 1;
break;
}
}
static void
initSignals
(void) {
/* I should use sigaction but... */
signal (SIGHUP, signalHandler);
signal (SIGTERM, signalHandler);
}
static char *
now
(void) {
time_t now;
char *str;
int len;
static char buff[1024];
time (&now);
str = ctime (&now);
len = strlen (str);
memcpy (buff, str, len - 1);
buff[len - 1] = '\0';
return buff;
}
static int
readUnknownChip
(const sensors_chip_name *chip) {
const sensors_feature_data *sensor;
int index0 = 0, index1 = 0;
int ret = 0;
while (!done && (ret == 0) && ((sensor = sensors_get_all_features (*chip, &index0, &index1)) != NULL)) {
char *label = NULL;
double value;
if (sensors_get_label (*chip, sensor->number, &label)) {
syslog (LOG_ERR, "Error getting sensor label: %s/%s", chip->prefix, sensor->name);
ret = 20;
} else if (!(sensor->mode & SENSORS_MODE_R)) {
fprintf (sensorsLog, "\t%s: %s\n", sensor->name, label);
} else if (sensors_get_feature (*chip, sensor->number, &value)) {
syslog (LOG_ERR, "Error getting sensor data: %s/%s", chip->prefix, sensor->name);
ret = 21;
} else {
fprintf (sensorsLog, "\t%s%s: %s: %.2f\n", (sensor->mapping == SENSORS_NO_MAPPING) ? "" : "-", sensor->name, label, value);
}
if (label)
free (label);
}
return ret;
}
static int
readKnownChip
(const sensors_chip_name *chip, ChipDescriptor *descriptor) {
FeatureDescriptor *features = descriptor->features;
int index, subindex;
int ret = 0;
for (index = 0; !done && (ret == 0) && features[index].format; ++ index) {
FeatureDescriptor *feature = features + index;
char *label = NULL;
double values[MAX_DATA];
if (sensors_get_label (*chip, feature->labelNumber, &label)) {
syslog (LOG_ERR, "Error getting sensor label: %s/#%d", chip->prefix, feature->labelNumber);
ret = 20;
}
for (subindex = 0; !done && (ret == 0) && (feature->dataNumbers[subindex] >= 0); ++ subindex) {
if (sensors_get_feature (*chip, feature->dataNumbers[subindex], values + subindex)) {
syslog (LOG_ERR, "Error getting sensor data: %s/#%d", chip->prefix, feature->dataNumbers[subindex]);
ret = 21;
}
}
if (!done && (ret == 0))
fprintf (sensorsLog, "\t%s: %s\n"/*, sensor->name*/, label, feature->format (values));
if (label)
free (label);
}
}
static int
readChip
(const sensors_chip_name *chip) {
const char *adapter, *algorithm;
int index, subindex, chipindex = -1;
int ret = 0;
if (chip->bus == SENSORS_CHIP_NAME_BUS_ISA)
fprintf (sensorsLog, "%s: Chip: %s-isa-%04x\n", now (), chip->prefix, chip->addr);
else
fprintf (sensorsLog, "%s: Chip: %s-i2c-%d-%02x\n", now (), chip->prefix, chip->bus, chip->addr);
adapter = sensors_get_adapter_name (chip->bus);
if (adapter)
fprintf (sensorsLog, "%s: Adapter: %s\n", now (), adapter);
algorithm = sensors_get_algorithm_name (chip->bus);
if (algorithm)
fprintf (sensorsLog, "%s: Algorithm: %s\n", now (), algorithm);
/* assert adapter || algorithm */
for (index = 0; knownChips[index].names; ++ index)
for (subindex = 0; knownChips[index].names[subindex]; ++ subindex)
if (!strcmp (chip->prefix, knownChips[index].names[subindex]))
chipindex = index;
if (chipindex >= 0)
ret = readKnownChip (chip, knownChips + chipindex);
else
ret = readUnknownChip (chip);
return ret;
}
static int
readChips
(void) {
const sensors_chip_name *chip;
int index = 0;
int ret = 0;
fprintf (sensorsLog, "%s: Sense.\n", now ());
fflush (sensorsLog);
while (!done && (ret == 0) && ((chip = sensors_get_detected_chips (&index)) != NULL)) {
ret = readChip (chip);
}
fprintf (sensorsLog, "%s: Done.\n", now ());
fflush (sensorsLog);
return ret;
}
static int
rotateLog
(void) {
int ret = 0;
if (sensorsLog != NULL) {
fprintf (sensorsLog, "%s: Rotate.\n", now ());
fclose (sensorsLog);
}
if (!(sensorsLog = fopen (sensorsLogFile, "a"))) {
syslog (LOG_ERR, "Error opening sensors log: %s", sensorsLogFile);
ret = 1;
} else {
fprintf (sensorsLog, "%s: Started.\n", now ());
fflush (sensorsLog);
}
rotate = 0;
return ret;
}
char cfgPath[4096] = "";
static int
initSensors
(void) {
struct stat stats;
FILE *cfg = NULL;
int ret = 0;
if (!cfgPath[0]) {
if (sensorsCfgFile[0] == '/') {
strcpy (cfgPath, sensorsCfgFile);
} else {
int index;
for (index = 0; sensorsCfgPaths[index]; ++ index) {
sprintf (cfgPath, "%s/%s", sensorsCfgPaths[index], sensorsCfgFile);
if (stat (cfgPath, &stats) == 0)
break;
}
if (!sensorsCfgPaths[index]) {
syslog (LOG_ERR, "Error locating sensors configuration: %s", sensorsCfgFile);
return 9;
}
}
}
if (stat (cfgPath, &stats) < 0) {
syslog (LOG_ERR, "Error stating sensors configuration: %s", cfgPath);
ret = 10;
} else if (!cfgLoaded || (difftime (stats.st_mtime, cfgLastModified) > 0.0)) {
if (!(cfg = fopen (cfgPath, "r"))) {
syslog (LOG_ERR, "Error opening sensors configuration: %s", cfgPath);
ret = 11;
} else if (sensors_init (cfg)) {
syslog (LOG_ERR, "Error loading sensors configuration: %s", cfgPath);
ret = 11;
} else {
cfgLastModified = stats.st_mtime;
cfgLoaded = 1;
}
if (cfg)
fclose (cfg);
}
return ret;
}
static int
sensord
(void) {
int ret = 0;
openlog ("sensord", 0, LOG_DAEMON);
initSignals ();
while (!done && (ret == 0)) {
if (rotate || (sensorsLog == NULL))
ret = rotateLog ();
if (ret == 0)
ret = initSensors ();
if (ret == 0)
ret = readChips ();
if (!done && !rotate && (ret == 0))
sleep (sleepTime);
}
if (cfgLoaded)
sensors_cleanup ();
if (sensorsLog) {
fprintf (sensorsLog, "%s: %s.\n", now (), ret ? "Failed" : "Stopped");
fclose (sensorsLog);
}
closelog ();
return ret;
}
static void
syntaxError
(void) {
printf ("Syntax: sensord [options]\n"
"\t-i <interval> -- seconds between samples (default 300)\n"
"\t-l <log-file> -- log file (default /var/log/sensors)\n"
"\t-c <config-file> -- configuration file (default /etc/sensors.conf)\n"
"\t-v -- display version and exit\n"
"\t-h -- display help and exit\n");
exit (EXIT_FAILURE);
}
static void
parseArgs
(int argc, char **argv) {
int i;
for (i = 1; i < argc; ++ i) {
if (!strcmp (argv[i], "-i") && (i < argc - 1)) {
sleepTime = atoi (argv[++ i]);
} else if (!strcmp (argv[i], "-l") && (i < argc - 1)) {
sensorsLogFile = argv[++ i];
} else if (!strcmp (argv[i], "-c") && (i < argc - 1)) {
sensorsCfgFile = argv[++ i];
} else if (!strcmp (argv[i], "-v")) {
printf ("sensord version %s\n", version);
exit (EXIT_SUCCESS);
} else {
syntaxError ();
}
}
}
static void
daemonize
(void) {
int pid;
if ((pid = fork ()) == -1) {
fprintf (stderr, "Error forking daemon\n");
exit (EXIT_FAILURE);
} else if (pid != 0) {
exit (EXIT_SUCCESS);
}
if (chdir ("/") < 0) {
perror ("chdir()");
exit (EXIT_FAILURE);
}
if (setsid () < 0) {
perror ("setsid()");
exit (EXIT_FAILURE);
}
close (STDIN_FILENO);
close (STDOUT_FILENO);
close (STDERR_FILENO);
}
int
main
(int argc, char **argv) {
parseArgs (argc, argv);
daemonize ();
return sensord ();
}
+39
View File
@@ -0,0 +1,39 @@
/*
* sensord
*
* A daemon that logs all sensor information to /var/log/sensors
* every 5 minutes.
*
* Copyright (c) 1999 Merlin Hughes <merlin@merlin.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#define MAX_DATA 5
typedef char * (*FormatterFN) (double[]);
typedef struct {
FormatterFN format;
int labelNumber;
int dataNumbers[MAX_DATA + 1];
} FeatureDescriptor;
typedef struct {
char **names;
FeatureDescriptor *features;
} ChipDescriptor;
extern ChipDescriptor knownChips[];