/* This file is part of tagr. Copyright (C) 2000, 2005, 2009 Max Bouglacoff, Sergey Poznyakoff This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #define obstack_chunk_alloc malloc #define obstack_chunk_free free #include #include #define ARG_UNUSED __attribute__ ((__unused__)) static struct obstack mon_stack; static struct monitor *mon_base; static size_t mon_count; static struct obstack pp_cmd_stack; static int pp_cmd_stack_init; struct monitor * find_monitor (const char *name) { struct monitor *mon; for (mon = mon_base; mon < mon_base + mon_count; mon++) if (strcmp (mon->name, name) == 0) return mon; return NULL; } struct monitor * find_monitor_id (const char *id) { struct monitor *mon; for (mon = mon_base; mon < mon_base + mon_count; mon++) if (strcmp (mon->id, id) == 0) return mon; return NULL; } void free_monitors () { struct monitor *mon; for (mon = mon_base; mon < mon_base + mon_count; mon++) { free (mon->id); free (mon->name); free (mon->dir); } obstack_free (&mon_stack, mon_base); mon_count = 0; } static int cb_monitor (enum grecs_callback_command cmd, grecs_locus_t *locus, void *varptr, grecs_value_t *value, void *cb_data) { int rc; struct monitor *mon; void **pdata = cb_data; switch (cmd) { case grecs_callback_section_begin: if (!value || value->type != GCONF_TYPE_STRING) { grecs_error (locus, 0, _("tag must be a string")); return 0; } mon = xzalloc (sizeof (*mon)); mon->id = strdup (value->v.string); mon->scale = 1.0; mon->ystep = 100; *pdata = mon; break; case grecs_callback_section_end: mon = *pdata; if (mon->max_rate == 0) { grecs_error (locus, 0, _("maximum speed not set")); free (mon); return 0; } if (!mon->name) mon->name = strdup (mon->id); if (!mon->dir) mon->dir = strdup (mon->id); if (mon_count == 0) obstack_init (&mon_stack); obstack_grow (&mon_stack, mon, sizeof *mon); mon_count++; free (mon); *pdata = NULL; break; case grecs_callback_set_value: grecs_error (locus, 0, _("invalid use of block statement")); } return 0; } static int cb_double (enum grecs_callback_command cmd, grecs_locus_t *locus, void *varptr, grecs_value_t *value, void *cb_data) { double d, *dptr = varptr; char *p; if (cmd != grecs_callback_set_value) { grecs_error (locus, 0, _("unexpected block statement")); return 1; } if (value->type != GCONF_TYPE_STRING) { grecs_error (locus, 0, _("expected scalar value but found list")); return 1; } d = strtod (value->v.string, &p); if (*p) { grecs_error (locus, 0, _("not a valid double precision value")); return 1; } if (d < 0) { grecs_error (locus, 0, _("negative values not allowed")); return 1; } *dptr = d; return 0; } static struct grecs_keyword monitor_kw[] = { { "host", NULL, N_("Host name or IP address"), grecs_type_string, NULL, offsetof(struct monitor, name) }, { "directory", N_("name"), N_("Subdirectory name"), grecs_type_string, NULL, offsetof(struct monitor, dir) }, { "max-speed", NULL, N_("Maximum speed"), grecs_type_ulong, NULL, offsetof(struct monitor, max_rate) }, { "rate-units", NULL, N_("Name of rate units"), grecs_type_string, NULL, offsetof(struct monitor, rate_unit) }, { "scale", N_("arg: double"), N_("Scaling factor"), grecs_type_string, NULL, offsetof(struct monitor, scale), cb_double }, { "y-step", NULL, N_("Step for Y axis"), grecs_type_ulong, NULL, offsetof(struct monitor, ystep) }, { "swap", NULL, N_("Swap in and out rates"), grecs_type_bool, NULL, offsetof(struct monitor, swap) }, { NULL } }; static int cb_number_suffixes (enum grecs_callback_command cmd, grecs_locus_t *locus, void *varptr, grecs_value_t *value, void *cb_data) { switch (value->type) { case GCONF_TYPE_STRING: number_suffix_count = 2; number_suffix = xcalloc (2, sizeof (number_suffix[0])); number_suffix[0] = ""; number_suffix[1] = value->v.string; break; case GCONF_TYPE_ARRAY: { int i; number_suffix_count = value->v.arg.c + 1; number_suffix = xcalloc (number_suffix_count, sizeof (number_suffix[0])); number_suffix[0] = ""; for (i = 0; i < value->v.arg.c; i++) { grecs_value_t *pv = &value->v.arg.v[i]; if (pv->type != GCONF_TYPE_STRING) { grecs_error (locus, 0, _("expected scalar value but found list")); return 1; } number_suffix[i] = pv->v.string; } } break; case GCONF_TYPE_LIST: { const void *p; int i = 1; gl_list_iterator_t itr = gl_list_iterator (value->v.list); while (gl_list_iterator_next (&itr, &p, NULL)) { const grecs_value_t *vp = p; if (vp->type != GCONF_TYPE_STRING) { grecs_error (locus, 0, _("expected scalar value but found list")); return 1; } number_suffix[i++] = vp->v.string; } gl_list_iterator_free (&itr); } } return 0; } static int cb_color (enum grecs_callback_command cmd, grecs_locus_t *locus, void *varptr, grecs_value_t *value, void *cb_data) { unsigned n; char *p; int *array = varptr; if (cmd != grecs_callback_set_value) { grecs_error (locus, 0, _("unexpected block statement")); return 1; } if (!value || value->type != GCONF_TYPE_STRING) { grecs_error (locus, 0, _("expected scalar value")); return 1; } /* FIXME: allow to use some predefined color names */ n = strtoul (value->v.string, &p, 0); if (*p) { grecs_error (locus, 0, _("invalid number")); return 1; } array[0] = (n >> 16) & 0xff; array[1] = (n >> 8) & 0xff; array[2] = n & 0xff; return 0; } static int cb_facility (enum grecs_callback_command cmd, grecs_locus_t *locus, void *varptr, grecs_value_t *value, void *cb_data) { int *iptr = varptr; const char *str; int i; static struct log_xlat { const char *name; int facility; } xlat_tab[] = { { "USER", LOG_USER }, { "DAEMON", LOG_DAEMON }, { "AUTH", LOG_AUTH }, { "AUTHPRIV",LOG_AUTHPRIV }, { "MAIL", LOG_MAIL }, { "CRON", LOG_CRON }, { "LOCAL0", LOG_LOCAL0 }, { "LOCAL1", LOG_LOCAL1 }, { "LOCAL2", LOG_LOCAL2 }, { "LOCAL3", LOG_LOCAL3 }, { "LOCAL4", LOG_LOCAL4 }, { "LOCAL5", LOG_LOCAL5 }, { "LOCAL6", LOG_LOCAL6 }, { "LOCAL7", LOG_LOCAL7 }, { NULL } }; if (cmd != grecs_callback_set_value) { grecs_error (locus, 0, _("unexpected block statement")); return 1; } if (value->type != GCONF_TYPE_STRING) { grecs_error (locus, 0, _("expected scalar value but found list")); return 1; } str = value->v.string; if (strncasecmp (str, "LOG_", 4) == 0) str += 4; for (i = 0; xlat_tab[i].name; i++) if (strcasecmp (str, xlat_tab[i].name) == 0) { *iptr = xlat_tab[i].facility; return 0; } grecs_error (locus, 0, _("unknown syslog facility")); return 0; } static struct grecs_keyword log_kw[] = { { "tag", N_("arg"), N_("Tag syslog diagnostics with this tag."), grecs_type_string, &log_tag, 0 }, { "log-facility", N_("arg"), N_("Set syslog facility. Arg is one of the following: user, daemon, " "auth, authpriv, mail, cron, local0 through local7 " "(case-insensitive), or a facility number."), grecs_type_string, NULL, 0, cb_facility }, { "print-severity", N_("arg"), N_("Prefix diagnostics messages with their severity."), grecs_type_bool, &log_print_severity, 0 }, { NULL } }; static struct grecs_keyword tagr_kw[] = { { "basedir", NULL, N_("Set base directory name"), grecs_type_string, &basedir }, { "user", NULL, N_("Run with this user privileges"), grecs_type_string, &user }, { "template", NULL, N_("HTML page template"), grecs_type_string, &html_template }, { "pidfile", NULL, N_("Set pid file name"), grecs_type_string, &pidfile }, { "listen", N_("socket"), N_("Listen on this address"), grecs_type_sockaddr, &listen_sockaddr, }, { "update-interval", NULL, N_("Set graph update interval"), grecs_type_uint, &update_interval }, { "monitor", N_("id: string"), N_("Configure a monitor"), grecs_type_section, NULL, 0, cb_monitor, NULL, monitor_kw }, { "log", NULL, N_("Configure logging"), grecs_type_section, NULL, 0, NULL, NULL, log_kw }, { "rate-units", NULL, N_("Name of rate units"), grecs_type_string, &rate_unit }, { "number-suffixes", N_("suffixes"), N_("Not implemented") /* FIXME */, grecs_type_string, NULL, 0, cb_number_suffixes }, { "transparent", NULL, N_("Transparent graphs"), grecs_type_bool, &transparent_option }, { "percent", NULL, N_("Draw in/out percent graph (not implemented)"), /* FIXME */ grecs_type_bool, &percent_option }, { "zero-unknown", NULL, N_("Zero-out missing samples (not implemented)") /* FIXME */, grecs_type_bool, &zero_unknown_option }, { "fill-incoming", NULL, N_("Fill incoming graph"), grecs_type_bool, &fill_incoming_option }, { "color-background", NULL, N_("Set background color"), grecs_type_int, color_background, 0, cb_color }, { "color-light", NULL, N_("`Light' color (for the border)"), grecs_type_int, color_light, 0, cb_color }, { "color-dark", NULL, N_("`Dark' color (for the border)"), grecs_type_int, color_dark, 0, cb_color }, { "color-major", NULL, N_("`Major' color (boundaries, max. values, zero mark, etc.)"), grecs_type_int, color_major, 0, cb_color }, { "color-in", NULL, N_("Color for the input graph"), grecs_type_int, color_in, 0, cb_color }, { "color-out", NULL, N_("Color for the output graph"), grecs_type_int, color_out, 0, cb_color }, { "color-percent", NULL, N_("Color for the i/o percent graph"), grecs_type_int, color_percent, 0, cb_color }, { "color-grid", NULL, N_("Grid and axes color"), grecs_type_int, color_grid, 0, cb_color }, { "color-in-max", NULL, N_("Not implemented") /* FIXME */, grecs_type_int, color_in_max, 0, cb_color }, { "color-out-max", NULL, N_("Not implemented") /* FIXME */, grecs_type_int, color_out_max, 0, cb_color }, /* FIXME */ { NULL } }; void config_help () { static char docstring[] = N_("Configuration file structure for tagr.\n"); /* FIXME: "For more information, use `info tagr configuration'."); */ grecs_format_docstring (stdout, docstring, 0); grecs_format_statement_array (stdout, tagr_kw, 1, 0); } int readconfig () { int rc; grecs_set_keywords (tagr_kw); grecs_include_path_setup (DEFAULT_VERSION_INCLUDE_DIR, DEFAULT_INCLUDE_DIR, NULL); grecs_preprocessor = DEFAULT_PREPROCESSOR; grecs_log_to_stderr = log_to_stderr; if (pp_cmd_stack_init && grecs_preprocessor) { char *defs = obstack_finish (&pp_cmd_stack); char *cmd = xmalloc (strlen (grecs_preprocessor) + strlen (defs) + 1); strcpy (cmd, grecs_preprocessor); strcat (cmd, defs); grecs_preprocessor = cmd; obstack_free (&pp_cmd_stack, NULL); } if (preprocess_only) exit (grecs_preproc_run (configfile, grecs_preprocessor) ? EX_CONFIG : 0); rc = grecs_parse (configfile); if (rc == 0) mon_base = obstack_finish (&mon_stack); return rc; } void define_symbol (char *p) { if (!pp_cmd_stack_init) { obstack_init (&pp_cmd_stack); pp_cmd_stack_init = 1; } obstack_grow (&pp_cmd_stack, " \"-D", 4); for (p = optarg; *p; p++) { if (*p == '\\' || *p == '"') obstack_1grow (&pp_cmd_stack, '\\'); obstack_1grow (&pp_cmd_stack, *p); } obstack_1grow (&pp_cmd_stack, '"'); }