Files
lm-sensors/prog/sensord/rrd.c
T
Merlin Hughes da09ec0eba Added RRD support to sensord; can automatically create
/configure database based on detected/configured sensors,
logs libsensors-processed readings to the database, has a
command-line option for generating a basic rrdcgi script.
Removed sensord from all target because it has an external
dependency on librrd.

This just makes using RRD with sensors easier; you don't
need to figure out what sensors are available and configure
the database with computations that are already in
sensors.conf.

To install:
make install-prog-sensord

To create and start logging to database:
sensord --rrd-file /var/log/sensord.rrd

To create a CGI script:
sensord --rrd-file /var/log/sensord.rrd \
  --rrd-cgi /var/www/sensord \
  > /usr/lib/cgi-bin/sensord.cgi

man sensord for more details.

Kudos to Mark for doing the hard work ;}

-- merlin


git-svn-id: http://lm-sensors.org/svn/lm-sensors/trunk@1357 7894878c-1315-0410-8ee3-d5d059ff63e0
2002-04-28 00:21:49 +00:00

316 lines
8.9 KiB
C

/*
* sensord
*
* A daemon that periodically logs sensor information to syslog.
*
* Copyright (c) 1999-2002 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.
*/
/*
* RRD is the Round Robin Database
*
* Get this package from:
* http://ee-staff.ethz.ch/~oetiker/webtools/rrdtool
*
* For compilation you need the development libraries;
* for execution you need the runtime libraries; for
* Web-based graph access you need the binary rrdtool.
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <getopt.h>
#include <rrd.h>
#include "sensord.h"
#include "lib/error.h"
#define DO_READ 0
#define DO_SCAN 1
#define DO_SET 2
#define DO_RRD 3
/* one integer */
#define STEP_BUFF 64
/* RRA:AVERAGE:0.5:1:12345 */
#define RRA_BUFF 256
/* weak: max sensors for RRD .. TODO: fix */
#define MAX_RRD_SENSORS 256
/* DS:label:GAUGE:900:U:U | :3000 */
#define RRD_BUFF 64
char rrdBuff[MAX_RRD_SENSORS * RRD_BUFF];
typedef int (*FeatureFN) (void *data, const char *rawLabel, char *label, const FeatureDescriptor *feature);
static int
applyToFeatures
(FeatureFN fn, void *data) {
const sensors_chip_name *chip;
int i = 0, j, ret = 0, num = 0;
while ((ret == 0) && ((chip = sensors_get_detected_chips (&i)) != NULL)) {
for (j = 0; (ret == 0) && (j < numChipNames); ++ j) {
if (sensors_match_chip (*chip, chipNames[j])) {
int index0, subindex, chipindex = -1;
for (index0 = 0; knownChips[index0]; ++ index0)
for (subindex = 0; knownChips[index0]->names[subindex]; ++ subindex)
if (!strcmp (chip->prefix, knownChips[index0]->names[subindex]))
chipindex = index0;
if (chipindex >= 0) {
const ChipDescriptor *descriptor = knownChips[chipindex];
const FeatureDescriptor *features = descriptor->features;
for (index0 = 0; (ret == 0) && features[index0].format; ++ index0) {
const FeatureDescriptor *feature = features + index0;
int labelNumber = feature->dataNumbers[0];
const char *rawLabel = NULL;
char *label = NULL;
int valid = 0;
if (getValid (*chip, labelNumber, &valid)) {
sensorLog (LOG_ERR, "Error getting sensor validity: %s/#%d", chip->prefix, labelNumber);
ret = -1;
} else if (getRawLabel (*chip, labelNumber, &rawLabel)) {
sensorLog (LOG_ERR, "Error getting raw sensor label: %s/#%d", chip->prefix, labelNumber);
ret = -1;
} else if (getLabel (*chip, labelNumber, &label)) {
sensorLog (LOG_ERR, "Error getting sensor label: %s/#%d", chip->prefix, labelNumber);
ret = -1;
} else if (valid) {
ret = fn (data, rawLabel, label, feature);
}
if (label)
free (label);
}
}
}
}
}
return ret;
}
struct ds {
int num;
const char **argv;
};
static int
rrdGetSensors_DS
(void *_data, const char *rawLabel, char *label, const FeatureDescriptor *feature) {
if (feature->rrd) {
struct ds *data = (struct ds *) _data;
char *ptr = rrdBuff + data->num * RRD_BUFF;
data->argv[data->num ++] = ptr;
sprintf (ptr, "DS:%s:GAUGE:%d:U:U", rawLabel, /* number of seconds downtime during which average be used instead of unknown */ 5 * rrdTime);
}
return 0;
}
static int
rrdGetSensors
(const char **argv) {
int ret = 0;
struct ds data = { 0, argv};
ret = applyToFeatures (rrdGetSensors_DS, &data);
return ret ? -1 : data.num;
}
int
rrdInit
(void) {
int ret = 0;
struct stat tmp;
sensorLog (LOG_DEBUG, "sensor RRD init");
if (stat (rrdFile, &tmp)) {
if (errno == ENOENT) {
char stepBuff[STEP_BUFF], rraBuff[RRA_BUFF];
int argc = 4, num;
const char *argv[6 + MAX_RRD_SENSORS] = {
"sensord", rrdFile, "-s", stepBuff
};
sensorLog (LOG_INFO, "creating round robin database");
num = rrdGetSensors (argv + argc);
if (num == 0) {
sensorLog (LOG_ERR, "Error creating RRD: %s: %s", rrdFile, "No sensors detected");
ret = 2;
} else if (num < 0) {
ret = -num;
} else {
sprintf (stepBuff, "%d", rrdTime);
sprintf (rraBuff, "RRA:AVERAGE:%f:%d:%d", 0.5 /* fraction of non-unknown samples needed per entry */, 1 /* samples per entry */, 7 * 24 * 60 * 60 / rrdTime /* 1 week */);
argc += num;
argv[argc ++] = rraBuff;
argv[argc] = NULL;
optind = 1;
opterr = 0;
optopt = '?';
optarg = NULL;
if ((ret = rrd_create (argc, (char **) /* WEAK */ argv))) {
sensorLog (LOG_ERR, "Error creating RRD file: %s: %s", rrdFile, rrd_get_error ());
}
}
} else {
sensorLog (LOG_ERR, "Error stat()ing RRD: %s: %s", rrdFile, strerror (errno));
ret = 1;
}
}
sensorLog (LOG_DEBUG, "sensor RRD inited");
return ret;
}
#define RRDCGI "/usr/bin/rrdcgi"
#define WWWDIR "/sensord"
struct gr {
DataType type;
char *h2;
char *image;
char *title;
char *axisTitle;
char *axisDefn;
char *options;
};
static int
rrdCGI_DEF
(void *_data, const char *rawLabel, char *label, const FeatureDescriptor *feature) {
struct gr *data = (struct gr *) _data;
if (feature->rrd && (feature->type == data->type))
printf ("\n\tDEF:%s=%s:%s:AVERAGE", rawLabel, rrdFile, rawLabel);
return 0;
}
static int
rrdCGI_LINE
(void *_data, const char *rawLabel, char *label, const FeatureDescriptor *feature) {
struct gr *data = (struct gr *) _data;
if (feature->rrd && (feature->type == data->type))
printf ("\n\tLINE2:%s#%.6x:\"%s\"", rawLabel, random () & 0xffffff, label);
return 0;
}
static struct gr graphs[] = {
{
DataType_temperature,
"Daily Temperature Summary",
"daily-temperature",
"Temperature",
"Temperature (C)",
"HOUR:1:HOUR:3:HOUR:3:0:%b %d %H:00",
"-s -1d -l 0"
}, {
DataType_rpm,
"Daily Fan Speed Summary",
"daily-rpm",
"Fan Speed",
"Speed (RPM)",
"HOUR:1:HOUR:3:HOUR:3:0:%b %d %H:00",
"-s -1d -l 0"
}, {
DataType_voltage,
"Daily Voltage Summary",
"daily-voltage",
"Power Supply",
"Voltage (V)",
"HOUR:1:HOUR:3:HOUR:3:0:%b %d %H:00",
"-s -1d --alt-autoscale"
}, {
DataType_temperature,
"Weekly Temperature Summary",
"weekly-temperature",
"Temperature",
"Temperature (C)",
"HOUR:6:DAY:1:DAY:1:86400:%a %b %d",
"-s -1w -l 0"
}, {
DataType_rpm,
"Weekly Fan Speed Summary",
"weekly-rpm",
"Fan Speed",
"Speed (RPM)",
"HOUR:6:DAY:1:DAY:1:86400:%a %b %d",
"-s -1w -l 0"
}, {
DataType_voltage,
"Weekly Voltage Summary",
"weekly-voltage",
"Power Supply",
"Voltage (V)",
"HOUR:6:DAY:1:DAY:1:86400:%a %b %d",
"-s -1w --alt-autoscale"
}, {
DataType_other
}
};
int
rrdUpdate
(void) {
int ret = rrdChips ();
if (!ret) {
const char *argv[] = {
"sensord", rrdFile, rrdBuff, NULL
};
optind = 1;
opterr = 0;
optopt = '?';
optarg = NULL;
if ((ret = rrd_update (3, (char **) /* WEAK */ argv))) {
sensorLog (LOG_ERR, "Error updating RRD file: %s: %s", rrdFile, rrd_get_error ());
}
}
sensorLog (LOG_DEBUG, "sensor rrd updated");
return ret;
}
int
rrdCGI
(void) {
int ret = 0;
struct gr *graph = graphs;
printf ("#!" RRDCGI "\n\n<HTML>\n<HEAD>\n<TITLE>sensord</TITLE>\n</HEAD>\n<BODY>\n<H1>sensord</H1>\n");
while (graph->type != DataType_other) {
printf ("<H2>%s</H2>\n", graph->h2);
printf ("<P>\n<RRD::GRAPH %s/%s.png\n\t--imginfo '<IMG SRC=" WWWDIR "/%%s WIDTH=%%lu HEIGHT=%%lu>'\n\t-a PNG\n\t-h 200 -w 800\n", cgiDir, graph->image);
printf ("\t--lazy\n\t-v '%s'\n\t-t '%s'\n\t-x '%s'\n\t%s", graph->axisTitle, graph->title, graph->axisDefn, graph->options);
if (!ret)
ret = applyToFeatures (rrdCGI_DEF, graph);
if (!ret)
ret = applyToFeatures (rrdCGI_LINE, graph);
printf (">\n</P>\n");
++ graph;
}
printf ("<p>\n<small><b>sensord</b> by <a href=\"mailto:merlin@merlin.org\">Merlin Hughes</a>, all credit to the <a href=\"http://www.lm-sensors.nu/\">lm_sensors</a> crew.</small>\n</p>\n");
printf ("</BODY>\n</HTML>\n");
return ret;
}