summaryrefslogtreecommitdiffstats
path: root/src/conf.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/conf.c')
-rw-r--r--src/conf.c596
1 files changed, 596 insertions, 0 deletions
diff --git a/src/conf.c b/src/conf.c
new file mode 100644
index 0000000..c1a1676
--- /dev/null
+++ b/src/conf.c
@@ -0,0 +1,596 @@
+/*
+ * conf.c Config Crap
+ *
+ * Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2013 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <bmon/bmon.h>
+#include <bmon/conf.h>
+#include <bmon/unit.h>
+#include <bmon/attr.h>
+#include <bmon/element.h>
+#include <bmon/element_cfg.h>
+#include <bmon/history.h>
+#include <bmon/utils.h>
+
+cfg_t *cfg;
+
+static cfg_opt_t element_opts[] = {
+ CFG_STR("description", NULL, CFGF_NONE),
+ CFG_BOOL("show", cfg_true, CFGF_NONE),
+ CFG_INT("rxmax", 0, CFGF_NONE),
+ CFG_INT("txmax", 0, CFGF_NONE),
+ CFG_INT("max", 0, CFGF_NONE),
+ CFG_END()
+};
+
+static cfg_opt_t history_opts[] = {
+ CFG_FLOAT("interval", 1.0f, CFGF_NONE),
+ CFG_INT("size", 60, CFGF_NONE),
+ CFG_STR("type", "64bit", CFGF_NONE),
+ CFG_END()
+};
+
+static cfg_opt_t attr_opts[] = {
+ CFG_STR("description", "", CFGF_NONE),
+ CFG_STR("unit", "", CFGF_NONE),
+ CFG_STR("type", "counter", CFGF_NONE),
+ CFG_BOOL("history", cfg_false, CFGF_NONE),
+ CFG_END()
+};
+
+static cfg_opt_t variant_opts[] = {
+ CFG_FLOAT_LIST("div", "{}", CFGF_NONE),
+ CFG_STR_LIST("txt", "", CFGF_NONE),
+ CFG_END()
+};
+
+static cfg_opt_t unit_opts[] = {
+ CFG_SEC("variant", variant_opts, CFGF_MULTI | CFGF_TITLE),
+ CFG_END()
+};
+
+static cfg_opt_t global_opts[] = {
+ CFG_FLOAT("read_interval", 1.0f, CFGF_NONE),
+ CFG_FLOAT("rate_interval", 1.0f, CFGF_NONE),
+ CFG_FLOAT("lifetime", 30.0f, CFGF_NONE),
+ CFG_FLOAT("history_variance", 0.1f, CFGF_NONE),
+ CFG_FLOAT("variance", 0.1f, CFGF_NONE),
+ CFG_BOOL("show_all", cfg_false, CFGF_NONE),
+ CFG_INT("unit_exp", -1, CFGF_NONE),
+ CFG_INT("sleep_time", 20000UL, CFGF_NONE),
+ CFG_BOOL("use_si", 0, CFGF_NONE),
+ CFG_STR("uid", NULL, CFGF_NONE),
+ CFG_STR("gid", NULL, CFGF_NONE),
+ CFG_STR("policy", "", CFGF_NONE),
+ CFG_SEC("unit", unit_opts, CFGF_MULTI | CFGF_TITLE),
+ CFG_SEC("attr", attr_opts, CFGF_MULTI | CFGF_TITLE),
+ CFG_SEC("history", history_opts, CFGF_MULTI | CFGF_TITLE),
+ CFG_SEC("element", element_opts, CFGF_MULTI | CFGF_TITLE),
+};
+
+float cfg_read_interval;
+float cfg_rate_interval;
+float cfg_rate_variance;
+float cfg_history_variance;
+int cfg_show_all;
+int cfg_unit_exp = DYNAMIC_EXP;
+
+static char * configfile = NULL;
+
+#if defined HAVE_CURSES
+#if defined HAVE_USE_DEFAULT_COLORS
+struct layout cfg_layout[] =
+{
+ {-1, -1, 0}, /* dummy, not used */
+ {-1, -1, 0}, /* default */
+ {-1, -1, A_REVERSE}, /* statusbar */
+ {-1, -1, 0}, /* header */
+ {-1, -1, 0}, /* list */
+ {-1, -1, A_REVERSE}, /* selected */
+};
+#else
+struct layout cfg_layout[] =
+{
+ {0, 0, 0}, /* dummy, not used */
+ {COLOR_BLACK, COLOR_WHITE, 0}, /* default */
+ {COLOR_BLACK, COLOR_WHITE, A_REVERSE}, /* statusbar */
+ {COLOR_BLACK, COLOR_WHITE, 0}, /* header */
+ {COLOR_BLACK, COLOR_WHITE, 0}, /* list */
+ {COLOR_BLACK, COLOR_WHITE, A_REVERSE}, /* selected */
+};
+#endif
+#endif
+
+tv_t * parse_tv(char *data)
+{
+ char *value;
+ tv_t *tv = xcalloc(1, sizeof(tv_t));
+
+ init_list_head(&tv->tv_list);
+
+ value = strchr(data, '=');
+
+ if (value) {
+ *value = '\0';
+ ++value;
+ tv->tv_value = strdup(value);
+ }
+
+ tv->tv_type = strdup(data);
+ return tv;
+}
+
+module_conf_t * parse_module(char *data)
+{
+ char *name = data, *opts = data, *next;
+ module_conf_t *m;
+
+ if (!*name)
+ quit("No module name given");
+
+ m = xcalloc(1, sizeof(module_conf_t));
+
+ init_list_head(&m->m_attrs);
+
+ opts = strchr(data, ':');
+
+ if (opts) {
+ *opts = '\0';
+ opts++;
+
+ do {
+ tv_t *tv;
+ next = strchr(opts, ';');
+
+ if (next) {
+ *next = '\0';
+ ++next;
+ }
+
+ tv = parse_tv(opts);
+ list_add_tail(&tv->tv_list, &m->m_attrs);
+
+ opts = next;
+ } while(next);
+ }
+
+ m->m_name = strdup(name);
+ return m;
+}
+
+
+int parse_module_param(const char *data, struct list_head *list)
+{
+ char *buf = strdup(data);
+ char *next;
+ char *current = buf;
+ module_conf_t *m;
+ int n = 0;
+
+ do {
+ next = strchr(current, ',');
+
+ if (next) {
+ *next = '\0';
+ ++next;
+ }
+
+ m = parse_module(current);
+ if (m) {
+ list_add_tail(&m->m_list, list);
+ n++;
+ }
+
+ current = next;
+ } while (next);
+
+ free(buf);
+
+ return n;
+}
+
+static void configfile_read_history(void)
+{
+ int i, nhistory;
+
+ nhistory = cfg_size(cfg, "history");
+
+ for (i = 0; i < nhistory; i++) {
+ struct history_def *def;
+ cfg_t *history;
+ const char *name, *type;
+ float interval;
+ int size;
+
+ if (!(history = cfg_getnsec(cfg, "history", i)))
+ BUG();
+
+ if (!(name = cfg_title(history)))
+ BUG();
+
+ interval = cfg_getfloat(history, "interval");
+ size = cfg_getint(history, "size");
+ type = cfg_getstr(history, "type");
+
+ if (interval == 0.0f)
+ interval = cfg_getfloat(cfg, "read_interval");
+
+ def = history_def_alloc(name);
+ def->hd_interval = interval;
+ def->hd_size = size;
+
+ if (!strcasecmp(type, "8bit"))
+ def->hd_type = HISTORY_TYPE_8;
+ else if (!strcasecmp(type, "16bit"))
+ def->hd_type = HISTORY_TYPE_16;
+ else if (!strcasecmp(type, "32bit"))
+ def->hd_type = HISTORY_TYPE_32;
+ else if (!strcasecmp(type, "64bit"))
+ def->hd_type = HISTORY_TYPE_64;
+ else
+ quit("Invalid type \'%s\', must be \"(8|16|32|64)bit\""
+ " in history definition #%d\n", type, i+1);
+ }
+}
+
+static void configfile_read_element_cfg(void)
+{
+ int i, nelement;
+
+ nelement = cfg_size(cfg, "element");
+
+ for (i = 0; i < nelement; i++) {
+ struct element_cfg *ec;
+ cfg_t *element;
+ const char *name, *description;
+ long max;
+
+ if (!(element = cfg_getnsec(cfg, "element", i)))
+ BUG();
+
+ if (!(name = cfg_title(element)))
+ BUG();
+
+ ec = element_cfg_alloc(name);
+
+ if ((description = cfg_getstr(element, "description")))
+ ec->ec_description = strdup(description);
+
+ if ((max = cfg_getint(element, "max")))
+ ec->ec_rxmax = ec->ec_txmax = max;
+
+ if ((max = cfg_getint(element, "rxmax")))
+ ec->ec_rxmax = max;
+
+ if ((max = cfg_getint(element, "txmax")))
+ ec->ec_txmax = max;
+
+ if (cfg_getbool(element, "show"))
+ ec->ec_flags |= ELEMENT_CFG_SHOW;
+ else
+ ec->ec_flags |= ELEMENT_CFG_HIDE;
+ }
+}
+
+static void add_div(struct unit *unit, int type, cfg_t *variant)
+{
+ int ndiv, n, ntxt;
+
+ if (!(ndiv = cfg_size(variant, "div")))
+ return;
+
+ ntxt = cfg_size(variant, "txt");
+ if (ntxt != ndiv)
+ quit("Number of elements for div and txt not equal\n");
+
+ if (!list_empty(&unit->u_div[type])) {
+ struct fraction *f, *n;
+
+ list_for_each_entry_safe(f, n, &unit->u_div[type], f_list)
+ fraction_free(f);
+ }
+
+ for (n = 0; n < ndiv; n++) {
+ char *txt;
+ float div;
+
+ div = cfg_getnfloat(variant, "div", n);
+ txt = cfg_getnstr(variant, "txt", n);
+
+ unit_add_div(unit, type, txt, div);
+ }
+}
+
+static void configfile_read_units(void)
+{
+ int i, nunits;
+ struct unit *u;
+
+ nunits = cfg_size(cfg, "unit");
+
+ for (i = 0; i < nunits; i++) {
+ int nvariants, n;
+ cfg_t *unit;
+ const char *name;
+
+ if (!(unit = cfg_getnsec(cfg, "unit", i)))
+ BUG();
+
+ if (!(name = cfg_title(unit)))
+ BUG();
+
+ if (!(nvariants = cfg_size(unit, "variant")))
+ continue;
+
+ if (!(u = unit_add(name)))
+ continue;
+
+ for (n = 0; n < nvariants; n++) {
+ cfg_t *variant;
+ const char *vtitle;
+
+ if (!(variant = cfg_getnsec(unit, "variant", n)))
+ BUG();
+
+ if (!(vtitle = cfg_title(variant)))
+ BUG();
+
+ if (!strcasecmp(vtitle, "default"))
+ add_div(u, UNIT_DEFAULT, variant);
+ else if (!strcasecmp(vtitle, "si"))
+ add_div(u, UNIT_SI, variant);
+ else
+ quit("Unknown unit variant \'%s\'\n", vtitle);
+ }
+ }
+}
+
+static void configfile_read_attrs(void)
+{
+ int i, nattrs, t;
+
+ nattrs = cfg_size(cfg, "attr");
+
+ for (i = 0; i < nattrs; i++) {
+ struct unit *u;
+ cfg_t *attr;
+ const char *name, *description, *unit, *type;
+ int flags = 0;
+
+ if (!(attr = cfg_getnsec(cfg, "attr", i)))
+ BUG();
+
+ if (!(name = cfg_title(attr)))
+ BUG();
+
+ description = cfg_getstr(attr, "description");
+ unit = cfg_getstr(attr, "unit");
+ type = cfg_getstr(attr, "type");
+
+ if (!unit)
+ quit("Attribute '%s' is missing unit specification\n",
+ name);
+
+ if (!type)
+ quit("Attribute '%s' is missing type specification\n",
+ name);
+
+ if (!(u = unit_lookup(unit)))
+ quit("Unknown unit \'%s\' attribute '%s'\n",
+ unit, name);
+
+ if (!strcasecmp(type, "counter"))
+ t = ATTR_TYPE_COUNTER;
+ else if (!strcasecmp(type, "rate"))
+ t = ATTR_TYPE_RATE;
+ else if (!strcasecmp(type, "percent"))
+ t = ATTR_TYPE_PERCENT;
+ else
+ quit("Unknown type \'%s\' in attribute '%s'\n",
+ type, name);
+
+ if (cfg_getbool(attr, "history"))
+ flags |= ATTR_FORCE_HISTORY;
+
+ if (cfg_getbool(attr, "ignore_overflows"))
+ flags |= ATTR_IGNORE_OVERFLOWS;
+
+ attr_def_add(name, description, u, t, flags);
+ }
+}
+
+static void conf_read(const char *path, int must)
+{
+ int err;
+
+ DBG("Reading configfile %s...", path);
+
+ if (access(path, R_OK) != 0) {
+ if (must)
+ quit("Error: Unable to read configfile \"%s\": %s\n",
+ path, strerror(errno));
+ else
+ return;
+ }
+
+ err = cfg_parse(cfg, path);
+ if (err == CFG_FILE_ERROR) {
+ quit("Error while reading configfile \"%s\": %s\n",
+ path, strerror(errno));
+ } else if (err == CFG_PARSE_ERROR) {
+ quit("Error while reading configfile \"%s\": parse error\n",
+ path);
+ }
+
+ configfile_read_units();
+ configfile_read_history();
+ configfile_read_attrs();
+ configfile_read_element_cfg();
+}
+
+static const char default_config[] = \
+"unit byte {" \
+" variant default {" \
+" div = { 1, 1024, 1048576, 1073741824, 1099511627776}" \
+" txt = { \"B\", \"KiB\", \"MiB\", \"GiB\", \"TiB\" }" \
+" }" \
+" variant si {" \
+" div = { 1, 1000, 1000000, 1000000000, 1000000000000 }" \
+" txt = { \"B\", \"KB\", \"MB\", \"GB\", \"TB\" }" \
+" }" \
+" }" \
+"unit bit {" \
+" variant default {" \
+" div = { 1, 1024, 1048576, 1073741824, 1099511627776}" \
+" txt = { \"b\", \"Kib\", \"Mib\", \"Gib\", \"TiB\" }" \
+" }" \
+" variant si {" \
+" div = { 1, 1000, 1000000, 1000000000, 1000000000000 }" \
+" txt = { \"b\", \"Kb\", \"Mb\", \"Gb\", \"Tb\" }" \
+" }" \
+"}" \
+"unit number {" \
+" variant default {" \
+" div = { 1, 1000, 1000000, 1000000000, 1000000000000 }" \
+" txt = { \"\", \"K\", \"M\", \"G\", \"T\" }" \
+" }" \
+"}" \
+"unit percent {" \
+" variant default {" \
+" div = { 1. }" \
+" txt = { \"%\" }" \
+" }" \
+"}" \
+"history second {" \
+" interval = 1.0" \
+" size = 60" \
+"}" \
+"history minute {" \
+" interval = 60.0" \
+" size = 60" \
+"}" \
+"history hour {" \
+" interval = 3600.0" \
+" size = 60" \
+"}" \
+"history day {" \
+" interval = 86400.0" \
+" size = 60" \
+"}";
+
+static void conf_read_default(void)
+{
+ int err;
+
+ DBG("Reading default config");
+
+ err = cfg_parse_buf(cfg, default_config);
+ if (err)
+ quit("Error while parsing default config\n");
+
+ configfile_read_units();
+ configfile_read_history();
+ configfile_read_attrs();
+ configfile_read_element_cfg();
+}
+
+void configfile_read(void)
+{
+ if (configfile)
+ conf_read(configfile, 1);
+ else {
+ conf_read(SYSCONFDIR "/bmonrc", 0);
+
+ if (getenv("HOME")) {
+ char path[FILENAME_MAX+1];
+ snprintf(path, sizeof(path), "%s/.bmonrc",
+ getenv("HOME"));
+ conf_read(path, 0);
+ }
+ }
+}
+
+void conf_init_pre(void)
+{
+ conf_read_default();
+}
+
+void conf_init_post(void)
+{
+ cfg_read_interval = cfg_getfloat(cfg, "read_interval");
+ cfg_rate_interval = cfg_getfloat(cfg, "rate_interval");
+ cfg_rate_variance = cfg_getfloat(cfg, "variance") * cfg_rate_interval;
+ cfg_history_variance = cfg_getfloat(cfg, "history_variance");
+ cfg_show_all = cfg_getbool(cfg, "show_all");
+ cfg_unit_exp = cfg_getint(cfg, "unit_exp");
+
+ element_parse_policy(cfg_getstr(cfg, "policy"));
+}
+
+void set_configfile(const char *file)
+{
+ static int set = 0;
+ if (!set) {
+ configfile = strdup(file);
+ set = 1;
+ }
+}
+
+void set_unit_exp(const char *name)
+{
+ if (tolower(*name) == 'b')
+ cfg_setint(cfg, "unit_exp", 0);
+ else if (tolower(*name) == 'k')
+ cfg_setint(cfg, "unit_exp", 1);
+ else if (tolower(*name) == 'm')
+ cfg_setint(cfg, "unit_exp", 2);
+ else if (tolower(*name) == 'g')
+ cfg_setint(cfg, "unit_exp", 3);
+ else if (tolower(*name) == 't')
+ cfg_setint(cfg, "unit_exp", 4);
+ else if (tolower(*name) == 'd')
+ cfg_setint(cfg, "unit_exp", DYNAMIC_EXP);
+ else
+ quit("Unknown unit exponent '%s'\n", name);
+}
+
+unsigned int get_lifecycles(void)
+{
+ return (unsigned int)
+ (cfg_getfloat(cfg, "lifetime") / cfg_getfloat(cfg, "read_interval"));
+}
+
+static void __exit conf_shutdown(void)
+{
+ cfg_free(cfg);
+}
+
+static void __init __conf_init(void)
+{
+ DBG("init");
+
+ cfg = cfg_init(global_opts, CFGF_NOCASE);
+
+ /* FIXME: Add validation functions */
+ //cfg_set_validate_func(cfg, "bookmark", &cb_validate_bookmark);
+}