/* 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, '"');
}