#include #include #include "debug.h" #include "config.h" #include "rrdtool.h" #include "parser.h" #define RRD_DATABASE_LIST "rrd_database" #define RRD_DATABASE_NAME "name" static int read_strings_from_list(config_setting_t *list, const char ***strings) { config_setting_t *string; int j; int str_count = config_setting_length(list); const char **strs; strs = calloc(sizeof(*strs), str_count + 1); for (j = 0; j < str_count; j++) { const char *str; string = config_setting_get_elem(list, j); str = config_setting_get_string(string); strs[j] = strdup(str); } *strings = strs; return 0; } static int parse_images(config_setting_t *list, struct rrd_database *db) { int i, count; config_setting_t *image, *str_list; const char *database = NULL, *filename = NULL, *timestart = NULL; const char *timeend = NULL, *imageformat = NULL; const char **options = NULL, **text = NULL; const char *updatestr = NULL; int width = 0, height = 0, text_lead = 0; count = config_setting_length(list); pr_info("%s: Parsing %d images\n", db->name, count); db->images = calloc(sizeof(*db->images), count + 1); for (i = 0; i < count; i++) { image = config_setting_get_elem(list, i); db->images[i] = calloc(sizeof(*db->images[i]), 1); config_setting_lookup_string(image, "database", &database); config_setting_lookup_int(image, "text_lead", &text_lead); /* * The config_setting_lookup_* functions will leave * the destination unchanged in case requested value * is not found. If that is the case, the value from * previous image group is used. That makes it * possible for the user to only define once all * fields and then leave identical fields unset on the * following groups, making it easier to define many * almost identical images. */ config_setting_lookup_string(image, "filename", &filename); if (!filename) { pr_err("Database %s does not have \"filename\" entry " " in image data\n", db->name); return -1; } config_setting_lookup_int(image, "width", &width); if (!width) { pr_err("Database %s does not have \"width\" entry " " in image data\n", db->name); return -1; } config_setting_lookup_int(image, "height", &height); if (!height) { pr_err("Database %s does not have \"height\" entry " " in image data\n", db->name); return -1; } config_setting_lookup_string(image, "timestart", ×tart); if (!timestart) { pr_err("Database %s does not have \"timestart\" entry " " in image data\n", db->name); return -1; } config_setting_lookup_string(image, "timeend", &timeend); if (!timeend) { pr_err("Database %s does not have \"timeend\" entry " " in image data\n", db->name); return -1; } config_setting_lookup_string(image, "imageformat", &imageformat); if (!imageformat) { pr_err("Database %s does not have \"imageformat\" "\ "entry in image data\n", db->name); return -1; } config_setting_lookup_string(image, "update_string", &updatestr); str_list = config_setting_get_member(image, "options"); if (str_list) read_strings_from_list(str_list, &options); str_list = config_setting_get_member(image, "text"); if (str_list) read_strings_from_list(str_list, &text); db->images[i]->rrd_database = database ? strdup(database) : NULL; db->images[i]->image_filename = strdup(filename); db->images[i]->width = width; db->images[i]->height = height; strncpy(db->images[i]->timestart, timestart, sizeof(db->images[i]->timestart)); strncpy(db->images[i]->timeend, timeend, sizeof(db->images[i]->timeend)); strncpy(db->images[i]->imageformat, imageformat, sizeof(db->images[i]->imageformat)); db->images[i]->options = options; db->images[i]->text_lead = text_lead; db->images[i]->text = text; if (updatestr) db->images[i]->updatestr = strdup(updatestr); } return 0; } static int (*str_to_parser(const char *str))(char *rrd_data, const char **parser_data) { if (!str) return NULL; if (!strcmp(str, "cpu")) return cpu_parser; if (!strcmp(str, "mem")) return mem_parser; if (!strcmp(str, "cpu_mem")) return cpu_mem_parser; if (!strcmp(str, "digitemp")) return digitemp_parser; if (!strcmp(str, "digitemp_mod")) return digitemp_parser_mod; if (!strcmp(str, "script")) return script_parser; if (!strcmp(str, "netstats")) return netstats_parser; if (!strcmp(str, "onewire")) return onewire_parser; return NULL; } static int parse_data_sources(config_setting_t *rrd, struct rrd_database *db) { config_setting_t *list, *group; int i, count; const char *type = NULL, *name = NULL; int heartbeat = 0; double min = 0; double max = 0; list = config_setting_get_member(rrd, "sources"); if (!list) { pr_info("No data sources\n"); return 0; } if (!config_setting_is_list(list)) { pr_err("Data sources have no list\n"); return 0; } count = config_setting_length(list); db->sources = calloc(sizeof(*db->sources), count + 1); for (i = 0; i < count; i++) { group = config_setting_get_elem(list, i); config_setting_lookup_string(group, "type", &type); config_setting_lookup_string(group, "name", &name); config_setting_lookup_float(group, "min", &min); config_setting_lookup_float(group, "max", &max); config_setting_lookup_int(group, "heartbeat", &heartbeat); db->sources[i].type = strdup(type); db->sources[i].name = strdup(name); db->sources[i].heartbeat = heartbeat; db->sources[i].min = min; db->sources[i].max = max; } return 0; } static int parse_archives(config_setting_t *rrd, struct rrd_database *db) { config_setting_t *list, *group; int i, count; const char *type = NULL; double xff = 0; int steps = 0; int rows = 0; list = config_setting_get_member(rrd, "archives"); if (!list) { pr_info("No archive found\n"); return 0; } if (!config_setting_is_list(list)) { pr_err("Archive does not contain a list\n"); return 0; } count = config_setting_length(list); db->archives = calloc(sizeof(*db->archives), count + 1); for (i = 0; i < count; i++) { group = config_setting_get_elem(list, i); config_setting_lookup_string(group, "type", &type); config_setting_lookup_float(group, "xff", &xff); config_setting_lookup_int(group, "steps", &steps); config_setting_lookup_int(group, "rows", &rows); db->archives[i].type = strdup(type); db->archives[i].xff = xff; db->archives[i].steps = steps; db->archives[i].rows = rows; } return 0; } static int parse_database(config_setting_t *rrd, struct rrd_database *db) { config_setting_t *list, *str_list; const char *name, *parser = NULL, *filename, **parser_data; const char **pre_draw_cmd; if (!config_setting_lookup_string(rrd, "name", &name)) { pr_err("Database entry does not contain name\n"); return -1; } pr_info("parsing database %s\n", name); db->name = strdup(name); if (!config_setting_lookup_string(rrd, "filename", &filename)) { pr_err("Database %s does not contain filename\n", db->name); return -1; } db->filename = strdup(filename); if (!config_setting_lookup_int(rrd, "interval", &db->interval)) { pr_err("Database %s does not have interval set\n", name); return -1; } /* Parser is not a mandatory parameter */ config_setting_lookup_string(rrd, "parser", &parser); db->parse = str_to_parser(parser); list = config_setting_get_member(rrd, "image"); parse_images(list, db); parse_data_sources(rrd, db); parse_archives(rrd, db); str_list = config_setting_get_member(rrd, "parser_data"); if (str_list) { read_strings_from_list(str_list, &parser_data); db->parser_data = parser_data; } str_list = config_setting_get_member(rrd, "pre_draw_cmd"); if (str_list) { read_strings_from_list(str_list, &pre_draw_cmd); db->pre_draw_cmd = (char *const *)pre_draw_cmd; } return 0; } struct rrd_database **populate_database(const char *conffile) { struct rrd_database **rrd_db = NULL; config_t config; config_setting_t *rrd_database; int i, count; config_init(&config); if (!config_read_file(&config, conffile)) { pr_err("%s:%d - %s\n", config_error_file(&config), config_error_line(&config), config_error_text(&config)); goto out; } rrd_database = config_lookup(&config, "rrd_database"); count = config_setting_length(rrd_database); /* * Allocate one element extra. The last one is zeroed out to * indicate the end of the db list */ rrd_db = calloc(count + 1, sizeof(*rrd_db)); pr_info("There are %d database entries\n", count); for (i = 0; i < count; i++) { config_setting_t *rrd; rrd_db[i] = calloc(1, sizeof(*rrd_db[i])); rrd = config_setting_get_elem(rrd_database, i); parse_database(rrd, rrd_db[i]); } out: config_destroy(&config); return rrd_db; } #define CONFIG_ADD_STRING(_config, _setting, _name, _val) \ do { \ _setting = config_setting_add(_config, _name, \ CONFIG_TYPE_STRING); \ config_setting_set_string(_setting, _val); \ } while (0); \ #define CONFIG_ADD_INT(_config, _setting, _name, _val) \ do { \ _setting = config_setting_add(_config, _name, \ CONFIG_TYPE_INT); \ config_setting_set_int(_setting, _val); \ } while (0); \ #define CONFIG_ADD_FLOAT(_config, _setting, _name, _val) \ do { \ _setting = config_setting_add(_config, _name, \ CONFIG_TYPE_FLOAT); \ config_setting_set_float(_setting, _val); \ } while (0); \ static void put_strings_to_list(config_setting_t *config, const char *entry_name, const char **strings) { config_setting_t *list, *setting; int i; list = config_setting_add(config, entry_name, CONFIG_TYPE_LIST); for (i = 0; strings[i]; i++) CONFIG_ADD_STRING(list, setting, NULL, strings[i]); } static void put_image_to_list(config_setting_t *config, struct rrd_image *image) { config_setting_t *setting, *group; group = config_setting_add(config, "image", CONFIG_TYPE_GROUP); CONFIG_ADD_STRING(group, setting, "rrd_database", image->rrd_database); CONFIG_ADD_STRING(group, setting, "filename", image->image_filename); CONFIG_ADD_INT(group, setting, "width", image->width); CONFIG_ADD_INT(group, setting, "height", image->height); CONFIG_ADD_STRING(group, setting, "timestart", image->timestart); CONFIG_ADD_STRING(group, setting, "timeend", image->timeend); CONFIG_ADD_STRING(group, setting, "imageformat", image->imageformat); put_strings_to_list(group, "options", image->options); CONFIG_ADD_INT(group, setting, "text_lead", image->text_lead); put_strings_to_list(group, "text", image->text); } static void put_data_source_to_list(config_setting_t *list, struct rrd_data_source *source) { config_setting_t *setting, *group; group = config_setting_add(list, "data_source", CONFIG_TYPE_GROUP); CONFIG_ADD_STRING(group, setting, "type", source->type); CONFIG_ADD_STRING(group, setting, "name", source->name); CONFIG_ADD_INT(group, setting, "heartbeat", source->heartbeat); CONFIG_ADD_FLOAT(group, setting, "min", source->min); CONFIG_ADD_FLOAT(group, setting, "max", source->max); } static void put_archive_to_list(config_setting_t *list, struct rrd_archive *archive) { config_setting_t *setting, *group; group = config_setting_add(list, "archive", CONFIG_TYPE_GROUP); CONFIG_ADD_STRING(group, setting, "type", archive->type); CONFIG_ADD_FLOAT(group, setting, "xff", archive->xff); CONFIG_ADD_INT(group, setting, "steps", archive->steps); CONFIG_ADD_INT(group, setting, "rows", archive->rows); } static char *parser_to_str(int (*parser)(char *rrd_data, const char **parser_data)) { if (parser == cpu_parser) return "cpu"; if (parser == mem_parser) return "mem"; if (parser == cpu_mem_parser) return "cpu_mem"; if (parser == digitemp_parser) return "digitemp"; if (parser == digitemp_parser_mod) return "digitemp_mod"; if (parser == script_parser) return "script"; if (parser == netstats_parser) return "netstats"; if (parser == onewire_parser) return "onewire"; return NULL; } static void put_database_to_list(config_setting_t *config, struct rrd_database *db) { config_setting_t *setting; config_setting_t *list, *group; char *str = parser_to_str(db->parse); int i; group = config_setting_add(config, RRD_DATABASE_LIST, CONFIG_TYPE_GROUP); CONFIG_ADD_STRING(group, setting, "name", db->name); CONFIG_ADD_STRING(group, setting, "filename", db->filename); CONFIG_ADD_INT(group, setting, "interval", db->interval); CONFIG_ADD_STRING(group, setting, "parser", str); if (db->parser_data) put_strings_to_list(group, "parser_data", db->parser_data); list = config_setting_add(group, "image", CONFIG_TYPE_LIST); for (i = 0; db->images[i]; i++) { put_image_to_list(list, db->images[i]); } if (db->sources) { list = config_setting_add(group, "sources", CONFIG_TYPE_LIST); for (i = 0; db->sources[i].type; i++) { put_data_source_to_list(list, &db->sources[i]); } } if (db->archives) { list = config_setting_add(group, "archives", CONFIG_TYPE_LIST); for (i = 0; db->archives[i].type; i++) { put_archive_to_list(list, &db->archives[i]); } } } int write_database(const char *conffile, struct rrd_database **rrd_db) { config_t config; config_setting_t *root, *rrd_database_list; int i; int ret; config_init(&config); config_set_tab_width(&config, 4); root = config_root_setting(&config); rrd_database_list = config_setting_add(root, RRD_DATABASE_LIST, CONFIG_TYPE_LIST); for (i = 0; rrd_db[i]; i++) { put_database_to_list(rrd_database_list, rrd_db[i]); } ret = !config_write_file(&config, conffile); if (ret) pr_err("Error while writing file.\n"); config_destroy(&config); return ret; }