/*-
* Copyright (c) 2004 Andrey Simonenko
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "config.h"
#ifndef lint
static const char rcsid[] ATTR_UNUSED =
"@(#)$Id: ipastat_conf.c,v 1.1.4.6 2007/05/11 16:29:59 simon Exp $";
#endif /* !lint */
#include <ctype.h>
#include <errno.h>
#include <fnmatch.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/queue.h>
#include <dirent.h>
#include <regex.h>
#include "ipa_mod.h"
#include "dlapi.h"
#include "confcommon.h"
#include "memfunc.h"
#include "parser.h"
#include "pathnames.h"
#include "ipastat_conf.h"
#include "ipastat_log.h"
#include "ipastat_rules.h"
#include "ipastat_st.h"
#include "ipastat_main.h"
char *ipastat_conf_file = IPASTAT_CONF_FILE; /* -f <conf-file> */
int mimic_real_config = 0; /* 1, if -tt. */
static u_int section; /* Current section ID. */
static int global_section_set; /* 1, if has global{}. */
static regex_t reg_list;
static struct rule *currule; /* Current rule. */
static struct rulepat *currulepat; /* Current rulepat. */
static u_int rulepatno = 0;
static int posix_re_pattern; /* posix_re_pattern parameter. */
#ifdef WITH_LIMITS
static struct limit *curlimit; /* Current limit. */
static u_int limitno; /* Order number of the current limit. */
static struct limits_list *limits_list;
#endif
#ifdef WITH_THRESHOLDS
static struct threshold *curthreshold; /* Current threshold. */
static u_int thresholdno; /* Order number of the current threshold. */
static struct thresholds_list *thresholds_list;
#endif
/* Macro for validating if empty STAILQ_HEAD is in consistent state. */
#define STAILQ_EMPTY_HEAD_VALID(x) \
((x)->stqh_first == NULL && (x)->stqh_last == &(x)->stqh_first)
/*
* Exported support functions for modules.
*/
static const ipa_suppfunc suppfunc = {
print_string, /* print_string */
print_bytes, /* print_bytes */
print_time, /* print_time */
print_value, /* print_value */
print_boolean, /* print_boolean */
print_space, /* print_space */
set_indent, /* set_indent */
print_param_name, /* print_param_name */
print_args, /* print_param_args */
print_param_end, /* print_param_end */
print_sect_name, /* print_sect_name */
print_args, /* print_sect_args */
print_sect_begin, /* print_sect_begin */
print_sect_end, /* print_sect_end */
open_close_log, /* open_log */
open_close_log, /* close_log */
mod_logmsg, /* logmsg */
mod_logconferr, /* logconferr */
parser_local_sym_add, /* local_sym_add */
parser_local_sym_del, /* local_sym_del */
parser_global_sym_add, /* global_sym_add */
parser_global_sym_del /* global_sym_del */
};
void conferrx(const char *, ...) ATTR_FORMAT(printf, 1, 2);
static void confvlogmsgx_prefix(int, const char *, const char *, va_list) ATTR_FORMAT(printf, 3, 0);
/*
* Log message prepending it with prefix.
* If use_log == 1 then log is used, else printf(3) is used.
* Don't use mem_* functions.
*/
static void
confvlogmsgx_prefix(int priority, const char *prefix, const char *format,
va_list ap)
{
int rv;
char *ptr;
char buf[LOG_BUF_SIZE]; /* Try to use buffer in stack. */
if ( (rv = vsnprintf(buf, sizeof buf, format, ap)) < 0) {
logmsg(IPA_LOG_ERR, "confvlogmsgx_prefix: vsnprintf failed");
goto log_unformated;
}
if (rv >= sizeof buf) {
if ( (ptr = malloc(++rv)) == NULL) {
logmsgx(IPA_LOG_ERR, "confvlogmsgx_prefix: malloc failed");
goto log_unformated;
}
if (vsnprintf(ptr, rv, format, ap) < 0) {
logmsg(IPA_LOG_ERR, "confvlogmsgx_prefix: vsnprintf failed again");
free(ptr);
goto log_unformated;
}
logmsgx(priority, "%s: %s", prefix, ptr);
free(ptr);
} else
logmsgx(priority, "%s: %s", prefix, buf);
return;
log_unformated:
logmsgx(priority, "%s: unformated message: %s",
prefix, format);
}
/*
* The wrapper for parser_vlogmsgx.
*/
static void
parser_vlogmsgx_wrapper(const char *format, va_list ap)
{
confvlogmsgx_prefix(IPA_LOG_ERR, "parsing error", format, ap);
}
/*
* The wrapper for mvlogmsgx during configuration.
*/
static void
mvlogmsgx_conf_wrapper(const char *format, va_list ap)
{
confvlogmsgx_prefix(IPA_LOG_ERR, "config error", format, ap);
}
/*
* A wrapper for logging about errors.
*/
void
conferrx(const char *format, ...)
{
va_list ap;
va_start(ap, format);
confvlogmsgx_prefix(IPA_LOG_ERR, "config error", format, ap);
va_end(ap);
}
/*
* The same as above one, but with priority argument and va_list.
*/
static void
vconferrx_priority(int priority, const char *format, va_list ap)
{
confvlogmsgx_prefix(priority, "config error", format, ap);
}
/*
* Register a configuration event in st_mod module.
*/
static int
st_mod_conf_event(const struct st_mod *st_mod, u_int event,
u_int no, const void *arg)
{
if (st_mod->ipa_st_mod->conf_event(event, no, arg) < 0) {
logconferrx("module %s: conf_event(IPA_CONF_EVENT_%s) failed",
st_mod->mod_file, conf_event_msg[event]);
return -1;
}
return 0;
}
/*
* Register a configuration event in each module.
*/
static int
mod_conf_event(u_int event, u_int no, const void *arg)
{
const struct st_mod *st_mod;
SLIST_FOREACH(st_mod, &st_mod_list, link)
if (st_mod_conf_event(st_mod, event, no, arg) < 0) {
conferrx("mod_conf_event: st_mod_conf_event failed");
return -1;
}
return 0;
}
/*
* Inherit settings from rulepat from modules for rule.
*/
int
mod_conf_inherit(const struct rulepat *rulepat, const struct rule *rule)
{
const struct st_mod *st_mod;
SLIST_FOREACH(st_mod, &st_mod_list, link) {
if (st_mod->ipa_st_mod->conf_inherit != NULL)
if (st_mod->ipa_st_mod->conf_inherit(rulepat->rulepatno,
rule->ruleno, rule->rule_name) < 0) {
xlogmsgx(IPA_LOG_ERR, "module %s: conf_inherit(%s, %s) failed",
st_mod->mod_file, parser_buf_to_string(rulepat->pat),
rule->rule_name);
return -1;
}
}
return 0;
}
/*
* Parse the "global" section.
*/
/* ARGSUSED */
static int
parse_global(void *arg ATTR_UNUSED)
{
if (global_section_set) {
logconferrx("this section is duplicated");
return -1;
}
global_section_set = 1;
return mod_conf_event(IPA_CONF_EVENT_GLOBAL_BEGIN, 0, (void *)NULL);
}
/*
* Parse the "rule" section.
*/
static int
parse_rule(void *arg)
{
const char *rule_name = *(char **)arg;
struct rule *rule;
if (rule_by_name(rule_name) != NULL) {
logconferrx("this section is duplicated");
return -1;
}
if ( (rule = alloc_rule()) == NULL) {
logconferrx("alloc_rule failed");
return -1;
}
if ( (rule->rule_name = mem_strdup(rule_name, m_anon)) == NULL) {
xlogmsgx(IPA_LOG_ERR, "alloc_rule: mem_strdup failed");
return -1;
}
rule->free_mask = RULE_FREE_NAME;
add_rule_to_hash(rule);
#ifdef WITH_LIMITS
limits_list = &rule->limits;
STAILQ_INIT(limits_list);
limitno = 0;
#endif
#ifdef WITH_THRESHOLDS
thresholds_list = &rule->thresholds;
STAILQ_INIT(thresholds_list);
thresholdno = 0;
#endif
if (parser_local_sym_add("rule", rule->rule_name, 0) < 0)
return -1;
if (mod_conf_event(IPA_CONF_EVENT_RULE_BEGIN, rule->ruleno,
rule->rule_name) < 0)
return -1;
currule = rule;
return 0;
}
/*
* Parse the "dynamic_rules" parameter.
*/
static int
parse_dynamic_rules(void *arg)
{
dynamic_rules = *(int *)arg;
return 0;
}
/*
* Parse the "rulepat" section.
*/
static int
parse_rulepat(void *arg)
{
char *pat = *(char **)arg;
struct rulepat *rulepat;
STAILQ_FOREACH(rulepat, &rulepats_list, link)
if (strcmp(rulepat->pat, pat) == 0) {
logconferrx("this section is duplicated");
return -1;
}
if ( (rulepat = mzone_alloc(rulepat_mzone)) == NULL) {
logconferrx("mzone_alloc failed");
return -1;
}
STAILQ_INSERT_TAIL(&rulepats_list, rulepat, link);
if ( (re_errcode = regcomp(&rulepat->reg, pat, REG_EXTENDED|REG_NOSUB)) != 0) {
re_form_errbuf();
logconferrx("regcomp(\"%s\"): %s", pat, re_errbuf);
return -1;
}
rulepat->pat = pat;
rulepat->rulepatno = rulepatno++;
rulepat->check_next_rulepat = -1;
rulepat->st_list = NULL;
#ifdef WITH_LIMITS
limits_list = &rulepat->limits;
STAILQ_INIT(limits_list);
limitno = 0;
#endif
#ifdef WITH_THRESHOLDS
thresholds_list = &rulepat->thresholds;
STAILQ_INIT(thresholds_list);
thresholdno = 0;
#endif
if (mod_conf_event(IPA_CONF_EVENT_RULEPAT_BEGIN,
rulepat->rulepatno, rulepat->pat) < 0)
return -1;
currulepat = rulepat;
return 0;
}
/*
* Parse the "check_next_rulepat" parameter.
*/
static int
parse_check_next_rulepat(void *arg)
{
currulepat->check_next_rulepat = *(int *)arg;
return 0;
}
#ifdef WITH_LIMITS
/*
* Parse the "limit" section.
*/
static int
parse_limit(void *arg)
{
const char *limit_name = *(char **)arg;
struct limit *limit;
STAILQ_FOREACH(limit, limits_list, link)
if (strcmp(limit->limit_name, limit_name) == 0) {
logconferrx("this section is duplicated");
return -1;
}
if ( (limit = alloc_limit()) == NULL) {
logconferrx("alloc_limit failed");
return -1;
}
STAILQ_INSERT_TAIL(limits_list, limit, link);
if ( (limit->limit_name = mem_strdup(limit_name, m_anon)) == NULL) {
logconferrx("mem_strdup failed");
return -1;
}
limit->limitno = limitno++;
limit->free_mask = LIMIT_FREE_NAME;
if (mod_conf_event(IPA_CONF_EVENT_LIMIT_BEGIN, limit->limitno,
limit->limit_name) < 0)
return -1;
curlimit = limit;
return 0;
}
/*
* Parse "dynamic_limits" parameter.
*/
static int
parse_dynamic_limits(void *arg)
{
dynamic_limits = *(int *)arg;
return 0;
}
#endif /* WITH_LIMITS */
#ifdef WITH_THRESHOLDS
/*
* Parse "threshold" section.
*/
static int
parse_threshold(void *arg)
{
const char *threshold_name = *(char **)arg;
struct threshold *threshold;
STAILQ_FOREACH(threshold, thresholds_list, link)
if (strcmp(threshold->threshold_name, threshold_name) == 0) {
logconferrx("this section is duplicated");
return -1;
}
if ( (threshold = alloc_threshold()) == NULL) {
logconferrx("alloc_threshold failed");
return -1;
}
STAILQ_INSERT_TAIL(thresholds_list, threshold, link);
if ( (threshold->threshold_name = mem_strdup(threshold_name, m_anon)) == NULL) {
logconferrx("mem_strdup failed");
return -1;
}
threshold->thresholdno = thresholdno++;
threshold->free_mask = THRESHOLD_FREE_NAME;
if (mod_conf_event(IPA_CONF_EVENT_THRESHOLD_BEGIN,
threshold->thresholdno, threshold->threshold_name) < 0)
return -1;
curthreshold = threshold;
return 0;
}
/*
* Parse "dynamic_thresholds" parameter.
*/
static int
parse_dynamic_thresholds(void *arg)
{
dynamic_thresholds = *(int *)arg;
return 0;
}
#endif /* WITH_THRESHOLDS */
/*
* Check security of configuration file: absolute path, regular file.
*/
static int
check_conf_file(const char *fname)
{
struct stat statbuf;
if (lstat(fname, &statbuf) < 0) {
conferrx("lstat(%s): %s", fname, strerror(errno));
return -1;
}
if (!S_ISREG(statbuf.st_mode)) {
conferrx("configuration file \"%s\" should be a regular file",
fname);
return -1;
}
return 0;
}
/*
* Get module's name from the file name "[/path/]foobar[-x.y.z][.so]".
* Return "foobar" part from the given path name.
*/
static char *
get_mod_name(char *file_name)
{
char *ptr, *ptr2, *name;
if ( (ptr = strrchr(file_name, '/')) != NULL)
++ptr; /* foobar[-x.y.z].[so] */
else
ptr = file_name;
if ( (name = mem_strdup(ptr, m_anon)) == NULL) {
logconferrx("get_mod_name: mem_strdup failed");
return NULL;
}
if ( (ptr2 = strchr(name, '.')) != NULL)
*ptr2 = '\0'; /* foobar[-x] */
if ( (ptr = strrchr(name, '-')) != NULL) {
for (ptr2 = ptr; *ptr2 != '\0'; ++ptr2)
if (isdigit(*ptr2) == 0)
break;
if (*ptr2 == '\0')
*ptr = '\0'; /* foobar */
}
return name;
}
/*
* Parse the "st_mod" parameter.
*/
static int
parse_st_mod(void *arg)
{
char *mod_name, *sym;
struct st_mod *st_mod, *st_mod2;
struct ipa_st_mod *ipa_st_mod;
if ( (st_mod = mem_malloc(sizeof *st_mod, m_anon)) == NULL) {
logconferrx("mem_malloc failed");
return -1;
}
st_mod->mod_file = *(char **)arg;
if ( (st_mod->mod_handle = dl_open(st_mod->mod_file)) == NULL) {
logconferrx("dl_open(%s): %s", st_mod->mod_file, dl_error());
return -1;
}
if ( (mod_name = get_mod_name(st_mod->mod_file)) == NULL)
return -1;
if (mem_asprintf(m_anon, &sym, "%s_st_mod", mod_name) < 0) {
logconferrx("mem_asprintf failed");
return -1;
}
if ( (st_mod->ipa_st_mod = (struct ipa_st_mod *)dl_lookup_sym(st_mod->mod_handle, sym)) == NULL) {
logconferrx("given module is not an IPA statistics module or unknown symbol naming scheme is used");
return -1;
}
mem_free(sym, m_anon);
mem_free(mod_name, m_anon);
ipa_st_mod = st_mod->ipa_st_mod;
/* Check ipa_st_mod API version. */
if (ipa_st_mod->api_ver != IPA_ST_MOD_API_VERSION) {
logconferrx("module %s uses ipa_st_mod API version %u, my ipa_st_mod API version is %u",
st_mod->mod_file, ipa_st_mod->api_ver, IPA_DB_MOD_API_VERSION);
return -1;
}
/* Check if module is thread-safe or vice versa. */
#ifdef WITH_PTHREAD
if (!(ipa_st_mod->mod_flags & IPA_MOD_FLAG_PTHREAD_SAFE)) {
logconferrx("module %s must be thread-safe", st_mod->mod_file);
return -1;
}
#else
if (ipa_st_mod->mod_flags & IPA_MOD_FLAG_PTHREAD_SAFE) {
logconferrx("module %s must not be thread-safe", st_mod->mod_file);
return -1;
}
#endif /* WITH_PTHREAD */
if (strcmp(ipa_st_mod->st_name, "null") == 0) {
logconferrx("module's statistics name is \"null\", this is a name of the builtin statistics");
return -1;
}
if ( (st_mod2 = st_mod_by_name(ipa_st_mod->st_name)) != NULL) {
conferrx("duplicated statistics name \"%s\" in %s and %s modules",
ipa_st_mod->st_name, st_mod->mod_file, st_mod2->mod_file);
return -1;
}
if ( (st_mod2 = st_mod_by_prefix(ipa_st_mod->conf_prefix)) != NULL) {
conferrx("duplicated configuration prefix \"%s\" in %s and %s modules",
ipa_st_mod->conf_prefix, st_mod->mod_file, st_mod2->mod_file);
return -1;
}
ipa_st_mod->suppfunc = &suppfunc;
ipa_st_mod->memfunc = &memfunc;
if (init_conf_tbls(st_mod->mod_file, 1,
ipa_st_mod->conf_sect_tbl, &st_mod->conf_sect_hash,
ipa_st_mod->conf_param_tbl, &st_mod->conf_param_hash) < 0)
return -1;
if (ipa_st_mod->conf_init() < 0) {
conferrx("module %s: conf_init failed", st_mod->mod_file);
return -1;
}
SLIST_INSERT_HEAD(&st_mod_list, st_mod, link);
return 0;
}
/*
* Parse the "st_list" parameter.
*/
static int
parse_st_list(void *arg)
{
char *ptr;
const char *st_name;
struct st_elem *st;
struct st_set *set;
struct st_list *list;
const struct st_mod *st_mod;
const struct st_list **listp;
switch (section) {
#ifdef WITH_RULES
case IPA_CONF_SECT_RULE:
listp = &currule->st_list;
break;
#endif
case IPA_CONF_SECT_RULEPAT:
listp = &currulepat->st_list;
break;
#ifdef WITH_LIMITS
case IPA_CONF_SECT_LIMIT:
listp = &curlimit->st_list;
break;
#endif
#ifdef WITH_THRESHOLDS
case IPA_CONF_SECT_THRESHOLD:
listp = &curthreshold->st_list;
break;
#endif
default: /* IPA_CONF_SECT_GLOBAL */
listp = &global_st_list;
}
if (*listp != NULL) {
logconferrx("cannot redefine this parameter");
return -1;
}
set = NULL;
list = NULL;
for (ptr = *(char **)arg; ptr != NULL;) {
/* Get the name of the next statistics system. */
st_name = ptr;
if ( (ptr = strchr(ptr, ' ')) != NULL)
*ptr++ = '\0';
/* Handle "null" statistics system. */
if (strcmp(st_name, "null") == 0) {
if (list != NULL || ptr != NULL) {
logconferrx("builtin statistics system \"null\" cannot be used together with another statistics systems");
return -1;
}
*listp = &st_list_null;
return 0;
}
if ( (st_mod = st_mod_by_name(st_name)) == NULL) {
logconferrx("cannot find module with \"%s\" statistics system name", st_name);
return -1;
}
if (set != NULL) {
/* We already have set for current st_list parameter. */
STAILQ_FOREACH(st, list, link)
if (strcmp(st_name, st->ipa_st_mod->st_name) == 0) {
logconferrx("duplicated statistics system name \"%s\"",
st_name);
return -1;
}
} else {
/* Create new set for st_list parameter. */
if ( (set = mem_malloc(sizeof *set, m_anon)) == NULL) {
logconferrx("mem_malloc failed");
return -1;
}
list = &set->list;
STAILQ_INIT(list);
}
/* Add new st element to st_list. */
if ( (st = mem_malloc(sizeof *st, m_anon)) == NULL) {
logconferrx("mem_malloc failed");
return -1;
}
st->ipa_st_mod = st_mod->ipa_st_mod;
st->mod_file = st_mod->mod_file;
STAILQ_INSERT_TAIL(list, st, link);
}
/* New st_list --> add it to st_sets. */
*listp = list;
SLIST_INSERT_HEAD(&st_sets, set, link);
return 0;
}
/*
* Parse "posix_re_pattern" parameter.
*/
static int
parse_posix_re_pattern(void *arg)
{
posix_re_pattern = *(int *)arg;
return 0;
}
/*
* Parse "debug_st_null" parameter.
*/
static int
parse_debug_st_null(void *arg)
{
uint32_t level = *(uint32_t *)arg;
if (level > 1) {
logconferrx("too big debug level, max level is 1");
return -1;
}
debug_st_null = (int)level;
return 0;
}
/*
* Check file path: non empty string and should start with '/'.
*/
static int
check_file_path(const char *fname, const char *what)
{
if (*fname == '\0') {
logconferrx("argument should be a non-empty string");
return -1;
}
if (*fname != '/') {
logconferrx("%s should be given with absolute path", what);
return -1;
}
return 0;
}
/*
* Parse the "include" parameter.
*/
static int
parse_include(void *arg)
{
char *fname = *(char **)arg;
struct parser_pbuf *pbuf;
/* Save offset of current configuration file and close it. */
if ( (parser_curpbuf->foff = ftell(parser_curpbuf->fp)) < 0) {
logconferr("ftell(%s)", parser_curpbuf->fname);
return -1;
}
if (fclose(parser_curpbuf->fp) != 0) {
logconferr("fclose(%s)", parser_curpbuf->fname);
return -1;
}
/* Validate an argument. */
if (check_file_path(fname, "file") < 0)
return -1;
/* Check security of configuration file. */
if (check_conf_file(fname) < 0)
return -1;
/* Open included configuration file and put it to stack. */
if ( (pbuf = parser_new_pbuf(0)) == NULL)
return -1;
if ( (pbuf->fp = fopen(fname, "r")) == NULL) {
logconferr("fopen(%s, \"r\")", fname);
return -1;
}
pbuf->fname = fname;
return parser_push_pbuf(pbuf);
}
/*
* Parse the "include_files" parameter.
*/
static int
parse_include_files(void *arg)
{
char *dir = *(char **)arg, *pat, *fname;
int include_something;
DIR *dirp;
regex_t re;
struct stat statbuf;
struct dirent *dp;
struct parser_pbuf *pbuf, *old_curpbuf;
/* Validate an argument. */
if (check_file_path(dir, "directory") < 0)
return -1;
pat = strrchr(dir, '/');
*pat++ = '\0';
if (posix_re_pattern > 0)
if ( (re_errcode = regcomp(&re, pat, REG_EXTENDED|REG_NOSUB)) != 0) {
regerror(re_errcode, (regex_t *)NULL, re_errbuf, sizeof re_errbuf);
logconferrx("cannot compile regular expression: regcomp(\"%s\"): %s",
pat, re_errbuf);
return -1;
}
/* Check security of the given directory. */
if (lstat(dir, &statbuf) < 0) {
logconferr("lstat(%s)", dir);
return -1;
}
if (!S_ISDIR(statbuf.st_mode)) {
logconferrx("given pathname is not a directory");
return -1;
}
if ( (dirp = opendir(dir)) == NULL) {
logconferr("opendir(%s)", dir);
return -1;
}
include_something = 0;
old_curpbuf = parser_curpbuf;
for (;;) {
errno = 0;
if ( (dp = readdir(dirp)) == NULL) {
if (errno != 0) {
logconferr("readdir(%s)", dir);
return -1;
}
break;
}
if (*dp->d_name == '.')
/* Optimize and don't use strcmp(3) here for "." and "..". */
switch (*(dp->d_name + 1)) {
case '\0':
continue;
case '.':
if (*(dp->d_name + 2) == '\0')
continue;
}
if (posix_re_pattern != 1) {
if (fnmatch(pat, dp->d_name, FNM_PERIOD) == FNM_NOMATCH)
continue;
} else {
if (regexec(&re, dp->d_name, 0, (regmatch_t *)NULL, 0) != 0)
continue;
}
if (mem_asprintf(m_parser, &fname, "%s/%s", dir, dp->d_name) < 0) {
logconferrx("mem_asprintf failed");
return -1;
}
if (lstat(fname, &statbuf) < 0) {
logconferr("lstat(%s)", fname);
return -1;
}
if (!S_ISREG(statbuf.st_mode)) {
mem_free(fname, m_parser);
continue;
}
if ( (pbuf = parser_new_pbuf(0)) == NULL)
return -1;
pbuf->fname = fname;
pbuf->foff = 0;
if (parser_push_pbuf(pbuf) < 0)
return -1;
include_something = 1;
}
if (posix_re_pattern > 0)
regfree(&re);
if (closedir(dirp) < 0) {
logconferr("closedir(%s)", dir);
return -1;
}
mem_free(dir, m_parser);
if (include_something) {
if ( (old_curpbuf->foff = ftell(old_curpbuf->fp)) < 0) {
logconferr("ftell(%s)", old_curpbuf->fname);
return -1;
}
if (fclose(old_curpbuf->fp) != 0) {
logconferr("fclose(%s)", old_curpbuf->fname);
return -1;
}
if ( (parser_curpbuf->fp = fopen(parser_curpbuf->fname, "r")) == NULL) {
logconferr("fopen(%s, \"r\")", parser_curpbuf->fname);
return -1;
}
}
return 0;
}
static const u_int sect_root[] = { IPA_CONF_SECT_ROOT, 0 };
static const u_int sect_for_st_list[] = { IPA_CONF_SECT_GLOBAL, IPA_CONF_SECT_RULE, IPA_CONF_SECT_LIMIT, IPA_CONF_SECT_THRESHOLD, IPA_CONF_SECT_RULEPAT, 0 };
static const u_int sect_rulepat[] = { IPA_CONF_SECT_RULEPAT, 0 };
#ifdef WITH_ANY_LIMITS
static const u_int sect_for_any_limit[] = { IPA_CONF_SECT_RULE,
IPA_CONF_SECT_RULEPAT, 0
};
#endif
/*
* Sections in ipastat.conf(5).
*/
static ipa_conf_sect conf_sect_tbl[] = {
{ "global", IPA_CONF_SECT_GLOBAL, 0, NULL, NULL, IPA_CONF_TYPE_MISC, sect_root, parse_global },
{ "rule", IPA_CONF_SECT_RULE, 1, NULL, NULL, IPA_CONF_TYPE_MISC, sect_root, parse_rule },
{ "rulepat", IPA_CONF_SECT_RULEPAT, 1, NULL, NULL, IPA_CONF_TYPE_STRING, sect_root, parse_rulepat },
#ifdef WITH_LIMITS
{ "limit", IPA_CONF_SECT_LIMIT, 1, NULL, NULL, IPA_CONF_TYPE_MISC, sect_for_any_limit, parse_limit },
#endif
#ifdef WITH_THRESHOLDS
{ "threshold", IPA_CONF_SECT_THRESHOLD,1, NULL, NULL, IPA_CONF_TYPE_MISC, sect_for_any_limit, parse_threshold },
#endif
{ NULL, 0, 0, NULL, NULL, IPA_CONF_TYPE_MISC, 0, NULL }
};
#define PAT_LIST "^[^ \"]+( [^ \"]+)*$"
/*
* Parameters in ipastat.conf(5).
*/
static ipa_conf_param conf_param_tbl[] = {
{ "st_mod", 1, NULL, NULL, IPA_CONF_TYPE_STRING, sect_root, parse_st_mod },
{ "include", 1, NULL, NULL, IPA_CONF_TYPE_STRING, NULL, parse_include },
{ "include_files", 1, NULL, NULL, IPA_CONF_TYPE_STRING, NULL, parse_include_files },
{ "posix_re_pattern", 1, NULL, NULL, IPA_CONF_TYPE_BOOLEAN, sect_root, parse_posix_re_pattern },
{ "st_list", -1, PAT_LIST, ®_list, IPA_CONF_TYPE_MISC, sect_for_st_list, parse_st_list },
{ "check_next_rulepat", 1, NULL, NULL, IPA_CONF_TYPE_BOOLEAN, sect_rulepat, parse_check_next_rulepat },
{ "debug_st_null", 1, NULL, NULL, IPA_CONF_TYPE_UINT32, sect_root, parse_debug_st_null },
{ "dynamic_rules", 1, NULL, NULL, IPA_CONF_TYPE_BOOLEAN, sect_root, parse_dynamic_rules },
#ifdef WITH_LIMITS
{ "dynamic_limits", 1, NULL, NULL, IPA_CONF_TYPE_BOOLEAN, sect_root, parse_dynamic_limits },
#endif
#ifdef WITH_THRESHOLDS
{ "dynamic_thresholds", 1, NULL, NULL, IPA_CONF_TYPE_BOOLEAN, sect_root, parse_dynamic_thresholds },
#endif
{ NULL, 0, NULL, NULL, 0, 0, NULL }
};
int
configure(PARSING_MODE mode)
{
int real_config;
char *ptr;
const u_int *id_ptr;
const char *prefix; /* Configuration prefix. */
struct rule *rule;
struct rulepat *rulepat;
/* Stack of nested configuration sections. */
struct section_stack {
SLIST_ENTRY(section_stack) link;
u_int section; /* Section ID. */
} *section_stack_ptr;
SLIST_HEAD(, section_stack) section_stack_list = SLIST_HEAD_INITIALIZER(section_stack_list);
const struct st_mod *st_mod;
struct parser_pbuf *pbuf;
struct conf_sect_hash *conf_sect_hash;
struct conf_param_hash *conf_param_hash;
const struct conf_sect_hash *conf_sect_hash_ptr;
const struct conf_param_hash *conf_param_hash_ptr;
const ipa_conf_sect *conf_sect_tbl_ptr, *conf_sect;
const ipa_conf_param *conf_param_tbl_ptr, *conf_param;
int in_own_sect; /* 1, if in own ipastat.conf(5) section. */
const struct get_arg_tbl *get_arg_ptr;
real_config = mode != TEST_PARSING;
xvlogmsgx = vconferrx_priority;
/* Set wrappers for log functions. */
mvlogmsgx = mvlogmsgx_conf_wrapper;
parser_vlogmsgx = parser_vlogmsgx_wrapper;
if ((m_result = mem_type_new_local(MTYPE_NAME(result), "Memory for query results", 0)) == NULL ||
(m_parser = mem_type_new_local(MTYPE_NAME(parser), "Memory of parser", MEMTYPE_FLAGS)) == NULL) {
logmsgx(IPA_LOG_ERR, "configure: mem_type_new_local failed");
goto parsing_failed;
}
global_st_list = NULL;
debug_st_null = -1;
SLIST_INIT(&st_mod_list);
if ( (conf_sect_hentry_mzone = mzone_init(MZONE_NAME(conf_sect_hentry),
"Config sections hash entries", 0, sizeof(struct conf_sect_hentry),
CONF_SECT_HENTRY_NSIZE, CONF_SECT_HENTRY_NALLOC)) == NULL)
goto parsing_failed;
if ( (conf_param_hentry_mzone = mzone_init(MZONE_NAME(conf_param_hentry),
"Config parameters hash entries", 0, sizeof(struct conf_param_hentry),
CONF_PARAM_HENTRY_NSIZE, CONF_PARAM_HENTRY_NALLOC)) == NULL)
goto parsing_failed;
init_rules_hash();
if ( (rule_mzone = mzone_init(MZONE_NAME(rule), "Rules", 0,
sizeof(struct rule), RULE_NSIZE, RULE_NALLOC)) == NULL)
goto parsing_failed;
if ( (rulepat_mzone = mzone_init(MZONE_NAME(rulepat), "Rules patterns", 0,
sizeof(struct rulepat), RULEPAT_NSIZE, RULEPAT_NALLOC)) == NULL)
goto parsing_failed;
#ifdef WITH_LIMITS
if ( (limit_mzone = mzone_init(MZONE_NAME(limit), "Limits", 0,
sizeof(struct limit), LIMIT_NSIZE, LIMIT_NALLOC)) == NULL)
goto parsing_failed;
#endif
#ifdef WITH_THRESHOLDS
if ( (threshold_mzone = mzone_init(MZONE_NAME(threshold), "Thresholds", 0,
sizeof(struct threshold), THRESHOLD_NSIZE, THRESHOLD_NALLOC)) == NULL)
goto parsing_failed;
#endif
posix_re_pattern = -1;
if (check_conf_file(ipastat_conf_file) < 0)
goto parsing_failed;
section = IPA_CONF_SECT_ROOT;
memfunc.m_parser = m_parser;
/* Init parser. */
if (parser_init() < 0) {
logmsgx(IPA_LOG_ERR, "configure: parser_init failed");
return -1;
}
/* Init first pbuf. */
if ( (pbuf = parser_new_pbuf(0)) == NULL)
return -1;
pbuf->fname = ipastat_conf_file;
if ( (pbuf->fp = fopen(ipastat_conf_file, "r")) == NULL) {
conferrx("fopen(%s, \"r\"): %s", ipastat_conf_file, strerror(errno));
goto parsing_failed;
}
if (parser_push_pbuf(pbuf) < 0)
goto parsing_failed;
/* Needed for log messages. */
curparam = cursect = curmodfile = NULL;
if (build_conf_regexp() < 0) {
conferrx("cannot build additional regular expressions needed for parsing");
goto parsing_failed;
}
if (init_conf_tbls((char *)NULL, 1,
conf_sect_tbl, &conf_sect_hash,
conf_param_tbl, &conf_param_hash) < 0)
goto parsing_failed;
/* Set ipa.conf(5) configuration tables. */
conf_sect_tbl_ptr = conf_sect_tbl;
conf_param_tbl_ptr = conf_param_tbl;
conf_sect_hash_ptr = conf_sect_hash;
conf_param_hash_ptr = conf_param_hash;
in_own_sect = 1;
for (;;) {
switch (parser_read_string()) {
case 1:
/* Successfully read one logical line. */
break;
case 0:
/* EOF of current configuration file. */
if (fclose(parser_curpbuf->fp) != 0) {
conferrx("fclose(%s): %s", parser_curpbuf->fname, strerror(errno));
goto parsing_failed;
}
if (parser_curpbuf->fname != ipastat_conf_file)
mem_free(parser_curpbuf->fname, m_parser);
parser_pop_pbuf();
if (parser_curpbuf != NULL) {
/* Init previous file for parsing. */
if ( (parser_curpbuf->fp = fopen(parser_curpbuf->fname, "r")) == NULL) {
conferrx("fopen(%s, \"r\"): %s", parser_curpbuf->fname, strerror(errno));
goto parsing_failed;
}
if (fseek(parser_curpbuf->fp, parser_curpbuf->foff, SEEK_SET) < 0) {
conferrx("fseek(%s, %ld, SEEK_SET): %s",
parser_curpbuf->fname, parser_curpbuf->foff, strerror(errno));
goto parsing_failed;
}
continue;
}
goto end_of_parsing;
default: /* -1 */
goto parsing_failed;
}
switch (parser_token_id) {
case TOKEN_ID_SECTION_BEGIN:
/* Put previous section on top of sections stack. */
if ( (section_stack_ptr = mem_malloc(sizeof *section_stack_ptr, m_anon)) == NULL) {
conferrx("configure: mem_malloc failed");
goto parsing_failed;
}
section_stack_ptr->section = section;
SLIST_INSERT_HEAD(§ion_stack_list, section_stack_ptr, link);
/* Get name of current section. */
cursect = parser_token; /* "section" or "prefix:section". */
if (in_own_sect) {
/* Previous section is not a custom section. */
if ( (ptr = strchr(cursect, ':')) != NULL) {
*ptr = '\0';
prefix = cursect;
cursect = ptr + 1; /* Fix section name. */
if ( (st_mod = st_mod_by_prefix(prefix)) != NULL) {
conf_sect_hash_ptr = st_mod->conf_sect_hash;
conf_param_hash_ptr = st_mod->conf_param_hash;
conf_sect_tbl_ptr = st_mod->ipa_st_mod->conf_sect_tbl;
conf_param_tbl_ptr = st_mod->ipa_st_mod->conf_param_tbl;
curmodfile = st_mod->mod_file;
} else {
logconferrx("cannot find module with \"%s\" configuration prefix",
prefix);
goto parsing_failed;
}
in_own_sect = 0;
}
}
/* Find ID of section. */
if ( (conf_sect = find_conf_sect(conf_sect_tbl_ptr, conf_sect_hash_ptr, cursect)) == NULL) {
logconferrx("unknown section");
goto parsing_failed;
}
/* Check if current section is used in correct place. */
if ( (id_ptr = conf_sect->sect_where) != NULL)
for (;; ++id_ptr) {
if (*id_ptr == section)
break;
if (*id_ptr == 0)
goto section_not_expected;
}
/* Check number of section's arguments. */
if (conf_sect->arg_nargs >= 0) {
if (parser_nargs != conf_sect->arg_nargs) {
logconferrx("wrong number of arguments (has %d, should have %d)",
parser_nargs, conf_sect->arg_nargs);
goto parsing_failed;
}
} else {
if (parser_nargs < -conf_sect->arg_nargs) {
logconferrx("this section should have at least %d argument%s",
-conf_sect->arg_nargs, conf_sect->arg_nargs == -1 ? "" : "s");
goto parsing_failed;
}
}
/* Validate arguments if needed. */
if (conf_sect->arg_regexp != NULL)
if (regexec(conf_sect->arg_regexp, parser_args, 0, (regmatch_t *)NULL, 0) != 0) {
logconferrx("wrong format of an argument");
goto parsing_failed;
}
section = conf_sect->sect_id;
/* Register config event in module. */
if (!in_own_sect)
if (st_mod_conf_event(st_mod, IPA_CONF_EVENT_CUSTOM_SECT_BEGIN, section, (void *)NULL) < 0)
goto parsing_failed;
/* Parse it. */
if (conf_sect->arg_type <= IPA_CONF_TYPE_MISC) {
get_arg_ptr = &get_arg_tbl[conf_sect->arg_type];
if (get_arg_ptr->reg != NULL)
if (regexec(get_arg_ptr->reg, parser_args, 0, (regmatch_t *)NULL, 0) != 0) {
logconferrx("wrong format of an argument");
goto parsing_failed;
}
if (get_arg_ptr->get_arg(get_arg_ptr->argp) < 0)
goto parsing_failed;
if (conf_sect->arg_parse != NULL)
if (conf_sect->arg_parse(get_arg_ptr->argp) < 0)
goto parsing_failed;
} else {
logconferrx("internal error: unknown type %u of function's argument",
conf_sect->arg_type);
goto parsing_failed;
}
break;
case TOKEN_ID_PARAMETER:
curparam = parser_token; /* "param" or "prefix:param". */
if (in_own_sect) {
/* Current section is not a custom section. */
if ( (ptr = strchr(curparam, ':')) != NULL) {
*ptr = '\0';
prefix = curparam;
curparam = ptr + 1; /* Fix parameter name. */
if ( (st_mod = st_mod_by_prefix(prefix)) != NULL) {
conf_param_hash_ptr = st_mod->conf_param_hash;
conf_param_tbl_ptr = st_mod->ipa_st_mod->conf_param_tbl;
curmodfile = st_mod->mod_file;
} else {
logconferrx("cannot find module with \"%s\" configuration prefix",
prefix);
goto parsing_failed;
}
}
}
/* Find ID of parameter. */
if ( (conf_param = find_conf_param(conf_param_tbl_ptr, conf_param_hash_ptr, curparam)) == NULL) {
logconferrx("unknown parameter");
goto parsing_failed;
}
/* Check if parameter is used in correct place. */
if ( (id_ptr = conf_param->param_where) != NULL)
for (;; ++id_ptr) {
if (*id_ptr == section)
break;
if (*id_ptr == 0) {
logconferrx("this parameter is not expected here");
goto parsing_failed;
}
}
/* Check number of parameters's arguments. */
if (conf_param->arg_nargs >= 0) {
if (parser_nargs != conf_param->arg_nargs) {
logconferrx("wrong number of arguments (has %d, should have %d)",
parser_nargs, conf_param->arg_nargs);
goto parsing_failed;
}
} else {
if (parser_nargs < -conf_param->arg_nargs) {
logconferrx("this parameter should have at least %d argument%s",
-conf_param->arg_nargs, conf_param->arg_nargs == -1 ? "": "s");
goto parsing_failed;
}
}
/* Validate arguments if needed. */
if (conf_param->arg_regexp != NULL)
if (regexec(conf_param->arg_regexp, parser_args, 0, (regmatch_t *)NULL, 0) != 0) {
logconferrx("wrong format of an argument");
goto parsing_failed;
}
/* Parse it. */
if (conf_param->arg_type <= IPA_CONF_TYPE_MISC) {
get_arg_ptr = &get_arg_tbl[conf_param->arg_type];
if (get_arg_ptr->reg != NULL)
if (regexec(get_arg_ptr->reg, parser_args, 0, (regmatch_t *)NULL, 0) != 0) {
logconferrx("wrong format of an argument");
goto parsing_failed;
}
if (get_arg_ptr->get_arg(get_arg_ptr->argp) < 0)
goto parsing_failed;
if (conf_param->arg_parse != NULL)
if (conf_param->arg_parse(get_arg_ptr->argp) < 0)
goto parsing_failed;
curparam = NULL;
} else {
logconferrx("internal error: unknown type %u of an argument",
conf_param->arg_type);
goto parsing_failed;
}
if (curmodfile != NULL && in_own_sect) {
curmodfile = NULL; /* Was set above for logxxx() functions. */
/* Restore ipa.conf(5) parameters tables. */
conf_param_hash_ptr = conf_param_hash;
conf_param_tbl_ptr = conf_param_tbl;
}
break;
default: /* TOKEN_ID_SECTION_END */
if (section > IPA_CONF_SECT_CUSTOM_OFFSET) {
if (st_mod_conf_event(st_mod, IPA_CONF_EVENT_CUSTOM_SECT_END, section, (void *)NULL) < 0)
goto parsing_failed;
} else {
switch (section) {
case IPA_CONF_SECT_RULE:
if (mod_conf_event(IPA_CONF_EVENT_RULE_END, currule->ruleno, (void *)NULL) < 0)
goto parsing_failed;
break;
case IPA_CONF_SECT_RULEPAT:
if (mod_conf_event(IPA_CONF_EVENT_RULEPAT_END, currulepat->rulepatno, (void *)NULL) < 0)
goto parsing_failed;
break;
#ifdef WITH_LIMITS
case IPA_CONF_SECT_LIMIT:
(void)parser_local_sym_del("limit");
if (mod_conf_event(IPA_CONF_EVENT_LIMIT_END, curlimit->limitno, (void *)NULL) < 0)
goto parsing_failed;
break;
#endif /* WITH_LIMITS */
#ifdef WITH_THRESHOLDS
case IPA_CONF_SECT_THRESHOLD:
(void)parser_local_sym_del("threshold");
if (mod_conf_event(IPA_CONF_EVENT_THRESHOLD_END, curthreshold->thresholdno, (void *)NULL) < 0)
goto parsing_failed;
break;
#endif /* WITH_THRESHOLDS */
case IPA_CONF_SECT_GLOBAL:
if (mod_conf_event(IPA_CONF_EVENT_GLOBAL_END, 0, (void *)NULL) < 0)
goto parsing_failed;
break;
}
}
section_stack_ptr = SLIST_FIRST(§ion_stack_list);
SLIST_REMOVE_HEAD(§ion_stack_list, link);
section = section_stack_ptr->section;
mem_free(section_stack_ptr, m_anon);
if (!in_own_sect && section < IPA_CONF_SECT_CUSTOM_OFFSET) {
/* We were in custom section and previous section is not a custom section. */
curmodfile = NULL;
/* Restore ipastat.conf(5) configuration tables. */
conf_sect_tbl_ptr = conf_sect_tbl;
conf_param_tbl_ptr = conf_param_tbl;
conf_sect_hash_ptr = conf_sect_hash;
conf_param_hash_ptr = conf_param_hash;
in_own_sect = 1;
}
}
}
end_of_parsing:
deinit_conf_tbls(0, conf_sect_tbl, conf_sect_hash,
conf_param_tbl, conf_param_hash);
mzone_deinit(conf_sect_hentry_mzone);
mzone_deinit(conf_param_hentry_mzone);
if (mzone_is_empty(rulepat_mzone)) {
mzone_deinit(rulepat_mzone);
rulepat_mzone = NULL;
}
if (parser_deinit() < 0)
goto parsing_failed;
SLIST_FOREACH(st_mod, &st_mod_list, link) {
if (mimic_real_config)
if (st_mod->ipa_st_mod->conf_mimic_real() < 0) {
conferrx("module %s: conf_mimic_real failed", st_mod->mod_file);
goto parsing_failed;
}
deinit_conf_tbls(1,
st_mod->ipa_st_mod->conf_sect_tbl, st_mod->conf_sect_hash,
st_mod->ipa_st_mod->conf_param_tbl, st_mod->conf_param_hash);
if (st_mod->ipa_st_mod->conf_deinit() < 0) {
conferrx("module %s: conf_deinit failed", st_mod->mod_file);
goto parsing_failed;
}
}
if (real_config || mimic_real_config) {
/* Set global parameters. */
global_section_set = 1;
if (global_st_list == NULL)
global_st_list = &st_list_null;
if (debug_st_null < 0)
debug_st_null = 0;
if (dynamic_rules < 0)
dynamic_rules = 0;
#ifdef WITH_LIMITS
if (dynamic_limits < 0)
dynamic_limits = 0;
#endif
#ifdef WITH_THRESHOLDS
if (dynamic_thresholds < 0)
dynamic_thresholds = 0;
#endif
/* Inherit some settings for rulepat{} from global{}. */
STAILQ_FOREACH(rulepat, &rulepats_list, link)
if (rulepat->check_next_rulepat <= 0) {
rulepat->check_next_rulepat = 0;
if (rulepat->st_list == NULL)
rulepat->st_list = global_st_list;
}
}
if (mimic_real_config) {
STAILQ_FOREACH(rule, &rules_list, list) {
STAILQ_FOREACH(rulepat, &rulepats_list, link) {
if (regexec(&rulepat->reg, rule->rule_name, 0, (regmatch_t *)NULL, 0) == 0) {
/* Inherit some settings for rule{} from rulepat{}. */
if (rule->st_list == NULL)
rule->st_list = rulepat->st_list;
#ifdef WITH_LIMITS
if (STAILQ_EMPTY(&rule->limits) && !STAILQ_EMPTY(&rulepat->limits))
if (copy_limits(rule, &rulepat->limits) < 0) {
conferrx("rule %s: cannot copy all limits from rulepat %s",
rule->rule_name, parser_buf_to_string(rulepat->pat));
goto parsing_failed;
}
#endif
#ifdef WITH_THRESHOLDS
if (STAILQ_EMPTY(&rule->thresholds) && !STAILQ_EMPTY(&rulepat->thresholds))
if (copy_thresholds(rule, &rulepat->thresholds) < 0) {
conferrx("rule %s: cannot copy all thresholds from rulepat %s",
rule->rule_name, parser_buf_to_string(rulepat->pat));
goto parsing_failed;
}
#endif
if (mod_conf_inherit(rulepat, rule) < 0) {
conferrx("configure: mod_conf_inherit failed");
goto parsing_failed;
}
if (rulepat->check_next_rulepat == 0)
break;
}
}
if (rulepat == NULL) {
/* Inherit some settings for rule{} from global{}. */
if (rule->st_list == NULL)
rule->st_list = global_st_list;
}
}
}
mvlogmsgx = mvlogmsgx_wrapper;
xvlogmsgx = vlogmsgx;
return 0;
section_not_expected:
logconferrx("this section is not expected here");
parsing_failed:
logmsgx(IPA_LOG_ERR, "configuration file(s) parsing failed!");
return -1;
}
/*
* Unload all modules and free memory used by structures which
* describe loaded modules. Note that any pointer should not references
* any data in unloaded modules' memory. So, the best place when
* to call this function: after memfunc_deinit() and before checking
* amount of not freed memory.
*/
static int
unload_all_mods(void)
{
struct st_mod *st_mod, *st_mod_next;
for (st_mod = SLIST_FIRST(&st_mod_list); st_mod != NULL; st_mod = st_mod_next) {
st_mod_next = SLIST_NEXT(st_mod, link);
if (dl_close(st_mod->mod_handle) < 0) {
logmsgx(IPA_LOG_ERR, "module %s: dl_close failed: %s",
st_mod->mod_file, dl_error());
return -1;
}
mem_free(st_mod->mod_file, m_parser);
mem_free(st_mod, m_anon);
}
return 0;
}
/*
* Free all resources allocated during work.
*/
int
free_all(void)
{
u_int n;
free_rules();
if (!STAILQ_EMPTY(&rules_list)) {
logmsgx(IPA_LOG_ERR, "internal error: free_all: rules_list is not empty");
return -1;
}
if (!STAILQ_EMPTY_HEAD_VALID(&rules_list)) {
logmsgx(IPA_LOG_ERR, "internal error: free_all: empty rules_list list is in inconsistent state");
return -1;
}
if ( (n = mzone_nused(rule_mzone)) != 0) {
logmsgx(IPA_LOG_ERR, "internal error: free_all: rule_mzone is not empty: %u", n);
return -1;
}
mzone_deinit(rule_mzone);
#ifdef WITH_LIMITS
if ( (n = mzone_nused(limit_mzone)) != 0) {
logmsgx(IPA_LOG_ERR, "internal error: free_all: limit_mzone is not empty: %u", n);
return -1;
}
mzone_deinit(limit_mzone);
#endif
#ifdef WITH_THRESHOLDS
if ( (n = mzone_nused(threshold_mzone)) != 0) {
logmsgx(IPA_LOG_ERR, "internal error: free_all: threshold_mzone is not empty: %u", n);
return -1;
}
mzone_deinit(threshold_mzone);
#endif
free_rulepats();
free_st_lists();
memfunc_deinit_1(0);
if (unload_all_mods() < 0) {
logmsgx(IPA_LOG_ERR, "free_all: cannot correctly unload all modules");
return -1;
}
memfunc_deinit_2(0);
return 0;
}
static void
mod_conf_show(u_int sect_id, u_int no)
{
const struct st_mod *st_mod;
need_nl = 0;
SLIST_FOREACH(st_mod, &st_mod_list, link)
st_mod->ipa_st_mod->conf_show(sect_id, no);
if (sect_id == IPA_CONF_SECT_ROOT)
print_nl();
}
static void
show_st_list(const struct st_list *list)
{
const struct st_elem *st;
if (list != NULL) {
printf("%*sst_list =", conf_indent, "");
if (list != &st_list_null)
STAILQ_FOREACH(st, list, link)
printf(" %s", st->ipa_st_mod->st_name);
else
printf(" null");
print_eol();
}
}
#ifdef WITH_LIMITS
static void
show_limits(const struct limits_list *list)
{
const struct limit *limit;
STAILQ_FOREACH(limit, list, link) {
conf_indent = 2 * CONF_INDENT;
printf("%*slimit %s {\n", CONF_INDENT, "", limit->limit_name);
show_st_list(limit->st_list);
mod_conf_show(IPA_CONF_SECT_LIMIT, limit->limitno);
printf("%*s}\n", CONF_INDENT, "");
}
}
#endif
#ifdef WITH_THRESHOLDS
static void
show_thresholds(const struct thresholds_list *list)
{
const struct threshold *threshold;
STAILQ_FOREACH(threshold, list, link) {
conf_indent = 2 * CONF_INDENT;
printf("%*sthreshold %s {\n", CONF_INDENT, "", threshold->threshold_name);
show_st_list(threshold->st_list);
mod_conf_show(IPA_CONF_SECT_THRESHOLD, threshold->thresholdno);
printf("%*s}\n", CONF_INDENT, "");
}
}
#endif
void
show_config(void)
{
const struct rule *rule;
const struct rulepat *rulepat;
const struct st_mod *st_mod;
printf("\
/*\n\
* This output is not identical to the original content of the\n\
* configuration file(s), this is just how ipastat(8) and IPA modules\n\
* see their configurations. Any \"include\" or \"include_files\"\n\
* parameters are not printed and all macro variables are expanded.\n\
*/\n\n");
if (mimic_real_config)
printf("/* Mimic real configuration regime. */\n\n");
conf_indent = mod_indent = 0;
SLIST_FOREACH(st_mod, &st_mod_list, link) {
printf("st_mod ");
print_string(st_mod->mod_file);
print_eol();
need_nl = 1;
}
print_nl();
if (posix_re_pattern >= 0) {
printf("posix_re_pattern = ");
print_boolean(posix_re_pattern);
print_eol();
need_nl = 1;
}
print_nl();
if (debug_st_null >= 0) {
printf("debug_st_null = %d", debug_st_null);
print_eol();
need_nl = 1;
print_nl();
}
if (dynamic_rules >= 0) {
printf("dynamic_rules = ");
print_boolean(dynamic_rules);
print_eol();
need_nl = 1;
}
#ifdef WITH_LIMITS
if (dynamic_limits >= 0) {
printf("dynamic_limits = ");
print_boolean(dynamic_limits);
print_eol();
need_nl = 1;
}
#endif
#ifdef WITH_THRESHOLDS
if (dynamic_thresholds >= 0) {
printf("dynamic_thresholds = ");
print_boolean(dynamic_thresholds);
print_eol();
need_nl = 1;
}
#endif
print_nl();
mod_conf_show(IPA_CONF_SECT_ROOT, 0);
if (global_section_set) {
conf_indent = CONF_INDENT;
printf("global {\n");
show_st_list(global_st_list);
mod_conf_show(IPA_CONF_SECT_GLOBAL, 0);
printf("}\n\n");
}
STAILQ_FOREACH(rulepat, &rulepats_list, link) {
conf_indent = CONF_INDENT;
printf("rulepat ");
print_string(rulepat->pat);
printf(" {\n");
if (rulepat->check_next_rulepat >= 0) {
printf("%*scheck_next_rulepat = ", CONF_INDENT, "");
print_boolean(rulepat->check_next_rulepat);
print_eol();
}
show_st_list(rulepat->st_list);
mod_conf_show(IPA_CONF_SECT_RULE, rulepat->rulepatno);
#ifdef WITH_LIMITS
show_limits(&rulepat->limits);
#endif
#ifdef WITH_THRESHOLDS
show_thresholds(&rulepat->thresholds);
#endif
printf("}\n\n");
}
STAILQ_FOREACH(rule, &rules_list, list) {
conf_indent = CONF_INDENT;
printf("rule %s {\n", rule->rule_name);
show_st_list(rule->st_list);
mod_conf_show(IPA_CONF_SECT_RULE, rule->ruleno);
#ifdef WITH_LIMITS
show_limits(&rule->limits);
#endif
#ifdef WITH_THRESHOLDS
show_thresholds(&rule->thresholds);
#endif
printf("}\n\n");
}
}
syntax highlighted by Code2HTML, v. 0.9.1