root/lm-sensors/branches/lm-sensors-3.0.0/prog/sensord/rrd.c @ 5163

Revision 5163, 12.2 KB (checked in by khali, 6 years ago)

Patch from Aurelien Jarno:

I have just noticed that the FSF address is the old one in all files
except COPYING. Please find a patch below to fix that.

  • 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., 51 Franklin Street, Fifth Floor, Boston,
21 * MA 02110-1301 USA.
22 */
23
24/*
25 * RRD is the Round Robin Database
26 *
27 * Get this package from:
28 *   http://people.ee.ethz.ch/~oetiker/webtools/rrdtool/
29 *
30 * For compilation you need the development libraries;
31 * for execution you need the runtime libraries; for
32 * Web-based graph access you need the binary rrdtool.
33 */
34
35#include <errno.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <syslog.h>
40#include <unistd.h>
41#include <sys/stat.h>
42#include <sys/types.h>
43
44#include <rrd.h>
45
46#include "sensord.h"
47
48#define DO_READ 0
49#define DO_SCAN 1
50#define DO_SET 2
51#define DO_RRD 3
52
53/* one integer */
54#define STEP_BUFF 64
55/* RRA:AVERAGE:0.5:1:12345 */
56#define RRA_BUFF 256
57/* weak: max sensors for RRD .. TODO: fix */
58#define MAX_RRD_SENSORS 256
59/* weak: max raw label length .. TODO: fix */
60#define RAW_LABEL_LENGTH 32
61/* DS:label:GAUGE:900:U:U | :3000 .. TODO: fix */
62#define RRD_BUFF 64
63
64char rrdBuff[MAX_RRD_SENSORS * RRD_BUFF + 1];
65static char rrdLabels[MAX_RRD_SENSORS][RAW_LABEL_LENGTH + 1];
66
67#define LOADAVG "loadavg"
68#define LOAD_AVERAGE "Load Average"
69
70typedef int (*FeatureFN) (void *data, const char *rawLabel, const char *label, const FeatureDescriptor *feature);
71
72static char
73rrdNextChar
74(char c) {
75  if (c == '9') {
76    return 'A';
77  } else if (c == 'Z') {
78    return 'a';
79  } else if (c == 'z') {
80    return 0;
81  } else {
82    return c + 1;
83  }
84}
85
86static void
87rrdCheckLabel
88(const char *rawLabel, int index0) {
89  char *buffer = rrdLabels[index0];
90  int i, j, okay;
91 
92  i = 0;
93  while ((i < RAW_LABEL_LENGTH) && rawLabel[i]) { /* contrain raw label to [A-Za-z0-9_] */
94    char c = rawLabel[i];
95    if (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) || ((c >= '0') && (c <= '9')) || (c == '_')) {
96      buffer[i] = c;
97    } else {
98      buffer[i] = '_';
99    }
100    ++ i;
101  }
102  buffer[i] = '\0';
103
104  j = 0;
105  okay = (i > 0);
106  while (okay && (j < index0)) /* locate duplicates */
107    okay = strcmp (rrdLabels[j ++], buffer);
108
109  while (!okay) { /* uniquify duplicate labels with _? or _?? */
110    if (!buffer[i]) {
111      if (i > RAW_LABEL_LENGTH - 3)
112        i = RAW_LABEL_LENGTH - 3;
113      buffer[i] = '_';
114      buffer[i + 1] = '0';
115      buffer[i + 2] = '\0';
116    } else if (!buffer[i + 2]) {
117      if (!(buffer[i + 1] = rrdNextChar (buffer[i + 1]))) {
118        buffer[i + 1] = '0';
119        buffer[i + 2] = '0';
120        buffer[i + 3] = '\0';
121      }
122    } else {
123      if (!(buffer[i + 2] = rrdNextChar (buffer[i + 2]))) {
124        buffer[i + 1] = rrdNextChar (buffer[i + 1]);
125        buffer[i + 2] = '0';
126      }
127    }
128    j = 0;
129    okay = 1;
130    while (okay && (j < index0))
131      okay = strcmp (rrdLabels[j ++], buffer);
132  }
133}
134
135static int
136applyToFeatures
137(FeatureFN fn, void *data) {
138  const sensors_chip_name *chip;
139  int i = 0, j, ret = 0, num = 0;
140
141  for (j = 0; (ret == 0) && (j < numChipNames); ++ j) {
142    while ((ret == 0) && ((chip = sensors_get_detected_chips (&chipNames[j], &i)) != NULL)) {
143      int index0, chipindex = -1;
144      for (index0 = 0; knownChips[index0].features; ++ index0)
145        /* Trick: we compare addresses here. We know it works because both
146           pointers were returned by sensors_get_detected_chips(), so they
147           refer to libsensors internal structures, which do not move. */
148        if (knownChips[index0].name == chip) {
149          chipindex = index0;
150          break;
151        }
152      if (chipindex >= 0) {
153        const ChipDescriptor *descriptor = &knownChips[chipindex];
154        const FeatureDescriptor *features = descriptor->features;
155
156        for (index0 = 0; (ret == 0) && (num < MAX_RRD_SENSORS) && features[index0].format; ++ index0) {
157          const FeatureDescriptor *feature = features + index0;
158          const char *rawLabel = feature->feature->name;
159          char *label = NULL;
160
161          if (!(label = sensors_get_label (chip, feature->feature))) {
162            sensorLog (LOG_ERR, "Error getting sensor label: %s/%s", chip->prefix, rawLabel);
163            ret = -1;
164          } else  {
165            rrdCheckLabel (rawLabel, num);
166            ret = fn (data, rrdLabels[num], label, feature);
167            ++ num;
168          }
169          if (label)
170            free (label);
171        }
172      }
173    }
174  }
175
176  return ret;
177}
178
179struct ds {
180  int num;
181  const char **argv;
182};
183
184static int
185rrdGetSensors_DS
186(void *_data, const char *rawLabel, const char *label, const FeatureDescriptor *feature) {
187  (void) label; /* no warning */
188  if (!feature || feature->rrd) {
189    struct ds *data = (struct ds *) _data;
190    char *ptr = rrdBuff + data->num * RRD_BUFF;
191    const char *min, *max;
192    data->argv[data->num ++] = ptr;
193    switch (feature ? feature->type : DataType_other) { /* arbitrary sanity limits */
194      case DataType_voltage:
195        min="-25";
196        max="25";
197        break;
198      case DataType_rpm:
199        min = "0";
200        max = "12000";
201        break;
202      case DataType_temperature:
203        min = "0";
204        max = "250";
205        break;
206      default:
207        min = max = "U";
208        break;
209    }
210    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);
211  }
212  return 0;
213}
214
215static int
216rrdGetSensors
217(const char **argv) {
218  int ret = 0;
219  struct ds data = { 0, argv};
220  ret = applyToFeatures (rrdGetSensors_DS, &data);
221  if (!ret && doLoad)
222    ret = rrdGetSensors_DS (&data, LOADAVG, LOAD_AVERAGE, NULL);
223  return ret ? -1 : data.num;
224}
225
226int
227rrdInit
228(void) {
229  int ret = 0;
230  struct stat tmp;
231 
232  sensorLog (LOG_DEBUG, "sensor RRD init"); 
233  if (stat (rrdFile, &tmp)) {
234    if (errno == ENOENT) {
235      char stepBuff[STEP_BUFF], rraBuff[RRA_BUFF];
236      int argc = 4, num;
237      const char *argv[6 + MAX_RRD_SENSORS] = {
238        "sensord", rrdFile, "-s", stepBuff
239      };
240     
241      sensorLog (LOG_INFO, "creating round robin database");
242      num = rrdGetSensors (argv + argc);
243      if (num == 0) {
244        sensorLog (LOG_ERR, "Error creating RRD: %s: %s", rrdFile, "No sensors detected");
245        ret = 2;
246      } else if (num < 0) {
247        ret = -num;
248      } else {
249        sprintf (stepBuff, "%d", rrdTime);
250        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 */);
251        argc += num;
252        argv[argc ++] = rraBuff;
253        argv[argc] = NULL;
254        if ((ret = rrd_create (argc, (char **) /* WEAK */ argv))) {
255          sensorLog (LOG_ERR, "Error creating RRD file: %s: %s", rrdFile, rrd_get_error ());
256        }
257      }
258    } else {
259      sensorLog (LOG_ERR, "Error stat()ing RRD: %s: %s", rrdFile, strerror (errno));
260      ret = 1;
261    }
262  }
263  sensorLog (LOG_DEBUG, "sensor RRD inited"); 
264 
265  return ret;
266}
267
268#define RRDCGI "/usr/bin/rrdcgi"
269#define WWWDIR "/sensord"
270
271struct gr {
272  DataType type;
273  const char *h2;
274  const char *image;
275  const char *title;
276  const char *axisTitle;
277  const char *axisDefn;
278  const char *options;
279  int loadAvg;
280};
281
282static int
283rrdCGI_DEF
284(void *_data, const char *rawLabel, const char *label, const FeatureDescriptor *feature) {
285  struct gr *data = (struct gr *) _data;
286  (void) label; /* no warning */
287  if (!feature || (feature->rrd && (feature->type == data->type)))
288    printf ("\n\tDEF:%s=%s:%s:AVERAGE", rawLabel, rrdFile, rawLabel);
289  return 0;
290}
291
292/* Compute an arbitrary color based on the sensor label. This is preferred
293   over a random value because this guarantees that daily and weekly charts
294   will use the same colors. */
295static int
296rrdCGI_color
297(const char *label) {
298  unsigned long color = 0, brightness;
299  const char *c;
300
301  for (c = label; *c; c++) {
302    color = (color << 6) + (color >> (*c & 7));
303    color ^= (*c) * 0x401;
304  }
305  color &= 0xffffff;
306  /* Adjust very light colors */
307  brightness = (color & 0xff) + ((color >> 8) & 0xff) + (color >> 16);
308  if (brightness > 672)
309    color &= 0x7f7f7f;
310  /* Adjust very dark colors */
311  else if (brightness < 96)
312    color |= 0x808080;
313  return color;
314}
315
316static int
317rrdCGI_LINE
318(void *_data, const char *rawLabel, const char *label, const FeatureDescriptor *feature) {
319  struct gr *data = (struct gr *) _data;
320  if (!feature || (feature->rrd && (feature->type == data->type)))
321    printf ("\n\tLINE2:%s#%.6x:\"%s\"", rawLabel, rrdCGI_color(label), label);
322  return 0;
323}
324
325static struct gr graphs[] = {
326  {
327    DataType_temperature,
328    "Daily Temperature Summary",
329    "daily-temperature",
330    "Temperature",
331    "Temperature (C)",
332    "HOUR:1:HOUR:3:HOUR:3:0:%b %d %H:00",
333    "-s -1d -l 0",
334    1
335  }, {
336    DataType_rpm,
337    "Daily Fan Speed Summary",
338    "daily-rpm",
339    "Fan Speed",
340    "Speed (RPM)",
341    "HOUR:1:HOUR:3:HOUR:3:0:%b %d %H:00",
342    "-s -1d -l 0",
343    0
344  }, {
345    DataType_voltage,
346    "Daily Voltage Summary",
347    "daily-voltage",
348    "Power Supply",
349    "Voltage (V)",
350    "HOUR:1:HOUR:3:HOUR:3:0:%b %d %H:00",
351    "-s -1d --alt-autoscale",
352    0
353  }, {
354    DataType_temperature,
355    "Weekly Temperature Summary",
356    "weekly-temperature",
357    "Temperature",
358    "Temperature (C)",
359    "HOUR:6:DAY:1:DAY:1:86400:%a %b %d",
360    "-s -1w -l 0",
361    1
362  }, {
363    DataType_rpm,
364    "Weekly Fan Speed Summary",
365    "weekly-rpm",
366    "Fan Speed",
367    "Speed (RPM)",
368    "HOUR:6:DAY:1:DAY:1:86400:%a %b %d",
369    "-s -1w -l 0",
370    0
371  }, {
372    DataType_voltage,
373    "Weekly Voltage Summary",
374    "weekly-voltage",
375    "Power Supply",
376    "Voltage (V)",
377    "HOUR:6:DAY:1:DAY:1:86400:%a %b %d",
378    "-s -1w --alt-autoscale",
379    0
380  }, {
381    DataType_other,
382    NULL,
383    NULL,
384    NULL,
385    NULL,
386    NULL,
387    NULL,
388    0
389  } 
390};
391
392int
393rrdUpdate
394(void) {
395  int ret = rrdChips ();
396  if (!ret && doLoad) {
397    FILE *loadavg;
398    if (!(loadavg = fopen ("/proc/loadavg", "r"))) {
399      sensorLog (LOG_ERR, "Error opening `/proc/loadavg': %s", strerror (errno));
400      ret = 1;
401    } else {
402      float value;
403      if (fscanf (loadavg, "%f", &value) != 1) {
404        sensorLog (LOG_ERR, "Error reading load average");
405        ret = 2;
406      } else {
407        sprintf (rrdBuff + strlen (rrdBuff), ":%f", value);
408      }
409      fclose (loadavg);
410    }
411  }
412  if (!ret) {
413    const char *argv[] = {
414      "sensord", rrdFile, rrdBuff, NULL
415    };
416    if ((ret = rrd_update (3, (char **) /* WEAK */ argv))) {
417      sensorLog (LOG_ERR, "Error updating RRD file: %s: %s", rrdFile, rrd_get_error ());
418    }
419  }
420  sensorLog (LOG_DEBUG, "sensor rrd updated"); 
421 
422  return ret;
423}
424
425int
426rrdCGI
427(void) {
428  int ret = 0;
429  struct gr *graph = graphs;
430
431  printf ("#!" RRDCGI "\n\n<HTML>\n<HEAD>\n<TITLE>sensord</TITLE>\n</HEAD>\n<BODY>\n<H1>sensord</H1>\n");
432  while (graph->type != DataType_other) {
433    printf ("<H2>%s</H2>\n", graph->h2);
434    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);
435    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);
436    if (!ret)
437      ret = applyToFeatures (rrdCGI_DEF, graph);
438    if (!ret && doLoad && graph->loadAvg)
439      ret = rrdCGI_DEF (graph, LOADAVG, LOAD_AVERAGE, NULL);
440    if (!ret)
441      ret = applyToFeatures (rrdCGI_LINE, graph);
442    if (!ret && doLoad && graph->loadAvg)
443      ret = rrdCGI_LINE (graph, LOADAVG, LOAD_AVERAGE, NULL);
444    printf (">\n</P>\n");
445    ++ graph;
446  }
447  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");
448  printf ("</BODY>\n</HTML>\n");
449 
450  return ret;
451}
Note: See TracBrowser for help on using the browser.