1: /*
2: * conf.c Config Crap
3: *
4: * Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
5: * Copyright (c) 2013 Red Hat, Inc.
6: *
7: * Permission is hereby granted, free of charge, to any person obtaining a
8: * copy of this software and associated documentation files (the "Software"),
9: * to deal in the Software without restriction, including without limitation
10: * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11: * and/or sell copies of the Software, and to permit persons to whom the
12: * Software is furnished to do so, subject to the following conditions:
13: *
14: * The above copyright notice and this permission notice shall be included
15: * in all copies or substantial portions of the Software.
16: *
17: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18: * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19: * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20: * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21: * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22: * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23: * DEALINGS IN THE SOFTWARE.
24: */
25:
26: #include <bmon/bmon.h>
27: #include <bmon/conf.h>
28: #include <bmon/unit.h>
29: #include <bmon/attr.h>
30: #include <bmon/element.h>
31: #include <bmon/element_cfg.h>
32: #include <bmon/history.h>
33: #include <bmon/layout.h>
34: #include <bmon/utils.h>
35:
36: cfg_t *cfg;
37:
38: static cfg_opt_t element_opts[] = {
39: CFG_STR("description", NULL, CFGF_NONE),
40: CFG_BOOL("show", cfg_true, CFGF_NONE),
41: CFG_INT("rxmax", 0, CFGF_NONE),
42: CFG_INT("txmax", 0, CFGF_NONE),
43: CFG_INT("max", 0, CFGF_NONE),
44: CFG_END()
45: };
46:
47: static cfg_opt_t history_opts[] = {
48: CFG_FLOAT("interval", 1.0f, CFGF_NONE),
49: CFG_INT("size", 60, CFGF_NONE),
50: CFG_STR("type", "64bit", CFGF_NONE),
51: CFG_END()
52: };
53:
54: static cfg_opt_t attr_opts[] = {
55: CFG_STR("description", "", CFGF_NONE),
56: CFG_STR("unit", "", CFGF_NONE),
57: CFG_STR("type", "counter", CFGF_NONE),
58: CFG_BOOL("history", cfg_false, CFGF_NONE),
59: CFG_END()
60: };
61:
62: static cfg_opt_t variant_opts[] = {
63: CFG_FLOAT_LIST("div", "{}", CFGF_NONE),
64: CFG_STR_LIST("txt", "", CFGF_NONE),
65: CFG_END()
66: };
67:
68: static cfg_opt_t unit_opts[] = {
69: CFG_SEC("variant", variant_opts, CFGF_MULTI | CFGF_TITLE),
70: CFG_END()
71: };
72:
73: static cfg_opt_t color_opts[] = {
74: CFG_STR_LIST("color_pair", "", CFGF_NONE),
75: CFG_END()
76: };
77:
78: static cfg_opt_t layout_opts[] = {
79: CFG_SEC("color", color_opts, CFGF_MULTI | CFGF_TITLE),
80: CFG_END()
81: };
82:
83: static cfg_opt_t global_opts[] = {
84: CFG_FLOAT("read_interval", 1.0f, CFGF_NONE),
85: CFG_FLOAT("rate_interval", 1.0f, CFGF_NONE),
86: CFG_FLOAT("lifetime", 30.0f, CFGF_NONE),
87: CFG_FLOAT("history_variance", 0.1f, CFGF_NONE),
88: CFG_FLOAT("variance", 0.1f, CFGF_NONE),
89: CFG_BOOL("show_all", cfg_false, CFGF_NONE),
90: CFG_INT("unit_exp", -1, CFGF_NONE),
91: CFG_INT("sleep_time", 20000UL, CFGF_NONE),
92: CFG_BOOL("use_si", 0, CFGF_NONE),
93: CFG_BOOL("use_bit", 0, CFGF_NONE),
94: CFG_STR("uid", NULL, CFGF_NONE),
95: CFG_STR("gid", NULL, CFGF_NONE),
96: CFG_STR("policy", "", CFGF_NONE),
97: CFG_SEC("unit", unit_opts, CFGF_MULTI | CFGF_TITLE),
98: CFG_SEC("attr", attr_opts, CFGF_MULTI | CFGF_TITLE),
99: CFG_SEC("history", history_opts, CFGF_MULTI | CFGF_TITLE),
100: CFG_SEC("element", element_opts, CFGF_MULTI | CFGF_TITLE),
101: CFG_SEC("layout", layout_opts, CFGF_MULTI | CFGF_TITLE),
102: CFG_END()
103: };
104:
105: float cfg_read_interval;
106: float cfg_rate_interval;
107: float cfg_rate_variance;
108: float cfg_history_variance;
109: int cfg_show_all;
110: int cfg_unit_exp = DYNAMIC_EXP;
111:
112: static char * configfile = NULL;
113:
114: #if defined HAVE_CURSES
115: #if defined HAVE_USE_DEFAULT_COLORS
116: struct layout cfg_layout[] =
117: {
118: {-1, -1, 0}, /* dummy, not used */
119: {-1, -1, 0}, /* default */
120: {-1, -1, A_REVERSE}, /* statusbar */
121: {-1, -1, 0}, /* header */
122: {-1, -1, 0}, /* list */
123: {-1, -1, A_REVERSE}, /* selected */
124: {-1, -1, 0}, /* RX graph */
125: {-1, -1, 0}, /* TX graph */
126: };
127: #else
128: struct layout cfg_layout[] =
129: {
130: {0, 0, 0}, /* dummy, not used */
131: {COLOR_WHITE, COLOR_BLACK, 0}, /* default */
132: {COLOR_BLUE, COLOR_GREEN, A_REVERSE}, /* statusbar */
133: {COLOR_GREEN, COLOR_BLACK, 0}, /* header */
134: {COLOR_WHITE, COLOR_BLACK, 0}, /* list */
135: {COLOR_YELLOW, COLOR_BLACK, A_REVERSE}, /* selected */
136: {COLOR_GREEN, COLOR_BLACK, 0}, /* RX graph */
137: {COLOR_RED, COLOR_BLACK, 0}, /* TX graph */
138: };
139: #endif
140: #endif
141:
142: tv_t * parse_tv(char *data)
143: {
144: char *value;
145: tv_t *tv = xcalloc(1, sizeof(tv_t));
146:
147: init_list_head(&tv->tv_list);
148:
149: value = strchr(data, '=');
150:
151: if (value) {
152: *value = '\0';
153: ++value;
154: tv->tv_value = strdup(value);
155: }
156:
157: tv->tv_type = strdup(data);
158: return tv;
159: }
160:
161: module_conf_t * parse_module(char *data)
162: {
163: char *name = data, *opts = data, *next;
164: module_conf_t *m;
165:
166: if (!*name)
167: quit("No module name given");
168:
169: m = xcalloc(1, sizeof(module_conf_t));
170:
171: init_list_head(&m->m_attrs);
172:
173: opts = strchr(data, ':');
174:
175: if (opts) {
176: *opts = '\0';
177: opts++;
178:
179: do {
180: tv_t *tv;
181: next = strchr(opts, ';');
182:
183: if (next) {
184: *next = '\0';
185: ++next;
186: }
187:
188: tv = parse_tv(opts);
189: list_add_tail(&tv->tv_list, &m->m_attrs);
190:
191: opts = next;
192: } while(next);
193: }
194:
195: m->m_name = strdup(name);
196: return m;
197: }
198:
199:
200: int parse_module_param(const char *data, struct list_head *list)
201: {
202: char *buf = strdup(data);
203: char *next;
204: char *current = buf;
205: module_conf_t *m;
206: int n = 0;
207:
208: do {
209: next = strchr(current, ',');
210:
211: if (next) {
212: *next = '\0';
213: ++next;
214: }
215:
216: m = parse_module(current);
217: if (m) {
218: list_add_tail(&m->m_list, list);
219: n++;
220: }
221:
222: current = next;
223: } while (next);
224:
225: free(buf);
226:
227: return n;
228: }
229:
230: static void configfile_read_history(void)
231: {
232: int i, nhistory;
233:
234: nhistory = cfg_size(cfg, "history");
235:
236: for (i = 0; i < nhistory; i++) {
237: struct history_def *def;
238: cfg_t *history;
239: const char *name, *type;
240: float interval;
241: int size;
242:
243: if (!(history = cfg_getnsec(cfg, "history", i)))
244: BUG();
245:
246: if (!(name = cfg_title(history)))
247: BUG();
248:
249: interval = cfg_getfloat(history, "interval");
250: size = cfg_getint(history, "size");
251: type = cfg_getstr(history, "type");
252:
253: if (interval == 0.0f)
254: interval = cfg_getfloat(cfg, "read_interval");
255:
256: def = history_def_alloc(name);
257: def->hd_interval = interval;
258: def->hd_size = size;
259:
260: if (!strcasecmp(type, "8bit"))
261: def->hd_type = HISTORY_TYPE_8;
262: else if (!strcasecmp(type, "16bit"))
263: def->hd_type = HISTORY_TYPE_16;
264: else if (!strcasecmp(type, "32bit"))
265: def->hd_type = HISTORY_TYPE_32;
266: else if (!strcasecmp(type, "64bit"))
267: def->hd_type = HISTORY_TYPE_64;
268: else
269: quit("Invalid type \'%s\', must be \"(8|16|32|64)bit\""
270: " in history definition #%d\n", type, i+1);
271: }
272: }
273:
274: static void configfile_read_element_cfg(void)
275: {
276: int i, nelement;
277:
278: nelement = cfg_size(cfg, "element");
279:
280: for (i = 0; i < nelement; i++) {
281: struct element_cfg *ec;
282: cfg_t *element;
283: const char *name, *description;
284: long max;
285:
286: if (!(element = cfg_getnsec(cfg, "element", i)))
287: BUG();
288:
289: if (!(name = cfg_title(element)))
290: BUG();
291:
292: ec = element_cfg_alloc(name);
293:
294: if ((description = cfg_getstr(element, "description")))
295: ec->ec_description = strdup(description);
296:
297: if ((max = cfg_getint(element, "max")))
298: ec->ec_rxmax = ec->ec_txmax = max;
299:
300: if ((max = cfg_getint(element, "rxmax")))
301: ec->ec_rxmax = max;
302:
303: if ((max = cfg_getint(element, "txmax")))
304: ec->ec_txmax = max;
305:
306: if (cfg_getbool(element, "show"))
307: ec->ec_flags |= ELEMENT_CFG_SHOW;
308: else
309: ec->ec_flags |= ELEMENT_CFG_HIDE;
310: }
311: }
312:
313: static void add_div(struct unit *unit, int type, cfg_t *variant)
314: {
315: int ndiv, n, ntxt;
316:
317: if (!(ndiv = cfg_size(variant, "div")))
318: return;
319:
320: ntxt = cfg_size(variant, "txt");
321: if (ntxt != ndiv)
322: quit("Number of elements for div and txt not equal\n");
323:
324: if (!list_empty(&unit->u_div[type])) {
325: struct fraction *f, *n;
326:
327: list_for_each_entry_safe(f, n, &unit->u_div[type], f_list)
328: fraction_free(f);
329: }
330:
331: for (n = 0; n < ndiv; n++) {
332: char *txt;
333: float div;
334:
335: div = cfg_getnfloat(variant, "div", n);
336: txt = cfg_getnstr(variant, "txt", n);
337:
338: unit_add_div(unit, type, txt, div);
339: }
340: }
341:
342: static void configfile_read_units(void)
343: {
344: int i, nunits;
345: struct unit *u;
346:
347: nunits = cfg_size(cfg, "unit");
348:
349: for (i = 0; i < nunits; i++) {
350: int nvariants, n;
351: cfg_t *unit;
352: const char *name;
353:
354: if (!(unit = cfg_getnsec(cfg, "unit", i)))
355: BUG();
356:
357: if (!(name = cfg_title(unit)))
358: BUG();
359:
360: if (!(nvariants = cfg_size(unit, "variant")))
361: continue;
362:
363: if (!(u = unit_add(name)))
364: continue;
365:
366: for (n = 0; n < nvariants; n++) {
367: cfg_t *variant;
368: const char *vtitle;
369:
370: if (!(variant = cfg_getnsec(unit, "variant", n)))
371: BUG();
372:
373: if (!(vtitle = cfg_title(variant)))
374: BUG();
375:
376: if (!strcasecmp(vtitle, "default"))
377: add_div(u, UNIT_DEFAULT, variant);
378: else if (!strcasecmp(vtitle, "si"))
379: add_div(u, UNIT_SI, variant);
380: else if (!strcasecmp(vtitle, "bit"))
381: add_div(u, UNIT_BIT, variant);
382: else
383: quit("Unknown unit variant \'%s\'\n", vtitle);
384: }
385: }
386: }
387:
388: static void configfile_read_attrs(void)
389: {
390: int i, nattrs, t = 0;
391:
392: nattrs = cfg_size(cfg, "attr");
393:
394: for (i = 0; i < nattrs; i++) {
395: struct unit *u;
396: cfg_t *attr;
397: const char *name, *description, *unit, *type;
398: int flags = 0;
399:
400: if (!(attr = cfg_getnsec(cfg, "attr", i)))
401: BUG();
402:
403: if (!(name = cfg_title(attr)))
404: BUG();
405:
406: description = cfg_getstr(attr, "description");
407: unit = cfg_getstr(attr, "unit");
408: type = cfg_getstr(attr, "type");
409:
410: if (!unit)
411: quit("Attribute '%s' is missing unit specification\n",
412: name);
413:
414: if (!type)
415: quit("Attribute '%s' is missing type specification\n",
416: name);
417:
418: if (!(u = unit_lookup(unit)))
419: quit("Unknown unit \'%s\' attribute '%s'\n",
420: unit, name);
421:
422: if (!strcasecmp(type, "counter"))
423: t = ATTR_TYPE_COUNTER;
424: else if (!strcasecmp(type, "rate"))
425: t = ATTR_TYPE_RATE;
426: else if (!strcasecmp(type, "percent"))
427: t = ATTR_TYPE_PERCENT;
428: else
429: quit("Unknown type \'%s\' in attribute '%s'\n",
430: type, name);
431:
432: if (cfg_getbool(attr, "history"))
433: flags |= ATTR_FORCE_HISTORY;
434:
435: if (cfg_getbool(attr, "ignore_overflows"))
436: flags |= ATTR_IGNORE_OVERFLOWS;
437:
438: attr_def_add(name, description, u, t, flags);
439: }
440: }
441:
442: static void configfile_read_layout_cfg(void)
443: {
444: int i, nlayouts;
445: cfg_t *lout;
446: nlayouts = cfg_size(cfg, "layout");
447: for (i = 0; i < nlayouts; i++)
448: {
449: int c, ncolors;
450: const char *name;
451: if (!(lout = cfg_getnsec(cfg, "layout", i)))
452: BUG();
453:
454: if (!(name = cfg_title(lout)))
455: BUG();
456:
457: ncolors = cfg_size(lout, "color");
458: if (ncolors > LAYOUT_MAX) {
459: fprintf(stderr, "Warning excceeded maximum number of layouts\n");
460: ncolors = LAYOUT_MAX;
461: }
462:
463: for (c = 0; c < ncolors; c++) {
464: cfg_t *color_pair;
465:
466: if (!(color_pair = cfg_getnsec(lout, "color", c)))
467: BUG();
468:
469: if (!(name = cfg_title(color_pair)))
470: BUG();
471:
472: add_layout(name, color_pair);
473: }
474: }
475: }
476:
477: static void conf_read(const char *path, int must)
478: {
479: int err;
480:
481: DBG("Reading configfile %s...", path);
482:
483: if (access(path, R_OK) != 0) {
484: if (must)
485: quit("Error: Unable to read configfile \"%s\": %s\n",
486: path, strerror(errno));
487: else
488: return;
489: }
490:
491: err = cfg_parse(cfg, path);
492: if (err == CFG_FILE_ERROR) {
493: quit("Error while reading configfile \"%s\": %s\n",
494: path, strerror(errno));
495: } else if (err == CFG_PARSE_ERROR) {
496: quit("Error while reading configfile \"%s\": parse error\n",
497: path);
498: }
499:
500: configfile_read_units();
501: configfile_read_history();
502: configfile_read_attrs();
503: configfile_read_element_cfg();
504: configfile_read_layout_cfg();
505: }
506:
507: static const char default_config[] = \
508: "unit byte {" \
509: " variant default {" \
510: " div = { 1, 1024, 1048576, 1073741824, 1099511627776}" \
511: " txt = { \"B\", \"KiB\", \"MiB\", \"GiB\", \"TiB\" }" \
512: " }" \
513: " variant si {" \
514: " div = { 1, 1000, 1000000, 1000000000, 1000000000000 }" \
515: " txt = { \"B\", \"KB\", \"MB\", \"GB\", \"TB\" }" \
516: " }" \
517: " variant bit {" \
518: " div = { 0.125, 125, 125000, 125000000, 125000000000 }" \
519: " txt = { \"b\", \"Kb\", \"Mb\", \"Gb\", \"Tb\" }" \
520: " }" \
521: " }" \
522: "unit bit {" \
523: " variant default {" \
524: " div = { 1, 1000, 1000000, 1000000000, 1000000000000 }" \
525: " txt = { \"b\", \"Kb\", \"Mb\", \"Gb\", \"Tb\" }" \
526: " }" \
527: " variant si {" \
528: " div = { 1, 1000, 1000000, 1000000000, 1000000000000 }" \
529: " txt = { \"b\", \"Kb\", \"Mb\", \"Gb\", \"Tb\" }" \
530: " }" \
531: " variant bit {" \
532: " div = { 1, 1000, 1000000, 1000000000, 1000000000000 }" \
533: " txt = { \"b\", \"Kb\", \"Mb\", \"Gb\", \"Tb\" }" \
534: " }" \
535: "}" \
536: "unit number {" \
537: " variant default {" \
538: " div = { 1, 1000, 1000000, 1000000000, 1000000000000 }" \
539: " txt = { \"\", \"K\", \"M\", \"G\", \"T\" }" \
540: " }" \
541: "}" \
542: "unit percent {" \
543: " variant default {" \
544: " div = { 1. }" \
545: " txt = { \"%\" }" \
546: " }" \
547: "}" \
548: "history second {" \
549: " interval = 1.0" \
550: " size = 60" \
551: "}" \
552: "history minute {" \
553: " interval = 60.0" \
554: " size = 60" \
555: "}" \
556: "history hour {" \
557: " interval = 3600.0" \
558: " size = 60" \
559: "}" \
560: "history day {" \
561: " interval = 86400.0" \
562: " size = 60" \
563: "}"
564: "layout colors {" \
565: " color default {" \
566: " color_pair = { \"white\", \"black\" }" \
567: " }" \
568: " color statusbar{" \
569: " color_pair = { \"blue\", \"white\", \"reverse\" }" \
570: " }" \
571: " color header {" \
572: " color_pair = { \"yellow\", \"black\" }" \
573: " }" \
574: " color list {" \
575: " color_pair = { \"white\", \"black\" }" \
576: " }" \
577: " color selected {" \
578: " color_pair = { \"yellow\", \"black\", \"reverse\" }" \
579: " }" \
580: " color rx_graph {" \
581: " color_pair = { \"green\", \"black\" }" \
582: " }" \
583: " color tx_graph {" \
584: " color_pair = { \"red\", \"black\" }" \
585: " }" \
586: "}";
587:
588: static void conf_read_default(void)
589: {
590: int err;
591:
592: DBG("Reading default config");
593:
594: err = cfg_parse_buf(cfg, default_config);
595: if (err)
596: quit("Error while parsing default config\n");
597:
598: configfile_read_units();
599: configfile_read_history();
600: configfile_read_attrs();
601: configfile_read_element_cfg();
602: configfile_read_layout_cfg();
603: }
604:
605: void configfile_read(void)
606: {
607: if (configfile)
608: conf_read(configfile, 1);
609: else {
610: conf_read(SYSCONFDIR "/bmon.conf", 0);
611:
612: if (getenv("HOME")) {
613: char path[FILENAME_MAX+1];
614: snprintf(path, sizeof(path), "%s/.bmonrc",
615: getenv("HOME"));
616: conf_read(path, 0);
617: }
618: }
619: }
620:
621: void conf_init_pre(void)
622: {
623: conf_read_default();
624: }
625:
626: void conf_init_post(void)
627: {
628: cfg_read_interval = cfg_getfloat(cfg, "read_interval");
629: cfg_rate_interval = cfg_getfloat(cfg, "rate_interval");
630: cfg_rate_variance = cfg_getfloat(cfg, "variance") * cfg_rate_interval;
631: cfg_history_variance = cfg_getfloat(cfg, "history_variance");
632: cfg_show_all = cfg_getbool(cfg, "show_all");
633: cfg_unit_exp = cfg_getint(cfg, "unit_exp");
634:
635: element_parse_policy(cfg_getstr(cfg, "policy"));
636: }
637:
638: void set_configfile(const char *file)
639: {
640: static int set = 0;
641: if (!set) {
642: configfile = strdup(file);
643: set = 1;
644: }
645: }
646:
647: void set_unit_exp(const char *name)
648: {
649: if (tolower(*name) == 'b')
650: cfg_setint(cfg, "unit_exp", 0);
651: else if (tolower(*name) == 'k')
652: cfg_setint(cfg, "unit_exp", 1);
653: else if (tolower(*name) == 'm')
654: cfg_setint(cfg, "unit_exp", 2);
655: else if (tolower(*name) == 'g')
656: cfg_setint(cfg, "unit_exp", 3);
657: else if (tolower(*name) == 't')
658: cfg_setint(cfg, "unit_exp", 4);
659: else if (tolower(*name) == 'd')
660: cfg_setint(cfg, "unit_exp", DYNAMIC_EXP);
661: else
662: quit("Unknown unit exponent '%s'\n", name);
663: }
664:
665: unsigned int get_lifecycles(void)
666: {
667: return (unsigned int)
668: (cfg_getfloat(cfg, "lifetime") / cfg_getfloat(cfg, "read_interval"));
669: }
670:
671: static void __exit conf_shutdown(void)
672: {
673: cfg_free(cfg);
674: }
675:
676: static void __init __conf_init(void)
677: {
678: DBG("init");
679:
680: cfg = cfg_init(global_opts, CFGF_NOCASE);
681:
682: /* FIXME: Add validation functions */
683: //cfg_set_validate_func(cfg, "bookmark", &cb_validate_bookmark);
684: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>