/*-
 * 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,		&reg_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(&section_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(&section_stack_list);
			SLIST_REMOVE_HEAD(&section_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