]> git.itanic.dy.fi Git - rrdd/blob - config.c
rrdtool: Add pre_draw_cmd
[rrdd] / config.c
1 #include <stdlib.h>
2 #include <string.h>
3
4 #include "debug.h"
5 #include "config.h"
6 #include "rrdtool.h"
7 #include "parser.h"
8
9 #define RRD_DATABASE_LIST "rrd_database"
10 #define RRD_DATABASE_NAME "name"
11
12 static int read_strings_from_list(config_setting_t *list, const char ***strings)
13 {
14         config_setting_t *string;
15         int j;
16         int str_count = config_setting_length(list);
17         const char **strs;
18
19         strs = calloc(sizeof(*strs), str_count + 1);
20
21         for (j = 0; j < str_count; j++) {
22                 const char *str;
23
24                 string = config_setting_get_elem(list, j);
25                 str = config_setting_get_string(string);
26                 strs[j] = strdup(str);
27         }
28         *strings = strs;
29
30         return 0;
31 }
32
33 static int parse_images(config_setting_t *list, struct rrd_database *db)
34 {
35         int i, count;
36         config_setting_t *image, *str_list;
37         const char *database = NULL, *filename = NULL, *timestart = NULL;
38         const char *timeend = NULL, *imageformat = NULL;
39         const char **options = NULL, **text = NULL;
40         const char *updatestr = NULL;
41         int width = 0, height = 0, text_lead = 0;
42
43         count = config_setting_length(list);
44
45         pr_info("%s: Parsing %d images\n", db->name, count);
46         db->images = calloc(sizeof(*db->images), count + 1);
47
48         for (i = 0; i < count; i++) {
49                 image = config_setting_get_elem(list, i);
50                 db->images[i] = calloc(sizeof(*db->images[i]), 1);
51
52                 config_setting_lookup_string(image, "database", &database);
53                 config_setting_lookup_int(image, "text_lead", &text_lead);
54
55                 /*
56                  * The config_setting_lookup_* functions will leave
57                  * the destination unchanged in case requested value
58                  * is not found. If that is the case, the value from
59                  * previous image group is used. That makes it
60                  * possible for the user to only define once all
61                  * fields and then leave identical fields unset on the
62                  * following groups, making it easier to define many
63                  * almost identical images.
64                  */
65
66                 config_setting_lookup_string(image, "filename", &filename);
67                 if (!filename) {
68                         pr_err("Database %s does not have \"filename\" entry "
69                                 " in image data\n",
70                                 db->name);
71                         return -1;
72                 }
73
74                 config_setting_lookup_int(image, "width", &width);
75                 if (!width) {
76                         pr_err("Database %s does not have \"width\" entry "
77                                 " in image data\n",
78                                 db->name);
79                         return -1;
80                 }
81
82                 config_setting_lookup_int(image, "height", &height);
83                 if (!height) {
84                         pr_err("Database %s does not have \"height\" entry "
85                                 " in image data\n",
86                                 db->name);
87                         return -1;
88                 }
89
90                 config_setting_lookup_string(image, "timestart", &timestart);
91                 if (!timestart) {
92                         pr_err("Database %s does not have \"timestart\" entry "
93                                 " in image data\n",
94                                 db->name);
95                         return -1;
96                 }
97
98                 config_setting_lookup_string(image, "timeend", &timeend);
99                 if (!timeend) {
100                         pr_err("Database %s does not have \"timeend\" entry "
101                                 " in image data\n",
102                                 db->name);
103                         return -1;
104                 }
105
106                 config_setting_lookup_string(image, "imageformat",
107                                         &imageformat);
108                 if (!imageformat) {
109                         pr_err("Database %s does not have \"imageformat\" "\
110                                 "entry in image data\n",
111                                 db->name);
112                         return -1;
113                 }
114
115                 config_setting_lookup_string(image, "update_string",
116                                         &updatestr);
117
118                 str_list = config_setting_get_member(image, "options");
119                 if (str_list)
120                         read_strings_from_list(str_list, &options);
121
122                 str_list = config_setting_get_member(image, "text");
123                 if (str_list)
124                         read_strings_from_list(str_list, &text);
125
126                 db->images[i]->rrd_database     =
127                         database ? strdup(database) : NULL;
128                 db->images[i]->image_filename   = strdup(filename);
129                 db->images[i]->width            = width;
130                 db->images[i]->height           = height;
131                 strncpy(db->images[i]->timestart, timestart,
132                         sizeof(db->images[i]->timestart));
133                 strncpy(db->images[i]->timeend, timeend,
134                         sizeof(db->images[i]->timeend));
135                 strncpy(db->images[i]->imageformat, imageformat,
136                         sizeof(db->images[i]->imageformat));
137                 db->images[i]->options          = options;
138                 db->images[i]->text_lead        = text_lead;
139                 db->images[i]->text             = text;
140
141                 if (updatestr)
142                         db->images[i]->updatestr = strdup(updatestr);
143         }
144
145         return 0;
146 }
147
148 static int (*str_to_parser(const char *str))(char *rrd_data, const char **parser_data)
149 {
150         if (!str)
151                 return NULL;
152
153         if (!strcmp(str, "cpu"))
154                 return cpu_parser;
155
156         if (!strcmp(str, "mem"))
157                 return mem_parser;
158
159         if (!strcmp(str, "cpu_mem"))
160                 return cpu_mem_parser;
161
162         if (!strcmp(str, "digitemp"))
163                 return digitemp_parser;
164
165         if (!strcmp(str, "digitemp_mod"))
166                 return digitemp_parser_mod;
167
168         if (!strcmp(str, "script"))
169                 return script_parser;
170
171         if (!strcmp(str, "netstats"))
172                 return netstats_parser;
173
174         if (!strcmp(str, "onewire"))
175                 return onewire_parser;
176
177         return NULL;
178 }
179
180 static int parse_data_sources(config_setting_t *rrd, struct rrd_database *db)
181 {
182         config_setting_t *list, *group;
183         int i, count;
184         const char *type = NULL, *name = NULL;
185         int heartbeat = 0;
186         double min = 0;
187         double max = 0;
188
189         list = config_setting_get_member(rrd, "sources");
190         if (!list) {
191                 pr_info("No data sources\n");
192                 return 0;
193         }
194
195         if (!config_setting_is_list(list)) {
196                 pr_err("Data sources have no list\n");
197                 return 0;
198         }
199
200         count = config_setting_length(list);
201         db->sources = calloc(sizeof(*db->sources), count + 1);
202
203         for (i = 0; i < count; i++) {
204                 group = config_setting_get_elem(list, i);
205
206                 config_setting_lookup_string(group, "type", &type);
207                 config_setting_lookup_string(group, "name", &name);
208                 config_setting_lookup_float(group, "min", &min);
209                 config_setting_lookup_float(group, "max", &max);
210                 config_setting_lookup_int(group, "heartbeat", &heartbeat);
211
212                 db->sources[i].type = strdup(type);
213                 db->sources[i].name = strdup(name);
214                 db->sources[i].heartbeat = heartbeat;
215                 db->sources[i].min = min;
216                 db->sources[i].max = max;
217         }
218
219         return 0;
220 }
221
222 static int parse_archives(config_setting_t *rrd, struct rrd_database *db)
223 {
224         config_setting_t *list, *group;
225         int i, count;
226         const char *type = NULL;
227         double xff = 0;
228         int steps = 0;
229         int rows = 0;
230
231         list = config_setting_get_member(rrd, "archives");
232         if (!list) {
233                 pr_info("No archive found\n");
234                 return 0;
235         }
236
237         if (!config_setting_is_list(list)) {
238                 pr_err("Archive does not contain a list\n");
239                 return 0;
240         }
241
242         count = config_setting_length(list);
243         db->archives = calloc(sizeof(*db->archives), count + 1);
244
245         for (i = 0; i < count; i++) {
246                 group = config_setting_get_elem(list, i);
247
248                 config_setting_lookup_string(group, "type", &type);
249                 config_setting_lookup_float(group, "xff", &xff);
250                 config_setting_lookup_int(group, "steps", &steps);
251                 config_setting_lookup_int(group, "rows", &rows);
252
253                 db->archives[i].type = strdup(type);
254                 db->archives[i].xff = xff;
255                 db->archives[i].steps = steps;
256                 db->archives[i].rows = rows;
257         }
258
259         return 0;
260 }
261
262 static int parse_database(config_setting_t *rrd, struct rrd_database *db)
263 {
264         config_setting_t *list, *str_list;
265         const char *name, *parser = NULL, *filename, **parser_data;
266         const char **pre_draw_cmd;
267
268         if (!config_setting_lookup_string(rrd, "name", &name)) {
269                 pr_err("Database entry does not contain name\n");
270                 return -1;
271         }
272
273         pr_info("parsing database %s\n", name);
274         db->name = strdup(name);
275
276         if (!config_setting_lookup_string(rrd, "filename", &filename)) {
277                 pr_err("Database %s does not contain filename\n", db->name);
278                 return -1;
279         }
280         db->filename = strdup(filename);
281
282         if (!config_setting_lookup_int(rrd, "interval", &db->interval)) {
283                 pr_err("Database %s does not have interval set\n", name);
284                 return -1;
285         }
286
287         /* Parser is not a mandatory parameter */
288         config_setting_lookup_string(rrd, "parser", &parser);
289         db->parse = str_to_parser(parser);
290
291         list = config_setting_get_member(rrd, "image");
292         parse_images(list, db);
293         parse_data_sources(rrd, db);
294         parse_archives(rrd, db);
295
296         str_list = config_setting_get_member(rrd, "parser_data");
297         if (str_list) {
298                 read_strings_from_list(str_list, &parser_data);
299                 db->parser_data = parser_data;
300         }
301
302         str_list = config_setting_get_member(rrd, "pre_draw_cmd");
303         if (str_list) {
304                 read_strings_from_list(str_list, &pre_draw_cmd);
305                 db->pre_draw_cmd = (char *const *)pre_draw_cmd;
306         }
307
308         return 0;
309 }
310
311 struct rrd_database **populate_database(const char *conffile)
312 {
313         struct rrd_database **rrd_db = NULL;
314         config_t config;
315         config_setting_t *rrd_database;
316         int i, count;
317
318         config_init(&config);
319
320         if (!config_read_file(&config, conffile)) {
321                 pr_err("%s:%d - %s\n", config_error_file(&config),
322                         config_error_line(&config),
323                         config_error_text(&config));
324                 goto out;
325         }
326
327         rrd_database = config_lookup(&config, "rrd_database");
328         count = config_setting_length(rrd_database);
329
330         /*
331          * Allocate one element extra. The last one is zeroed out to
332          * indicate the end of the db list
333          */
334         rrd_db = calloc(count + 1, sizeof(*rrd_db));
335
336         pr_info("There are %d database entries\n", count);
337
338         for (i = 0; i < count; i++) {
339                 config_setting_t *rrd;
340                 rrd_db[i] = calloc(1, sizeof(*rrd_db[i]));
341                 rrd = config_setting_get_elem(rrd_database, i);
342                 parse_database(rrd, rrd_db[i]);
343         }
344
345 out:
346         config_destroy(&config);
347         return rrd_db;
348 }
349
350 #define CONFIG_ADD_STRING(_config, _setting, _name, _val)               \
351         do {                                                            \
352                 _setting = config_setting_add(_config, _name,           \
353                                         CONFIG_TYPE_STRING);            \
354                 config_setting_set_string(_setting, _val);              \
355         } while (0);                                                    \
356
357 #define CONFIG_ADD_INT(_config, _setting, _name, _val)                  \
358         do {                                                            \
359                 _setting = config_setting_add(_config, _name,           \
360                                         CONFIG_TYPE_INT);               \
361                 config_setting_set_int(_setting, _val);                 \
362         } while (0);                                                    \
363
364 #define CONFIG_ADD_FLOAT(_config, _setting, _name, _val)                \
365         do {                                                            \
366                 _setting = config_setting_add(_config, _name,           \
367                                         CONFIG_TYPE_FLOAT);             \
368                 config_setting_set_float(_setting, _val);               \
369         } while (0);                                                    \
370
371 static void put_strings_to_list(config_setting_t *config,
372                                 const char *entry_name, const char **strings)
373 {
374         config_setting_t *list, *setting;
375         int i;
376
377         list = config_setting_add(config, entry_name, CONFIG_TYPE_LIST);
378         for (i = 0; strings[i]; i++)
379                 CONFIG_ADD_STRING(list, setting, NULL, strings[i]);
380 }
381
382 static void put_image_to_list(config_setting_t *config,
383                         struct rrd_image *image)
384 {
385         config_setting_t *setting, *group;
386
387         group = config_setting_add(config, "image", CONFIG_TYPE_GROUP);
388
389         CONFIG_ADD_STRING(group, setting, "rrd_database", image->rrd_database);
390         CONFIG_ADD_STRING(group, setting, "filename", image->image_filename);
391         CONFIG_ADD_INT(group, setting, "width", image->width);
392         CONFIG_ADD_INT(group, setting, "height", image->height);
393         CONFIG_ADD_STRING(group, setting, "timestart", image->timestart);
394         CONFIG_ADD_STRING(group, setting, "timeend", image->timeend);
395         CONFIG_ADD_STRING(group, setting, "imageformat", image->imageformat);
396
397         put_strings_to_list(group, "options", image->options);
398
399         CONFIG_ADD_INT(group, setting, "text_lead", image->text_lead);
400
401         put_strings_to_list(group, "text", image->text);
402 }
403
404 static void put_data_source_to_list(config_setting_t *list,
405                                 struct rrd_data_source *source)
406 {
407         config_setting_t *setting, *group;
408
409         group = config_setting_add(list, "data_source", CONFIG_TYPE_GROUP);
410
411         CONFIG_ADD_STRING(group, setting, "type", source->type);
412         CONFIG_ADD_STRING(group, setting, "name", source->name);
413         CONFIG_ADD_INT(group, setting, "heartbeat", source->heartbeat);
414         CONFIG_ADD_FLOAT(group, setting, "min", source->min);
415         CONFIG_ADD_FLOAT(group, setting, "max", source->max);
416 }
417
418 static void put_archive_to_list(config_setting_t *list,
419                                 struct rrd_archive *archive)
420 {
421         config_setting_t *setting, *group;
422
423         group = config_setting_add(list, "archive", CONFIG_TYPE_GROUP);
424
425         CONFIG_ADD_STRING(group, setting, "type", archive->type);
426         CONFIG_ADD_FLOAT(group, setting, "xff", archive->xff);
427         CONFIG_ADD_INT(group, setting, "steps", archive->steps);
428         CONFIG_ADD_INT(group, setting, "rows", archive->rows);
429 }
430
431 static char *parser_to_str(int (*parser)(char *rrd_data, const char **parser_data))
432 {
433         if (parser == cpu_parser)
434                 return "cpu";
435
436         if (parser == mem_parser)
437                 return "mem";
438
439         if (parser == cpu_mem_parser)
440                 return "cpu_mem";
441
442         if (parser == digitemp_parser)
443                 return "digitemp";
444
445         if (parser == digitemp_parser_mod)
446                 return "digitemp_mod";
447
448         if (parser == script_parser)
449                 return "script";
450
451         if (parser == netstats_parser)
452                 return "netstats";
453
454         if (parser == onewire_parser)
455                 return "onewire";
456
457         return NULL;
458 }
459
460 static void put_database_to_list(config_setting_t *config,
461                                 struct rrd_database *db)
462 {
463         config_setting_t *setting;
464         config_setting_t *list, *group;
465         char *str = parser_to_str(db->parse);
466         int i;
467
468         group = config_setting_add(config, RRD_DATABASE_LIST,
469                                 CONFIG_TYPE_GROUP);
470
471         CONFIG_ADD_STRING(group, setting, "name", db->name);
472         CONFIG_ADD_STRING(group, setting, "filename", db->filename);
473         CONFIG_ADD_INT(group, setting, "interval", db->interval);
474         CONFIG_ADD_STRING(group, setting, "parser", str);
475
476         if (db->parser_data)
477                 put_strings_to_list(group, "parser_data", db->parser_data);
478
479         list = config_setting_add(group, "image", CONFIG_TYPE_LIST);
480         for (i = 0; db->images[i]; i++) {
481                 put_image_to_list(list, db->images[i]);
482         }
483
484         if (db->sources) {
485                 list = config_setting_add(group, "sources", CONFIG_TYPE_LIST);
486                 for (i = 0; db->sources[i].type; i++) {
487                         put_data_source_to_list(list, &db->sources[i]);
488                 }
489         }
490
491         if (db->archives) {
492                 list = config_setting_add(group, "archives", CONFIG_TYPE_LIST);
493                 for (i = 0; db->archives[i].type; i++) {
494                         put_archive_to_list(list, &db->archives[i]);
495                 }
496         }
497 }
498
499 int write_database(const char *conffile, struct rrd_database **rrd_db)
500 {
501         config_t config;
502         config_setting_t *root, *rrd_database_list;
503         int i;
504         int ret;
505
506         config_init(&config);
507
508         config_set_tab_width(&config, 4);
509
510         root = config_root_setting(&config);
511
512         rrd_database_list = config_setting_add(root, RRD_DATABASE_LIST,
513                                         CONFIG_TYPE_LIST);
514         for (i = 0; rrd_db[i]; i++) {
515                 put_database_to_list(rrd_database_list, rrd_db[i]);
516         }
517
518         ret = !config_write_file(&config, conffile);
519         if (ret)
520                 pr_err("Error while writing file.\n");
521
522         config_destroy(&config);
523
524         return ret;
525 }