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

Revision 5678, 11.7 KB (checked in by andy, 5 years ago)

Convert coding style in prog/sensord/ from gnu to kernel style.

  • 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,
71                          const FeatureDescriptor *feature);
72
73static char rrdNextChar(char c)
74{
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 rrdCheckLabel(const char *rawLabel, int index0)
87{
88        char *buffer = rrdLabels[index0];
89        int i, j, okay;
90 
91        i = 0;
92        /* contrain raw label to [A-Za-z0-9_] */
93        while ((i < RAW_LABEL_LENGTH) && rawLabel[i]) {
94                char c = rawLabel[i];
95                if (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z'))
96                    || ((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
108        /* locate duplicates */
109        while (okay && (j < index0))
110                okay = strcmp(rrdLabels[j ++], buffer);
111
112        /* uniquify duplicate labels with _? or _?? */
113        while (!okay) {
114                if (!buffer[i]) {
115                        if (i > RAW_LABEL_LENGTH - 3)
116                                i = RAW_LABEL_LENGTH - 3;
117                        buffer[i] = '_';
118                        buffer[i + 1] = '0';
119                        buffer[i + 2] = '\0';
120                } else if (!buffer[i + 2]) {
121                        if (!(buffer[i + 1] = rrdNextChar(buffer[i + 1]))) {
122                                buffer[i + 1] = '0';
123                                buffer[i + 2] = '0';
124                                buffer[i + 3] = '\0';
125                        }
126                } else {
127                        if (!(buffer[i + 2] = rrdNextChar(buffer[i + 2]))) {
128                                buffer[i + 1] = rrdNextChar(buffer[i + 1]);
129                                buffer[i + 2] = '0';
130                        }
131                }
132                j = 0;
133                okay = 1;
134                while (okay && (j < index0))
135                        okay = strcmp(rrdLabels[j ++], buffer);
136        }
137}
138
139static int applyToFeatures(FeatureFN fn, void *data)
140{
141        const sensors_chip_name *chip;
142        int i, j, ret = 0, num = 0;
143
144        for (j = 0; (ret == 0) && (j < numChipNames); ++ j) {
145                i = 0;
146                while ((ret == 0) && ((chip = sensors_get_detected_chips(&chipNames[j], &i)) != NULL)) {
147                        int index0, chipindex = -1;
148
149                        /* Trick: we compare addresses here. We know it works
150                         * because both pointers were returned by
151                         * sensors_get_detected_chips(), so they refer to
152                         * libsensors internal structures, which do not move.
153                         */
154                        for (index0 = 0; knownChips[index0].features; ++index0)
155                                if (knownChips[index0].name == chip) {
156                                        chipindex = index0;
157                                        break;
158                                }
159                        if (chipindex >= 0) {
160                                const ChipDescriptor *descriptor = &knownChips[chipindex];
161                                const FeatureDescriptor *features = descriptor->features;
162
163                                for (index0 = 0; (ret == 0) && (num < MAX_RRD_SENSORS) && features[index0].format; ++index0) {
164                                        const FeatureDescriptor *feature = features + index0;
165                                        const char *rawLabel = feature->feature->name;
166                                        char *label = NULL;
167
168                                        if (!(label = sensors_get_label(chip, feature->feature))) {
169                                                sensorLog(LOG_ERR, "Error getting sensor label: %s/%s", chip->prefix, rawLabel);
170                                                ret = -1;
171                                        } else  {
172                                                rrdCheckLabel(rawLabel, num);
173                                                ret = fn(data,
174                                                         rrdLabels[num],
175                                                         label, feature);
176                                                ++ num;
177                                        }
178                                        if (label)
179                                                free(label);
180                                }
181                        }
182                }
183        }
184        return ret;
185}
186
187struct ds {
188        int num;
189        const char **argv;
190};
191
192static int rrdGetSensors_DS(void *_data, const char *rawLabel,
193                            const char *label,
194                            const FeatureDescriptor *feature)
195{
196        (void) label; /* no warning */
197        if (!feature || feature->rrd) {
198                struct ds *data = (struct ds *) _data;
199                char *ptr = rrdBuff + data->num * RRD_BUFF;
200                const char *min, *max;
201                data->argv[data->num ++] = ptr;
202
203                /* arbitrary sanity limits */
204                switch (feature ? feature->type : DataType_other) {
205                case DataType_voltage:
206                        min="-25";
207                        max="25";
208                        break;
209                case DataType_rpm:
210                        min = "0";
211                        max = "12000";
212                        break;
213                case DataType_temperature:
214                        min = "-100";
215                        max = "250";
216                        break;
217                default:
218                        min = max = "U";
219                        break;
220                }
221
222                /*
223                 * number of seconds downtime during which average be used
224                 * instead of unknown
225                 */
226                sprintf(ptr, "DS:%s:GAUGE:%d:%s:%s", rawLabel, 5 * rrdTime,
227                        min, max);
228        }
229        return 0;
230}
231
232static int rrdGetSensors(const char **argv)
233{
234        int ret = 0;
235        struct ds data = { 0, argv};
236        ret = applyToFeatures(rrdGetSensors_DS, &data);
237        if (!ret && doLoad)
238                ret = rrdGetSensors_DS(&data, LOADAVG, LOAD_AVERAGE, NULL);
239        return ret ? -1 : data.num;
240}
241
242int rrdInit(void)
243{
244        int ret = 0;
245        struct stat tmp;
246 
247        sensorLog(LOG_DEBUG, "sensor RRD init");
248        if (stat(rrdFile, &tmp)) {
249                if (errno == ENOENT) {
250                        char stepBuff[STEP_BUFF], rraBuff[RRA_BUFF];
251                        int argc = 4, num;
252                        const char *argv[6 + MAX_RRD_SENSORS] = {
253                                "sensord", rrdFile, "-s", stepBuff
254                        };
255     
256                        sensorLog(LOG_INFO, "creating round robin database");
257                        num = rrdGetSensors(argv + argc);
258                        if (num == 0) {
259                                sensorLog(LOG_ERR,
260                                          "Error creating RRD: %s: %s",
261                                          rrdFile, "No sensors detected");
262                                ret = 2;
263                        } else if (num < 0) {
264                                ret = -num;
265                        } else {
266                                sprintf(stepBuff, "%d", rrdTime);
267                                sprintf(rraBuff, "RRA:%s:%f:%d:%d",
268                                        rrdNoAverage?"LAST":"AVERAGE",
269                                        0.5 /* fraction of non-unknown samples needed per entry */,
270                                        1 /* samples per entry */,
271                                        7 * 24 * 60 * 60 / rrdTime /* 1 week */);
272                                argc += num;
273                                argv[argc ++] = rraBuff;
274                                argv[argc] = NULL;
275                                if ((ret = rrd_create(argc,
276                                                      (char **) /* WEAK */ argv))) {
277                                        sensorLog(LOG_ERR,
278                                                  "Error creating RRD file: %s: %s",
279                                                  rrdFile, rrd_get_error());
280                                }
281                        }
282                } else {
283                        sensorLog(LOG_ERR, "Error stat()ing RRD: %s: %s",
284                                  rrdFile, strerror(errno));
285                        ret = 1;
286                }
287        }
288        sensorLog(LOG_DEBUG, "sensor RRD inited");
289 
290        return ret;
291}
292
293#define RRDCGI "/usr/bin/rrdcgi"
294#define WWWDIR "/sensord"
295
296struct gr {
297        DataType type;
298        const char *h2;
299        const char *image;
300        const char *title;
301        const char *axisTitle;
302        const char *axisDefn;
303        const char *options;
304        int loadAvg;
305};
306
307static int rrdCGI_DEF(void *_data, const char *rawLabel, const char *label,
308                      const FeatureDescriptor *feature)
309{
310        struct gr *data = (struct gr *) _data;
311        (void) label; /* no warning */
312        if (!feature || (feature->rrd && (feature->type == data->type)))
313                printf("\n\tDEF:%s=%s:%s:AVERAGE", rawLabel, rrdFile,
314                       rawLabel);
315        return 0;
316}
317
318/*
319 * Compute an arbitrary color based on the sensor label. This is preferred
320 * over a random value because this guarantees that daily and weekly charts
321 * will use the same colors.
322 */
323static int rrdCGI_color(const char *label)
324{
325        unsigned long color = 0, brightness;
326        const char *c;
327
328        for (c = label; *c; c++) {
329                color = (color << 6) + (color >> (*c & 7));
330                color ^= (*c) * 0x401;
331        }
332        color &= 0xffffff;
333        /* Adjust very light colors */
334        brightness = (color & 0xff) + ((color >> 8) & 0xff) + (color >> 16);
335        if (brightness > 672)
336                color &= 0x7f7f7f;
337        /* Adjust very dark colors */
338        else if (brightness < 96)
339                color |= 0x808080;
340        return color;
341}
342
343static int rrdCGI_LINE(void *_data, const char *rawLabel, const char *label,
344                       const FeatureDescriptor *feature)
345{
346        struct gr *data = (struct gr *) _data;
347        if (!feature || (feature->rrd && (feature->type == data->type)))
348                printf("\n\tLINE2:%s#%.6x:\"%s\"", rawLabel,
349                       rrdCGI_color(label), label);
350        return 0;
351}
352
353static struct gr graphs[] = {
354        {
355                DataType_temperature,
356                "Daily Temperature Summary",
357                "daily-temperature",
358                "Temperature",
359                "Temperature (C)",
360                "HOUR:1:HOUR:3:HOUR:3:0:%b %d %H:00",
361                "-s -1d -l 0",
362                1
363        }, {
364                DataType_rpm,
365                "Daily Fan Speed Summary",
366                "daily-rpm",
367                "Fan Speed",
368                "Speed (RPM)",
369                "HOUR:1:HOUR:3:HOUR:3:0:%b %d %H:00",
370                "-s -1d -l 0",
371                0
372        }, {
373                DataType_voltage,
374                "Daily Voltage Summary",
375                "daily-voltage",
376                "Power Supply",
377                "Voltage (V)",
378                "HOUR:1:HOUR:3:HOUR:3:0:%b %d %H:00",
379                "-s -1d --alt-autoscale",
380                0
381        }, {
382                DataType_temperature,
383                "Weekly Temperature Summary",
384                "weekly-temperature",
385                "Temperature",
386                "Temperature (C)",
387                "HOUR:6:DAY:1:DAY:1:86400:%a %b %d",
388                "-s -1w -l 0",
389                1
390        }, {
391                DataType_rpm,
392                "Weekly Fan Speed Summary",
393                "weekly-rpm",
394                "Fan Speed",
395                "Speed (RPM)",
396                "HOUR:6:DAY:1:DAY:1:86400:%a %b %d",
397                "-s -1w -l 0",
398                0
399        }, {
400                DataType_voltage,
401                "Weekly Voltage Summary",
402                "weekly-voltage",
403                "Power Supply",
404                "Voltage (V)",
405                "HOUR:6:DAY:1:DAY:1:86400:%a %b %d",
406                "-s -1w --alt-autoscale",
407                0
408        }, {
409                DataType_other,
410                NULL,
411                NULL,
412                NULL,
413                NULL,
414                NULL,
415                NULL,
416                0
417        }
418};
419
420int rrdUpdate(void)
421{
422        int ret = rrdChips ();
423        if (!ret && doLoad) {
424                FILE *loadavg;
425                if (!(loadavg = fopen("/proc/loadavg", "r"))) {
426                        sensorLog(LOG_ERR,
427                                  "Error opening `/proc/loadavg': %s",
428                                  strerror(errno));
429                        ret = 1;
430                } else {
431                        float value;
432                        if (fscanf(loadavg, "%f", &value) != 1) {
433                                sensorLog(LOG_ERR,
434                                          "Error reading load average");
435                                ret = 2;
436                        } else {
437                                sprintf(rrdBuff + strlen(rrdBuff), ":%f",
438                                        value);
439                        }
440                        fclose(loadavg);
441                }
442        }
443        if (!ret) {
444                const char *argv[] = {
445                        "sensord", rrdFile, rrdBuff, NULL
446                };
447                if ((ret = rrd_update(3, (char **) /* WEAK */ argv))) {
448                        sensorLog(LOG_ERR, "Error updating RRD file: %s: %s",
449                                  rrdFile, rrd_get_error());
450                }
451        }
452        sensorLog(LOG_DEBUG, "sensor rrd updated");
453 
454        return ret;
455}
456
457int rrdCGI(void)
458{
459        int ret = 0;
460        struct gr *graph = graphs;
461
462        printf("#!" RRDCGI "\n\n<HTML>\n<HEAD>\n<TITLE>sensord</TITLE>\n</HEAD>\n<BODY>\n<H1>sensord</H1>\n");
463        while (graph->type != DataType_other) {
464                printf("<H2>%s</H2>\n", graph->h2);
465                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",
466                       cgiDir, graph->image);
467                printf("\t--lazy\n\t-v '%s'\n\t-t '%s'\n\t-x '%s'\n\t%s",
468                       graph->axisTitle, graph->title, graph->axisDefn,
469                       graph->options);
470                if (!ret)
471                        ret = applyToFeatures(rrdCGI_DEF, graph);
472                if (!ret && doLoad && graph->loadAvg)
473                        ret = rrdCGI_DEF(graph, LOADAVG, LOAD_AVERAGE, NULL);
474                if (!ret)
475                        ret = applyToFeatures(rrdCGI_LINE, graph);
476                if (!ret && doLoad && graph->loadAvg)
477                        ret = rrdCGI_LINE(graph, LOADAVG, LOAD_AVERAGE, NULL);
478                printf (">\n</P>\n");
479                ++ graph;
480        }
481        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");
482        printf("</BODY>\n</HTML>\n");
483 
484        return ret;
485}
Note: See TracBrowser for help on using the browser.