root/lm-sensors/trunk/prog/sensord/rrd.c @ 4808

Revision 4808, 12.1 KB (checked in by khali, 7 years ago)

Warning fixes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/*
2 * sensord
3 *
4 * A daemon that periodically logs sensor information to syslog.
5 *
6 * Copyright (c) 1999-2002 Merlin Hughes <merlin@merlin.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23/*
24 * RRD is the Round Robin Database
25 *
26 * Get this package from:
27 *   http://people.ee.ethz.ch/~oetiker/webtools/rrdtool/
28 *
29 * For compilation you need the development libraries;
30 * for execution you need the runtime libraries; for
31 * Web-based graph access you need the binary rrdtool.
32 */
33
34#include <errno.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <string.h>
38#include <syslog.h>
39#include <unistd.h>
40#include <sys/stat.h>
41#include <sys/types.h>
42
43#include <getopt.h>
44#include <rrd.h>
45
46#include "sensord.h"
47#include "lib/error.h"
48
49#define DO_READ 0
50#define DO_SCAN 1
51#define DO_SET 2
52#define DO_RRD 3
53
54/* one integer */
55#define STEP_BUFF 64
56/* RRA:AVERAGE:0.5:1:12345 */
57#define RRA_BUFF 256
58/* weak: max sensors for RRD .. TODO: fix */
59#define MAX_RRD_SENSORS 256
60/* weak: max raw label length .. TODO: fix */
61#define RAW_LABEL_LENGTH 32
62/* DS:label:GAUGE:900:U:U | :3000 .. TODO: fix */
63#define RRD_BUFF 64
64
65char rrdBuff[MAX_RRD_SENSORS * RRD_BUFF + 1];
66static char rrdLabels[MAX_RRD_SENSORS][RAW_LABEL_LENGTH + 1];
67
68#define LOADAVG "loadavg"
69#define LOAD_AVERAGE "Load Average"
70
71typedef int (*FeatureFN) (void *data, const char *rawLabel, const char *label, const FeatureDescriptor *feature);
72
73static char
74rrdNextChar
75(char c) {
76  if (c == '9') {
77    return 'A';
78  } else if (c == 'Z') {
79    return 'a';
80  } else if (c == 'z') {
81    return 0;
82  } else {
83    return c + 1;
84  }
85}
86
87static void
88rrdCheckLabel
89(const char *rawLabel, int index0) {
90  char *buffer = rrdLabels[index0];
91  int i, j, okay;
92 
93  i = 0;
94  while ((i < RAW_LABEL_LENGTH) && rawLabel[i]) { /* contrain raw label to [A-Za-z0-9_] */
95    char c = rawLabel[i];
96    if (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) || ((c >= '0') && (c <= '9')) || (c == '_')) {
97      buffer[i] = c;
98    } else {
99      buffer[i] = '_';
100    }
101    ++ i;
102  }
103  buffer[i] = '\0';
104
105  j = 0;
106  okay = (i > 0);
107  while (okay && (j < index0)) /* locate duplicates */
108    okay = strcmp (rrdLabels[j ++], buffer);
109
110  while (!okay) { /* uniquify duplicate labels with _? or _?? */
111    if (!buffer[i]) {
112      if (i > RAW_LABEL_LENGTH - 3)
113        i = RAW_LABEL_LENGTH - 3;
114      buffer[i] = '_';
115      buffer[i + 1] = '0';
116      buffer[i + 2] = '\0';
117    } else if (!buffer[i + 2]) {
118      if (!(buffer[i + 1] = rrdNextChar (buffer[i + 1]))) {
119        buffer[i + 1] = '0';
120        buffer[i + 2] = '0';
121        buffer[i + 3] = '\0';
122      }
123    } else {
124      if (!(buffer[i + 2] = rrdNextChar (buffer[i + 2]))) {
125        buffer[i + 1] = rrdNextChar (buffer[i + 1]);
126        buffer[i + 2] = '0';
127      }
128    }
129    j = 0;
130    okay = 1;
131    while (okay && (j < index0))
132      okay = strcmp (rrdLabels[j ++], buffer);
133  }
134}
135
136static int
137applyToFeatures
138(FeatureFN fn, void *data) {
139  const sensors_chip_name *chip;
140  int i = 0, j, ret = 0, num = 0;
141
142  while ((ret == 0) && ((chip = sensors_get_detected_chips (&i)) != NULL)) {
143    for (j = 0; (ret == 0) && (j < numChipNames); ++ j) {
144      if (sensors_match_chip (*chip, chipNames[j])) {
145        int index0, subindex, chipindex = -1;
146        for (index0 = 0; knownChips[index0]; ++ index0)
147          for (subindex = 0; knownChips[index0]->names[subindex]; ++ subindex)
148            if (!strcmp (chip->prefix, knownChips[index0]->names[subindex]))
149              chipindex = index0;
150        if (chipindex >= 0) {
151          const ChipDescriptor *descriptor = knownChips[chipindex];
152          const FeatureDescriptor *features = descriptor->features;
153
154          for (index0 = 0; (ret == 0) && (num < MAX_RRD_SENSORS) && features[index0].format; ++ index0) {
155            const FeatureDescriptor *feature = features + index0;
156            int labelNumber = feature->dataNumbers[0];
157            const char *rawLabel = NULL;
158            char *label = NULL;
159            int valid = 0;
160            if (getValid (*chip, labelNumber, &valid)) {
161              sensorLog (LOG_ERR, "Error getting sensor validity: %s/#%d", chip->prefix, labelNumber);
162              ret = -1;
163            } else if (getRawLabel (*chip, labelNumber, &rawLabel)) {
164              sensorLog (LOG_ERR, "Error getting raw sensor label: %s/#%d", chip->prefix, labelNumber);
165              ret = -1;
166            } else if (getLabel (*chip, labelNumber, &label)) {
167              sensorLog (LOG_ERR, "Error getting sensor label: %s/#%d", chip->prefix, labelNumber);
168              ret = -1;
169            } else if (valid) {
170              rrdCheckLabel (rawLabel, num);
171              ret = fn (data, rrdLabels[num], label, feature);
172              ++ num;
173            }
174            if (label)
175              free (label);
176          }
177        }
178      }
179    }
180  }
181
182  return ret;
183}
184
185struct ds {
186  int num;
187  const char **argv;
188};
189
190static int
191rrdGetSensors_DS
192(void *_data, const char *rawLabel, const char *label, const FeatureDescriptor *feature) {
193  if (!feature || feature->rrd) {
194    struct ds *data = (struct ds *) _data;
195    char *ptr = rrdBuff + data->num * RRD_BUFF;
196    const char *min, *max;
197    data->argv[data->num ++] = ptr;
198    switch (feature ? feature->type : DataType_other) { /* arbitrary sanity limits */
199      case DataType_voltage:
200        min="-25";
201        max="25";
202        break;
203      case DataType_rpm:
204        min = "0";
205        max = "12000";
206        break;
207      case DataType_temperature:
208        min = "0";
209        max = "250";
210        break;
211      case DataType_mhz:
212        min = "0";
213        max = "U";
214        break;
215      default:
216        min = max = "U";
217        break;
218    }
219    sprintf (ptr, "DS:%s:GAUGE:%d:%s:%s", rawLabel, /* number of seconds downtime during which average be used instead of unknown */ 5 * rrdTime, min, max);
220  }
221  return 0;
222}
223
224static int
225rrdGetSensors
226(const char **argv) {
227  int ret = 0;
228  struct ds data = { 0, argv};
229  ret = applyToFeatures (rrdGetSensors_DS, &data);
230  if (!ret && doLoad)
231    ret = rrdGetSensors_DS (&data, LOADAVG, LOAD_AVERAGE, NULL);
232  return ret ? -1 : data.num;
233}
234
235int
236rrdInit
237(void) {
238  int ret = 0;
239  struct stat tmp;
240 
241  sensorLog (LOG_DEBUG, "sensor RRD init"); 
242  if (stat (rrdFile, &tmp)) {
243    if (errno == ENOENT) {
244      char stepBuff[STEP_BUFF], rraBuff[RRA_BUFF];
245      int argc = 4, num;
246      const char *argv[6 + MAX_RRD_SENSORS] = {
247        "sensord", rrdFile, "-s", stepBuff
248      };
249     
250      sensorLog (LOG_INFO, "creating round robin database");
251      num = rrdGetSensors (argv + argc);
252      if (num == 0) {
253        sensorLog (LOG_ERR, "Error creating RRD: %s: %s", rrdFile, "No sensors detected");
254        ret = 2;
255      } else if (num < 0) {
256        ret = -num;
257      } else {
258        sprintf (stepBuff, "%d", rrdTime);
259        sprintf (rraBuff, "RRA:%s:%f:%d:%d", rrdNoAverage?"LAST":"AVERAGE", 0.5 /* fraction of non-unknown samples needed per entry */, 1 /* samples per entry */, 7 * 24 * 60 * 60 / rrdTime /* 1 week */);
260        argc += num;
261        argv[argc ++] = rraBuff;
262        argv[argc] = NULL;
263        optind = 1;
264        opterr = 0;
265        optopt = '?';
266        optarg = NULL;
267        if ((ret = rrd_create (argc, (char **) /* WEAK */ argv))) {
268          sensorLog (LOG_ERR, "Error creating RRD file: %s: %s", rrdFile, rrd_get_error ());
269        }
270      }
271    } else {
272      sensorLog (LOG_ERR, "Error stat()ing RRD: %s: %s", rrdFile, strerror (errno));
273      ret = 1;
274    }
275  }
276  sensorLog (LOG_DEBUG, "sensor RRD inited"); 
277 
278  return ret;
279}
280
281#define RRDCGI "/usr/bin/rrdcgi"
282#define WWWDIR "/sensord"
283
284struct gr {
285  DataType type;
286  const char *h2;
287  const char *image;
288  const char *title;
289  const char *axisTitle;
290  const char *axisDefn;
291  const char *options;
292  int loadAvg;
293};
294
295static int
296rrdCGI_DEF
297(void *_data, const char *rawLabel, const char *label, const FeatureDescriptor *feature) {
298  struct gr *data = (struct gr *) _data;
299  if (!feature || (feature->rrd && (feature->type == data->type)))
300    printf ("\n\tDEF:%s=%s:%s:AVERAGE", rawLabel, rrdFile, rawLabel);
301  return 0;
302}
303
304static int
305rrdCGI_LINE
306(void *_data, const char *rawLabel, const char *label, const FeatureDescriptor *feature) {
307  struct gr *data = (struct gr *) _data;
308  if (!feature || (feature->rrd && (feature->type == data->type)))
309    printf ("\n\tLINE2:%s#%.6x:\"%s\"", rawLabel, (int) random () & 0xffffff, label);
310  return 0;
311}
312
313static struct gr graphs[] = {
314  {
315    DataType_temperature,
316    "Daily Temperature Summary",
317    "daily-temperature",
318    "Temperature",
319    "Temperature (C)",
320    "HOUR:1:HOUR:3:HOUR:3:0:%b %d %H:00",
321    "-s -1d -l 0",
322    1
323  }, {
324    DataType_rpm,
325    "Daily Fan Speed Summary",
326    "daily-rpm",
327    "Fan Speed",
328    "Speed (RPM)",
329    "HOUR:1:HOUR:3:HOUR:3:0:%b %d %H:00",
330    "-s -1d -l 0",
331    0
332  }, {
333    DataType_voltage,
334    "Daily Voltage Summary",
335    "daily-voltage",
336    "Power Supply",
337    "Voltage (V)",
338    "HOUR:1:HOUR:3:HOUR:3:0:%b %d %H:00",
339    "-s -1d --alt-autoscale",
340    0
341  }, {
342    DataType_temperature,
343    "Weekly Temperature Summary",
344    "weekly-temperature",
345    "Temperature",
346    "Temperature (C)",
347    "HOUR:6:DAY:1:DAY:1:86400:%a %b %d",
348    "-s -1w -l 0",
349    1
350  }, {
351    DataType_rpm,
352    "Weekly Fan Speed Summary",
353    "weekly-rpm",
354    "Fan Speed",
355    "Speed (RPM)",
356    "HOUR:6:DAY:1:DAY:1:86400:%a %b %d",
357    "-s -1w -l 0",
358    0
359  }, {
360    DataType_voltage,
361    "Weekly Voltage Summary",
362    "weekly-voltage",
363    "Power Supply",
364    "Voltage (V)",
365    "HOUR:6:DAY:1:DAY:1:86400:%a %b %d",
366    "-s -1w --alt-autoscale",
367    0
368  }, {
369    DataType_other,
370    NULL,
371    NULL,
372    NULL,
373    NULL,
374    NULL,
375    NULL,
376    0
377  } 
378};
379
380int
381rrdUpdate
382(void) {
383  int ret = rrdChips ();
384  if (!ret && doLoad) {
385    FILE *loadavg;
386    if (!(loadavg = fopen ("/proc/loadavg", "r"))) {
387      sensorLog (LOG_ERR, "Error opening `/proc/loadavg': %s", strerror (errno));
388      ret = 1;
389    } else {
390      float value;
391      if (fscanf (loadavg, "%f", &value) != 1) {
392        sensorLog (LOG_ERR, "Error reading load average");
393        ret = 2;
394      } else {
395        sprintf (rrdBuff + strlen (rrdBuff), ":%f", value);
396      }
397      fclose (loadavg);
398    }
399  }
400  if (!ret) {
401    const char *argv[] = {
402      "sensord", rrdFile, rrdBuff, NULL
403    };
404    optind = 1;
405    opterr = 0;
406    optopt = '?';
407    optarg = NULL;
408    if ((ret = rrd_update (3, (char **) /* WEAK */ argv))) {
409      sensorLog (LOG_ERR, "Error updating RRD file: %s: %s", rrdFile, rrd_get_error ());
410    }
411  }
412  sensorLog (LOG_DEBUG, "sensor rrd updated"); 
413 
414  return ret;
415}
416
417int
418rrdCGI
419(void) {
420  int ret = 0;
421  struct gr *graph = graphs;
422
423  printf ("#!" RRDCGI "\n\n<HTML>\n<HEAD>\n<TITLE>sensord</TITLE>\n</HEAD>\n<BODY>\n<H1>sensord</H1>\n");
424  while (graph->type != DataType_other) {
425    printf ("<H2>%s</H2>\n", graph->h2);
426    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);
427    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);
428    if (!ret)
429      ret = applyToFeatures (rrdCGI_DEF, graph);
430    if (!ret && doLoad && graph->loadAvg)
431      ret = rrdCGI_DEF (graph, LOADAVG, LOAD_AVERAGE, NULL);
432    if (!ret)
433      ret = applyToFeatures (rrdCGI_LINE, graph);
434    if (!ret && doLoad && graph->loadAvg)
435      ret = rrdCGI_LINE (graph, LOADAVG, LOAD_AVERAGE, NULL);
436    printf (">\n</P>\n");
437    ++ graph;
438  }
439  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.org/\">lm_sensors</a> crew.</small>\n</p>\n");
440  printf ("</BODY>\n</HTML>\n");
441 
442  return ret;
443}
Note: See TracBrowser for help on using the browser.