/*-
 * Copyright (c) 1999-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: ipa_conf.c,v 1.3.2.8 2007/05/11 16:29:59 simon Exp $";
#endif /* !lint */

#include <ctype.h>
#include <errno.h>
#include <fnmatch.h>
#include <stdlib.h>
#include <limits.h>
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/queue.h>
#include <regex.h>
#include <stdarg.h>

#include <sys/socket.h>
#include <sys/un.h>

#include "ipa_mod.h"

#include "dlapi.h"
#include "confcommon.h"
#include "memfunc.h"
#include "parser.h"
#include "pathnames.h"

#include "ipa_ac.h"
#include "ipa_db.h"
#include "ipa_time.h"

#include "ipa_cmd.h"
#include "ipa_conf.h"
#include "ipa_ctl.h"
#include "ipa_log.h"
#include "ipa_main.h"
#include "ipa_rules.h"
#include "ipa_autorules.h"


char	*ipa_conf_file = IPA_CONF_FILE;	/* -f <conf-file> */

int	mimic_real_config = 0;		/* 1, if -tt. */

#ifdef CTL_CHECK_CREDS
static regex_t	reg_acl;
static regex_t	reg_ctl_acl_class;
#endif

/*
 * IPA_CONF_SECT_* with 1-7 values are defined in the ipa_mod.h file.
 * These are local values, modules should not use them.  Even if we have
 * some WITHOUT_xxx macros let's #define all SECT_xxx here, else we have
 * to #ifdef too many lines in conf_sect_tbl[] and conf_param_tbl[],
 * which is really not necessary.
 */

#define SECT_SUBLIMIT		 8

/* Next sections can contain "exec" parameters. */
#define SECT_RESTART		 9
#define SECT_REACH		10
#define SECT_EXPIRE		11
#define SECT_STARTUP		12
#define SECT_SHUTDOWN		13
#define SECT_IF_REACHED		14
#define SECT_IF_NOT_REACHED	15
#define SECT_IF_ALL_REACHED	16
#define SECT_IF_ANY_REACHED	17
#define SECT_IF_ANY_NOT_REACHED	18
#define SECT_IF_ALL_NOT_REACHED	19
#define SECT_BELOW_THRESHOLD	20
#define SECT_EQUAL_THRESHOLD	21
#define SECT_ABOVE_THRESHOLD	22

#ifdef WITH_RULES
static struct rule *currule;		/* Current rule. */
#endif

static struct rulepat *currulepat;	/* Current rulepat. */
static u_int	rulepatno;

#ifdef WITH_AUTORULES
static struct autorule *curautorule;	/* Current autorule. */
#endif

#ifdef WITH_LIMITS
static struct limit	*curlimit;	/* Current limit. */
static u_int		limitno;	/* Order number of the current limit. */
static regex_t		reg_texp;	/* Regular expression for +X like arguments. */
static struct limits_list *limits_list;
#endif

#ifdef WITH_SUBLIMITS
static struct sublimit	*cursublimit;	/* Current sublimit. */
#endif

#ifdef WITH_THRESHOLDS
static struct threshold *curthreshold;	/* Current threshold. */
static u_int		thresholdno;	/* Order number of current threshold. */
static struct thresholds_list *thresholds_list;
static regex_t		reg_thr_balance;/* Regular expression for "threshold_balance". */
#endif

static struct cmd_list	*curcmdl;	/* Current cmd list. */

/* Internal "null" accounting system and database. */
static struct ac_list ac_list_null = STAILQ_HEAD_INITIALIZER(ac_list_null);
static struct db_list db_list_null = STAILQ_HEAD_INITIALIZER(db_list_null);

/* Empty time interval. */
static struct tint_list tint_list_empty = STAILQ_HEAD_INITIALIZER(tint_list_empty);

static int	posix_re_pattern;	/* posix_re_pattern parameter. */
static int	only_abs_paths;		/* only_abs_paths parameter. */

struct debug_param {
	const char	*param_name;
	int		*param_value;
};

/* debug_* parameters in IPA_CONF_SECT_ROOT. */
static struct debug_param common_debug_param[] = {
#ifdef WITH_AUTORULES
	{ "debug_autorule",		&debug_autorule			},
#endif
	{ "debug_ctl",			&debug_ctl,			},
	{ "debug_time",			&debug_time			},
	{ "debug_worktime",		&debug_worktime			},
	{ "debug_ac_null",		&debug_ac_null			},
	{ "debug_db_null",		&debug_db_null			},
	{ NULL,				NULL				}
};

/* debug_* parameters in IPA_CONF_SECT_GLOBAL. */
static struct debug_param global_debug_param[] = {
#ifdef WITH_LIMITS
	{ "debug_limit",		&global_debug_limit		},
	{ "debug_limit_init",		&global_debug_limit_init	},
#endif
#ifdef WITH_THRESHOLDS
	{ "debug_threshold",		&global_debug_threshold		},
	{ "debug_threshold_init",	&global_debug_threshold_init	},
#endif
	{ "debug_exec",			&global_debug_exec		},
	{ NULL,				NULL				}
};

struct time_param {
	const char	*param_name;
	u_int		*param_value;
};

/* *_time parameters in IPA_CONF_SECT_ROOT. */
static struct time_param common_time_param[] = {
	{ "wakeup_time",		&wakeup_time			},
	{ "freeze_time",		&freeze_time			},
	{ "sleep_after_dump",		&sleep_after_dump		},
	{ "sensitive_time",		&sensitive_time			},
	{ NULL,				NULL				}
};

static int	use_log = 1;		/* 0, if -t or -x switch was used. */

static PARSING_MODE parsing_mode;	/* xxx_PARSING. */

static u_int	section;		/* Current section ID. */
static u_int	section_first;		/* autorule rulepat rule */
static u_int	section_top;		/* autorule rulepat rule limit sublimit threshold */

static int	global_section_set;	/* 1, if has global{}. */

static regex_t	reg_worktime;
static regex_t	reg_list;

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);

/* Macro for validating if empty TAILQ_HEAD is in consistent state. */
#define TAILQ_EMPTY_HEAD_VALID(x)	\
	((x)->tqh_first == NULL && (x)->tqh_last == &(x)->tqh_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_log,		/* open_log		*/
	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	*/
};

/*
 * 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)
{
	if (use_log) {
		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);
	} else {
		fflush(stdout);
		fprintf(stderr, "%s: ", prefix);
		vfprintf(stderr, format, ap);
		fprintf(stderr, "\n");
	}
}

/*
 * 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., don't use
 * mem_* functions.
 */
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);
}

/*
 * Try to find tevent with the given value of event_step,
 * if such tevent does not exist, then allocate new one.
 * Return NULL if new tevent can't be allocated.
 */
static struct tevent *
find_tevent(u_int event_step)
{
	struct tevent *tevent;

	SLIST_FOREACH(tevent, &tevents_list, link)
		if (tevent->event_step == event_step)
			return tevent;

	if ( (tevent = mzone_alloc(tevent_mzone)) != NULL) {
		tevent->event_step = event_step;
		SLIST_INSERT_HEAD(&tevents_list, tevent, link);
	} else
		conferrx("find_tevent: mzone_alloc failed");

	return tevent;
}

/*
 * Register a configuration event in ac_mod module.
 */
static int
ac_mod_conf_event(const struct ac_mod *ac_mod, u_int event,
    u_int no, const void *arg)
{
	if (ac_mod->ipa_ac_mod->conf_event(event, no, arg) < 0) {
		logconferrx("module %s: conf_event(IPA_CONF_EVENT_%s) failed",
		    ac_mod->mod_file, conf_event_msg[event]);
		return -1;
	}
	return 0;
}

/*
 * Register a configuration event in db_mod module.
 */
static int
db_mod_conf_event(const struct db_mod *db_mod, u_int event,
    u_int no, const void *arg)
{
	if (db_mod->ipa_db_mod->conf_event(event, no, arg) < 0) {
		logconferrx("module %s: conf_event(IPA_CONF_EVENT_%s) failed",
		    db_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 ac_mod *ac_mod;
	const struct db_mod *db_mod;

	SLIST_FOREACH(ac_mod, &ac_mod_list, link)
		if (ac_mod_conf_event(ac_mod, event, no, arg) < 0) {
			logconferrx("mod_conf_event: ac_mod_conf_event failed");
			return -1;
		}

	SLIST_FOREACH(db_mod, &db_mod_list, link)
		if (db_mod_conf_event(db_mod, event, no, arg) < 0) {
			logconferrx("mod_conf_event: db_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 ac_mod *ac_mod;
	const struct db_mod *db_mod;

	SLIST_FOREACH(ac_mod, &ac_mod_list, link) {
		if (ac_mod->ipa_ac_mod->conf_inherit != NULL)
			if (ac_mod->ipa_ac_mod->conf_inherit(rulepat->rulepatno,
			    rule->ruleno, rule->rule_name) < 0) {
				xlogmsgx(IPA_LOG_ERR, "module %s: conf_inherit(%s, %s) failed",
				    ac_mod->mod_file, parser_buf_to_string(rulepat->pat),
				    rule->rule_name);
				return -1;
			}
	}

	SLIST_FOREACH(db_mod, &db_mod_list, link) {
		if (db_mod->ipa_db_mod->conf_inherit != NULL)
			if (db_mod->ipa_db_mod->conf_inherit(rulepat->rulepatno,
			    rule->ruleno, rule->rule_name) < 0) {
				xlogmsgx(IPA_LOG_ERR, "module %s: conf_inherit(%s, %s) failed",
				    db_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);
}

#ifdef WITH_RULES
/*
 * 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 = mzone_alloc(rule_mzone)) == NULL) {
		logconferrx("mzone_alloc failed");
		return -1;
	}

	if ( (rule->rule_name = mem_strdup(rule_name, m_anon)) == NULL) {
		logconferrx("mem_strdup failed");
		return -1;
	}
	rule->rule_info = NULL;
	rule->ruleno = nstatrules++;

	rule->cnt_neg = UINT64_C(0);

	rule->update_tevent = rule->append_tevent = NULL;

	rule->worktime = NULL;
	rule->is_active = UNKNOWN_ACTIVE_FLAG;

	rule->ac_list = NULL;
	rule->db_list = NULL;

	rule->ac_gather_add_pat = rule->ac_gather_sub_pat = NULL;
	SLIST_INIT(&rule->ac_gather_rev);

	rule->debug_exec = -1;

	init_cmds_in_rule(rule);

#ifdef WITH_LIMITS
	rule->debug_limit = rule->debug_limit_init = -1;
	limits_list = &rule->limits;
	STAILQ_INIT(limits_list);
	limitno = 0;
#endif

#ifdef WITH_THRESHOLDS
	rule->debug_threshold = rule->debug_threshold_init = -1;
	thresholds_list = &rule->thresholds;
	STAILQ_INIT(thresholds_list);
	thresholdno = 0;
#endif

#ifdef CTL_CHECK_CREDS
	rule->ctl_rule_acl = NULL;
#endif

	rule->free_mask = RULE_FREE_LIMITS|RULE_FREE_THRESHOLDS;

	section_first = section_top = IPA_CONF_SECT_RULE;

	if (parser_local_sym_add("rule", rule->rule_name, 0) < 0)
		return -1;

	TAILQ_INSERT_TAIL(&rules_list, rule, list);

	add_rule_to_hash(rule);

	if (mod_conf_event(IPA_CONF_EVENT_RULE_BEGIN, rule->ruleno,
	    rule->rule_name) < 0)
		return -1;

	currule = rule;

	return 0;
}
#endif /* WITH_RULES */

/*
 * 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->update_tevent = rulepat->append_tevent = NULL;

	rulepat->worktime = NULL;

	rulepat->ac_list = NULL;
	rulepat->db_list = NULL;

	rulepat->debug_exec = -1;

	init_cmds_in_rulepat(rulepat);

#ifdef WITH_LIMITS
	rulepat->debug_limit = rulepat->debug_limit_init = -1;
	limits_list = &rulepat->limits;
	STAILQ_INIT(limits_list);
	limitno = 0;
#endif

#ifdef WITH_THRESHOLDS
	rulepat->debug_threshold = rulepat->debug_threshold_init = -1;
	thresholds_list = &rulepat->thresholds;
	STAILQ_INIT(thresholds_list);
	thresholdno = 0;
#endif

#ifdef CTL_CHECK_CREDS
	rulepat->ctl_rule_acl = NULL;
#endif

	section_first = section_top = IPA_CONF_SECT_RULEPAT;

	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;
}

/*
 * Parse an argument of some "debug_*" parameter.
 */
static int
check_debug_level(uint32_t level, u_int maxlevel)
{
	if (level > maxlevel) {
		logconferrx("too big debug level, max level is %u", maxlevel);
		return -1;
	}
	return (int)level;
}

/*
 * Parse the "debug_time" parameter.
 */
static int
parse_debug_time(void *arg)
{
	return debug_time = check_debug_level(*(uint32_t *)arg, 2);
}

/*
 * Parse the "debug_ac_null" parameter.
 */
static int
parse_debug_ac_null(void *arg)
{
	return debug_ac_null = check_debug_level(*(uint32_t *)arg, 1);
}

/*
 * Parse the "debug_db_null" parameter.
 */
static int
parse_debug_db_null(void *arg)
{
	return debug_db_null = check_debug_level(*(uint32_t *)arg, 1);
}

/*
 * Parse the "debug_worktime" parameter.
 */
static int
parse_debug_worktime(void *arg)
{
	return debug_worktime = check_debug_level(*(uint32_t *)arg, 1);
}

/*
 * Parse the "debug_exec" parameter.
 */
static int
parse_debug_exec(void *arg)
{
	int level;

	if ( (level = check_debug_level(*(uint32_t *)arg, 2)) < 0)
		return -1;

	switch (section) {
	case IPA_CONF_SECT_GLOBAL:
		global_debug_exec = level;
		break;
#ifdef WITH_RULES
	case IPA_CONF_SECT_RULE:
		currule->debug_exec = level;
		break;
#endif
#ifdef WITH_AUTORULES
	case IPA_CONF_SECT_AUTORULE:
		curautorule->debug_exec = level;
		break;
#endif
	default: /* IPA_CONF_SECT_RULEPAT */
		currulepat->debug_exec = level;
	}

	return 0;
}

/*
 * Parse the "update_time" parameter.
 */
static int
parse_update_time(void *arg)
{
	uint64_t timeval = *(uint64_t *)arg;
	struct tevent *tevent;

	if (timeval == UINT64_C(0) || timeval > SECONDS_IN_DAY) {
		logconferrx("argument should be greater than zero seconds and less than or equal to 1 day");
		return -1;
	}
	if ( (tevent = find_tevent((u_int)timeval)) == NULL) {
		logconferrx("find_tevent failed");
		return -1;
	}
	switch (section) {
#ifdef WITH_RULES
	case IPA_CONF_SECT_RULE:
		currule->update_tevent = tevent;
		break;
#endif
#ifdef WITH_AUTORULES
	case IPA_CONF_SECT_AUTORULE:
		curautorule->update_tevent = tevent;
		break;
#endif
	case IPA_CONF_SECT_RULEPAT:
		currulepat->update_tevent = tevent;
		break;
	default: /* IPA_CONF_SECT_GLOBAL */
		global_update_tevent = tevent;
	}
	return 0;
}

/*
 * Parse the "freeze_time" parameter.
 */
static int
parse_freeze_time(void *arg)
{
	uint64_t timeval = *(uint64_t *)arg;

	if (timeval == UINT64_C(0) || timeval >= 5 * SECONDS_IN_MINUTE) {
		logconferrx("argument should be greater than zero seconds and less than 5 minutes");
		return -1;
	}
	freeze_time = (u_int)timeval;
	return 0;
}

/*
 * Parse the "append_time" parameter.
 */
static int
parse_append_time(void *arg)
{
	uint64_t timeval = *(uint64_t *)arg;
	struct tevent *tevent;

	if (timeval == UINT64_C(0) || timeval > SECONDS_IN_DAY) {
		logconferrx("argument should be greater than zero seconds and less than or equal to 1 day");
		return -1;
	}
	if ( (tevent = find_tevent((u_int)timeval)) == NULL) {
		logconferrx("find_tevent failed");
		return -1;
	}
	switch (section) {
#ifdef WITH_RULES
	case IPA_CONF_SECT_RULE:
		currule->append_tevent = tevent;
		break;
#endif
#ifdef WITH_AUTORULES
	case IPA_CONF_SECT_AUTORULE:
		curautorule->append_tevent = tevent;
		break;
#endif
	case IPA_CONF_SECT_RULEPAT:
		currulepat->append_tevent = tevent;
		break;
	default: /* IPA_CONF_SECT_GLOBAL */
		global_append_tevent = tevent;
	}
	return 0;
}

/*
 * Parse the "wakeup_time" parameter.
 */
static int
parse_wakeup_time(void *arg)
{
	uint64_t timeval = *(uint64_t *)arg;

	if (timeval == UINT64_C(0) || timeval > SECONDS_IN_DAY) {
		logconferrx("argument should be greater than zero seconds and less than or equal to 1 day");
		return -1;
	}
	wakeup_time = (u_int)timeval;
	return 0;
}

#if defined(WITH_THRESHOLDS) || defined(WITH_SUBLIMITS)
/*
 * get_arg_value() + get_arg_per_cent().
 */
static int
get_value_or_per_cent(uint64_t *val_p, u_int *cnt_type_p)
{
	u_int	cnt_type;
	uint64_t val;

	if (regexec(&reg_bytes, parser_args, 0, (regmatch_t *)NULL, 0) == 0) {
		if (get_arg_bytes(&val) < 0)
			return -1;
		cnt_type = IPA_CONF_TYPE_BYTES;
	} else if (regexec(&reg_time, parser_args, 0, (regmatch_t *)NULL, 0) == 0) {
		if (get_arg_time(&val) < 0)
			return -1;
		cnt_type = IPA_CONF_TYPE_TIME;
	} else {
		char *ptr;

		ptr = parser_args + parser_args_len - 1;
		if (*ptr == '%') {
			*ptr = '\0';
			cnt_type = IPA_CONF_TYPE_PER_CENT;
		} else
			cnt_type = IPA_CONF_TYPE_UINT64;

		if (get_arg_uint64(&val) < 0)
			return -1;
		if (cnt_type == IPA_CONF_TYPE_PER_CENT && val > 100) {
			logconferrx("per cent value should <= 100%%");
			return -1;
		}
	}

	*val_p = val;
	*cnt_type_p = cnt_type;

	return 0;
}
#endif /* WITH_THRESHOLDS || WITH_SUBLIMITS */

#ifdef WITH_LIMITS
/*
 * Parse the "debug_limit" parameter.
 */
static int
parse_debug_limit(void *arg)
{
	int level;

	if ( (level = check_debug_level(*(uint32_t *)arg, 1)) < 0)
		return -1;

	switch (section) {
	case IPA_CONF_SECT_GLOBAL:
		global_debug_limit = level;
		break;
#ifdef WITH_RULES
	case IPA_CONF_SECT_RULE:
		currule->debug_limit = level;
		break;
#endif
#ifdef WITH_AUTORULES
	case IPA_CONF_SECT_AUTORULE:
		curautorule->debug_limit = level;
		break;
#endif
	default: /* IPA_CONF_SECT_RULEPAT */
		currulepat->debug_limit = level;
	}

	return 0;
}

/*
 * Parse the "debug_limit_init" parameter.
 */
static int
parse_debug_limit_init(void *arg)
{
	int level;

	if ( (level = check_debug_level(*(uint32_t *)arg, 1)) < 0)
		return -1;

	switch (section) {
	case IPA_CONF_SECT_GLOBAL:
		global_debug_limit_init = level;
		break;
#ifdef WITH_RULES
	case IPA_CONF_SECT_RULE:
		currule->debug_limit_init = level;
		break;
#endif
#ifdef WITH_AUTORULES
	case IPA_CONF_SECT_AUTORULE:
		curautorule->debug_limit_init = level;
		break;
#endif
	default: /* IPA_CONF_SECT_RULEPAT */
		currulepat->debug_limit_init = level;
	}

	return 0;
}

/*
 * Parse the "load_limit" parameter.
 */
static int
parse_load_limit(void *arg)
{
	if (section == IPA_CONF_SECT_LIMIT)
		curlimit->load_limit = *(int *)arg;
	else /* IPA_CONF_SECT_GLOBAL */
		global_load_limit = *(int *)arg;

	return 0;
}

/*
 * Parse the "limit" section.
 */
static int
parse_limit_sect(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 = mzone_alloc(limit_mzone)) == NULL) {
		logconferrx("mzone_alloc 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->limit_info = NULL;
	limit->limitno = limitno++;

	limit->lim = limit->cnt_neg = UINT64_C(0);

	limit->load_limit = -1;

	limit->restart.restart.upto = limit->expire.expire.upto = TEXP_UPTO_NOTSET;

	init_cmds_in_limit(limit);

	limit->worktime = NULL;
	limit->is_active = ACTIVE_FLAG;

	limit->db_list = NULL;

#ifdef WITH_SUBLIMITS
	STAILQ_INIT(&limit->sublimits);
#endif

	limit->wpid.type = WPID_TYPE_LIMIT;
	limit->wpid.pid = 0;
	limit->wpid.u.limit = limit;

#ifdef WITH_RULES
	limit->rule = currule;
#endif

	if (section_top == IPA_CONF_SECT_RULE)
		++nstatlimits;
	section_top = IPA_CONF_SECT_LIMIT;

	if (parser_local_sym_add("limit", limit->limit_name, 0) < 0)
		return -1;

	if (mod_conf_event(IPA_CONF_EVENT_LIMIT_BEGIN, limit->limitno,
	    limit->limit_name) < 0)
		return -1;

	curlimit = limit;

	return 0;
}

/*
 * Parse the "limit" parameter.
 */
static int
parse_limit_param(void *arg)
{
	uint64_t *ptr = (uint64_t *)arg;
	struct limit *limit;

	limit = curlimit;

	if (limit->lim != UINT64_C(0)) {
		logconferrx("cannot redefine this parameter");
		return -1;
	}

	if ( (limit->lim = *(ptr + 1)) == UINT64_C(0)) {
		logconferrx("argument should be greater than zero");
		return -1;
	}

	limit->cnt_type = (u_int)*ptr;

	return 0;
}

#ifdef WITH_SUBLIMITS
/*
 * Parse the "sublimit" section.
 */
static int
parse_sublimit(void *arg)
{
	char	*sublimit_name;
	u_int	cnt_type;
	uint64_t lim;
	struct sublimit *sublimit;

	if (curlimit->lim == UINT64_C(0)) {
		logconferrx("\"limit\" parameter in previous section should be specified before");
		return -1;
	}

	if ( (sublimit_name = mem_strdup(*(char **)arg, m_anon)) == NULL) {
		logconferrx("mem_strdup failed");
		return -1;
	}

	if (get_value_or_per_cent(&lim, &cnt_type) < 0)
		return -1;

	if (lim == UINT64_C(0)) {
		logconferrx("argument should be greater than zero");
		return -1;
	}

	if (cnt_type != IPA_CONF_TYPE_PER_CENT && cnt_type != curlimit->cnt_type) {
		logconferrx("different arguments types for sublimit and limit");
		return -1;
	}

	STAILQ_FOREACH(sublimit, &curlimit->sublimits, link)
		if (cnt_type == IPA_CONF_TYPE_PER_CENT) {
			if ((u_int)lim == sublimit->lim_per_cent) {
				logconferrx("this section is duplicated");
				return -1;
			}
		} else {
			if (lim == sublimit->lim) {
				logconferrx("this section is duplicated");
				return -1;
			}
		}

	if ( (sublimit = mzone_alloc(sublimit_mzone)) == NULL) {
		logconferrx("mzone_alloc failed");
		return -1;
	}

	STAILQ_INSERT_TAIL(&curlimit->sublimits, sublimit, link);

	sublimit->sublimit_name = sublimit_name;
	if (cnt_type == IPA_CONF_TYPE_PER_CENT)
		sublimit->lim_per_cent = (u_int)lim;
	else {
		sublimit->lim = lim;
		sublimit->lim_per_cent = 0;
	}
	sublimit->cnt_type = cnt_type;

	init_cmds_in_sublimit(sublimit);

	sublimit->wpid.type = WPID_TYPE_SUBLIMIT;
	sublimit->wpid.pid = 0;
	sublimit->wpid.u.sublimit = sublimit;

	sublimit->limit = curlimit;

	if (section_top == IPA_CONF_SECT_RULE)
		++nstatsublimits;
	section_top = SECT_SUBLIMIT;

	if (parser_local_sym_add("sublimit", sublimit->sublimit_name, 0) < 0)
		return -1;

	cursublimit = sublimit;

	return 0;
}
#endif /* WITH_SUBLIMITS */

/*
 * Parse "expired time" value.
 */
static int
parse_texp(struct texp *texp, const void *arg)
{
	const char *ptr = (const char *)arg;
	char	*endptr;
	int	level = 0, error = 0, overflow = 0;
	uint32_t value;

	texp->seconds = 0;
	texp->upto = TEXP_UPTO_SIMPLE;
	for (;;) {
		if (*ptr == ' ')
			++ptr;
		if (*ptr == '+') {
			texp->upto = *++ptr;
			texp->side = texp->seconds > 0;
			if (*++ptr == '\0')
				break;	/* EOL */
			continue;
		}
		if (strto_uint32(&value, ptr, &endptr) < 0)
			return -1;
		ptr = endptr;
		switch (*ptr) {
		case 'W':
			if (level > 1)
				error = 1;
			else {
				if (value > UINT32_MAX / SECONDS_IN_WEEK)
					overflow = 1;
				else {
					level = 2;
					value *= SECONDS_IN_WEEK;
				}
			}
			break;
		case 'D':
			if (level > 2)
				error = 1;
			else {
				if (value > UINT32_MAX / SECONDS_IN_DAY)
					overflow = 1;
				else {
					level = 3;
					value *= SECONDS_IN_DAY;
				}
			}
			break;
		case 'h':
			if (level > 3)
				error = 1;
			else {
				if (value > UINT32_MAX / SECONDS_IN_HOUR)
					overflow = 1;
				else {
					level = 4;
					value *= SECONDS_IN_HOUR;
				}
			}
			break;
		case 'm':
			if (level > 4)
				error = 1;
			else {
				if (value > UINT32_MAX / SECONDS_IN_MINUTE)
					overflow = 1;
				else {
					level = 5;
					value *= SECONDS_IN_MINUTE;
				}
			}
			break;
		default: /* 's' */
			if (level > 5)
				error = 1;
			else
				level = 6;
		}
		if (error) {
			logconferrx("wrong format of an argument");
			return -1;
		}
		if (overflow || texp->seconds > UINT_MAX - value) {
			logconferrx("too big value for u_int");
			return -1;
		}
		texp->seconds += (u_int)value;
		if (*++ptr == '\0')
			break;	/* EOL */
	}
	return 0;
}

/*
 * Parse the "restart" parameter.
 */
static int
parse_restart(void *arg)
{
	struct texp *texp = &curlimit->restart.restart;

	if (parse_texp(texp, *(char **)arg) < 0)
		return -1;
	if (texp->seconds == 0 && texp->upto == TEXP_UPTO_SIMPLE) {
		logconferrx("argument should be greater than zero");
		return -1;
	}
	return 0;
}

/*
 * Parse the "expire" parameter.
 */
static int
parse_expire(void *arg)
{
	return parse_texp(&curlimit->expire.expire, *(char **)arg);
}

/*
 * Inherit settings for a limit from the "global" section.
 */
static void
inherit_limit_from_global(struct limit *limit)
{
	if (limit->load_limit < 0)
		limit->load_limit = global_load_limit;
}
#endif /* WITH_LIMITS */

/*
 * Parse the "exec" parameter.
 */
static int
parse_exec(void *arg)
{
	char	*start, *ptr, *str = *(char **)arg;
	int	allow_subst;
	struct cmd *cmd;

	switch (parser_nargs) {
	case 1:
	case 2:
		break;
	default:
		logconferrx("one or two arguments are expected");
		return -1;
	}

	/* Allocate new structure for command. */
	if ( (cmd = mzone_alloc(cmd_mzone)) == NULL) {
		logconferrx("mzone_alloc failed");
		return -1;
	}
	STAILQ_INSERT_TAIL(&curcmdl->list, cmd, link);

	/* user "..." or "..." */
	if (parser_nargs == 2) {
		if (*str == '\"') {
			logconferrx("wrong format of an argument");
			return -1;
		}
		ptr = str;
		str = strchr(str, ' ');
		*str++ = '\0';
		if ( (cmd->user = mem_strdup(ptr, m_cmd)) == NULL) {
			logconferrx("mem_strdup failed");
			return -1;
		}
		cmd->free_mask = CMD_FREE_STR|CMD_FREE_USER;
	} else {
		cmd->user = NULL;
		cmd->free_mask = CMD_FREE_STR;
	}

	/* Check for "..." */
	if (!parser_buf_is_string(str)) {
		logconferrx("wrong format of an argument");
		return -1;
	}

	/* Copy "..." */
	if ( (cmd->str = parser_string_strdup(str, m_cmd)) == NULL)
		return -1;

	/* Validate "..." */
	str = cmd->str;

	if (*str == '\0') {
		logconferrx("command should be a non-empty string");
		return -1;
	}

	if (*str != '/' && only_abs_paths != 0) {
		logconferrx("command should be given with absolute path");
		return -1;
	}

	switch (section_first) {
#ifdef WITH_AUTORULES
	case IPA_CONF_SECT_AUTORULE:
#endif
	case IPA_CONF_SECT_RULEPAT:
		allow_subst = 1;
		break;
	default:
		allow_subst = 0;
	}

	cmd->subst_per_cent = cmd->subst_rule = 0;

	/* Check if a string has correct substitutions and count them. */
	for (ptr = str, start = NULL, cmd->str_size = 1; *ptr != '\0'; cmd->str_size++, ++ptr)
		if (allow_subst && *ptr == '%') {
			if (start == NULL)
				start = ptr + 1;
			else {
				if (ptr != start) {
					/* %xxx% */
					*ptr = '\0';
					if (strcmp(start, "rule") != 0) {
						*ptr = '%';
						logconferrx("unknown substitute \"%s\" in command", start);
						return -1;
					}
					*ptr = '%';
					cmd->subst_rule++;
				} else {
					/* %% */
					cmd->subst_per_cent++;
				}
				start = NULL;
			}
		}

	if (start != NULL) {
		logconferrx("unclosed substitute in command");
		return -1;
	}

	return 0;
}

/*
 * Parse the "sync_exec" parameter.
 */
static int
parse_sync_exec(void *arg)
{
	curcmdl->sync_exec = *(int *)arg;
	return 0;
}

#if defined(WITH_RULES) || defined(WITH_ANY_LIMITS)
/*
 * Parse the "info" parameter.
 */
static int
parse_info(void *arg)
{
	char *ptr, **pptr;

	for (ptr = *(char **)arg; *ptr != '\0'; ++ptr)
		switch (*ptr) {
		case '\t':
		case '\n':
			logconferrx("'\\t' and '\\n' characters are not allowed here");
			return -1;
		}

	switch (section) {
#ifdef WITH_RULES
	case IPA_CONF_SECT_RULE:
		pptr = &currule->rule_info;
		break;
#endif
#ifdef WITH_LIMITS
	case IPA_CONF_SECT_LIMIT:
		pptr = &curlimit->limit_info;
		break;
#endif
#ifdef WITH_THRESHOLDS
	default: /* IPA_CONF_SECT_THRESHOLD */
		pptr = &curthreshold->threshold_info;
		break;
#endif
	}

	if (*pptr != NULL) {
		logconferrx("cannot redefine this parameter");
		return -1;
	}

	*pptr = *(char **)arg;

	return 0;
}
#endif /* WITH_RULES || WITH_ANY_LIMITS */

static const struct worktime *
parse_generic_worktime(void *arg)
{
	char	*ptr = *(char **)arg;
	u_int	wday, h1, m1, h2, m2, h2_prev, m2_prev;
	struct worktime *wt;
	struct tint *tint1, *tint2;
	struct tint_set *set1, *set2;
	struct tint_list *list;

	if ( (wt = mzone_alloc(worktime_mzone)) == NULL) {
		logconferrx("mzone_alloc failed");
		return NULL;
	}

	for (wday = 0; wday < DAYS_IN_WEEK; ++wday)
		wt->tint_list[wday] = &tint_list_empty;

	for (; *ptr != '\0';) {
		switch (*ptr) {
		case 'S':
			wday = 0;
			break;
		case 'M':
			wday = 1;
			break;
		case 'T':
			wday = 2;
			break;
		case 'W':
			wday = 3;
			break;
		case 'H':
			wday = 4;
			break;
		case 'F':
			wday = 5;
			break;
		default: /* 'A' */
			wday = 6;
		}
		if (wt->tint_list[wday] != &tint_list_empty) {
			logconferrx("each day (currently %s) should be specified only one time",
			    wdays[wday]);
			return NULL;
		}

		if ( (set1 = mem_malloc(sizeof *set1, m_anon)) == NULL) {
			logconferrx("mem_malloc failed");
			return NULL;
		}
		list = &set1->list;
		STAILQ_INIT(list);

		h2_prev = m2_prev = 0;
		ptr += 2; /* Skip day name and one white space. */
		for (;;) {
			if (*ptr == '*') {
				h1 = m1 = m2 = 0;
				h2 = HOURS_IN_DAY;
			} else {
				if (isdigit(*ptr) == 0)
					/* Next day in worktime. */
					break;
				errno = 0;
				if (sscanf(ptr, "%u:%u-%u:%u", &h1, &m1, &h2, &m2) != 4) {
					logconferr("sscanf(%s) failed", ptr);
					return NULL;
				}
				if ((h1 > 23 || h2 > 23 || m1 > 59 || m2 > 59) && !(h2 == HOURS_IN_DAY && m2 == 0)) {
					logconferrx("wrong value of hours or minutes in time interval for %s",
					    wdays[wday]);
					return NULL;
				}
				if (h1 == h2 && m1 == m2) {
					logconferrx("zero seconds time time intervals are not allowed");
					return NULL;
				}
				if ((h1 * MINUTES_IN_HOUR + m1) > (h2 * MINUTES_IN_HOUR + m2)) {
					logconferrx("wrong time interval for %s: %02u:%02u-%02u:%02u",
					    wdays[wday], h1, m1, h2, m2);
					return NULL;
				}
				if (h2_prev * MINUTES_IN_HOUR + m2_prev > h1 * MINUTES_IN_HOUR + m1) {
					logconferrx("nonsuccessive time interval for %s", wdays[wday]);
					return NULL;
				}
			}

			if ( (tint1 = mzone_alloc(tint_mzone)) == NULL) {
				logconferrx("mzone_alloc failed");
				return NULL;
			}
			tint1->sec1 = h1 * SECONDS_IN_HOUR + m1 * SECONDS_IN_MINUTE;
			tint1->sec2 = h2 * SECONDS_IN_HOUR + m2 * SECONDS_IN_MINUTE;
			STAILQ_INSERT_TAIL(&set1->list, tint1, link);

			for (; *ptr != ' '; ++ptr)
				if (*ptr == '\0')
					goto done;
			++ptr;
			h2_prev = h2;
			m2_prev = m2;
		}
done:
		/*
		 * If there is already the same tint_list,
		 * then free memory used by just allocated one.
		 */
		STAILQ_FOREACH(set2, &tint_sets, link) {
			tint1 = STAILQ_FIRST(&set1->list);
			tint2 = STAILQ_FIRST(&set2->list);
			while (tint1 != NULL && tint2 != NULL) {
				if (tint1->sec1 != tint2->sec1 ||
				    tint1->sec2 != tint2->sec2)
					break;
				tint1 = STAILQ_NEXT(tint1, link);
				tint2 = STAILQ_NEXT(tint2, link);
			}
			if (tint1 == NULL && tint2 == NULL) {
				/* Duplicate tint_list was found. */
				free_tint_set(set1);
				set1 = set2;
				break;
			}
		}
		if (set2 == NULL)
			/* New tint_list --> add it to tint_sets. */
			STAILQ_INSERT_TAIL(&tint_sets, set1, link);

		wt->tint_list[wday] = &set1->list;
	}

	return find_worktime(wt);
}

/*
 * Parse the "worktime" parameter.
 */
static int
parse_worktime(void *arg)
{
	const struct worktime **wtpp;

	switch (section) {
#ifdef WITH_RULES
	case IPA_CONF_SECT_RULE:
		wtpp = &currule->worktime;
		break;
#endif
#ifdef WITH_LIMITS
	case IPA_CONF_SECT_LIMIT:
		wtpp = &curlimit->worktime;
		break;
#endif
#ifdef WITH_THRESHOLDS
	case IPA_CONF_SECT_THRESHOLD:
		wtpp = &curthreshold->worktime;
		break;
#endif
#ifdef WITH_AUTORULES
	case IPA_CONF_SECT_AUTORULE:
		wtpp = &curautorule->worktime;
		break;
#endif
	case IPA_CONF_SECT_RULEPAT:
		wtpp = &currulepat->worktime;
		break;
	default: /* IPA_CONF_SECT_GLOBAL */
		wtpp = &global_worktime;
	}

	if (*wtpp != NULL) {
		logconferrx("cannot redefine this parameter");
		return -1;
	}

	return ( (*wtpp = parse_generic_worktime(arg)) == NULL) ? -1 : 0;
}

#ifdef WITH_AUTORULES
/*
 * Parse the "worktime_rule" parameter.
 */
static int
parse_worktime_rule(void *arg)
{
	if (curautorule->worktime_rule != NULL) {
		logconferrx("cannot redefine this parameter");
		return -1;
	}

	return ( (curautorule->worktime_rule = parse_generic_worktime(arg)) == NULL) ?
	    -1 : 0;
}
#endif

/*
 * Check security of configuration file: absolute path, regular file,
 * is owned by the user, who run ipa, writable only for the user.
 */
static int
check_conf_file(const char *fname, int ignore_non_regular)
{
	struct stat statbuf;

	if (lstat(fname, &statbuf) < 0) {
		conferrx("lstat(%s): %s", fname, strerror(errno));
		return -1;
	}
	if (!S_ISREG(statbuf.st_mode)) {
		if (ignore_non_regular)
			return 0;
		conferrx("configuration file \"%s\" should be a regular file",
		    fname);
		return -1;
	}
	if (statbuf.st_uid != myuid) {
		conferrx("configuration file \"%s\" should be owned by the user, who run this program",
		    fname);
		return -1;
	}
	if (statbuf.st_mode & (S_IWGRP|S_IWOTH)) {
		conferrx("configuration file \"%s\" should not have write permissions for group or other users",
		    fname);
		return -1;
	}
	return 1;
}

/*
 * 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) < 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;
	pbuf->foff = 0;
	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 (statbuf.st_uid != myuid) {
		logconferrx("directory should be owned by the user, who run this program");
		return -1;
	}
	if (statbuf.st_mode & (S_IWGRP|S_IWOTH)) {
		logconferrx("directory should not have write permissions for group and other users");
		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;
		}
		switch (check_conf_file(fname, 1)) {
		case -1:
			return -1;
		case 0:
			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;
}

/*
 * Parse the "keep_rules_order" parameter.
 */
static int
parse_keep_rules_order(void *arg)
{
	keep_rules_order = *(int *)arg;
	return 0;
}

/*
 * Parse the "only_abs_paths" parameter.
 */
static int
parse_only_abs_paths(void *arg)
{
	only_abs_paths = *(int *)arg;
	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 "ac_mod" parameter.
 */
static int
parse_ac_mod(void *arg)
{
	char	*mod_name, *sym;
	struct ac_mod *ac_mod, *ac_mod2;
	struct ipa_ac_mod *ipa_ac_mod;
	const struct db_mod *db_mod;

	if ( (ac_mod = mem_malloc(sizeof *ac_mod, m_anon)) == NULL) {
		logconferrx("mem_malloc failed");
		return -1;
	}
	ac_mod->mod_file = *(char **)arg;
	ac_mod->mod_ref_count = 0;
	if ( (ac_mod->mod_handle = dl_open(ac_mod->mod_file)) == NULL) {
		logconferrx("dl_open(%s): %s", ac_mod->mod_file, dl_error());
		return -1;
	}
	if ( (mod_name = get_mod_name(ac_mod->mod_file)) == NULL)
		return -1;
	if (mem_asprintf(m_anon, &sym, "%s_ac_mod", mod_name) < 0) {
		logconferrx("mem_asprintf failed");
		return -1;
	}
	if ( (ac_mod->ipa_ac_mod = (struct ipa_ac_mod *)dl_lookup_sym(ac_mod->mod_handle, sym)) == NULL) {
		logconferrx("given module is not an IPA accounting module or unknown symbol naming scheme is used");
		return -1;
	}
	mem_free(sym, m_anon);
	mem_free(mod_name, m_anon);
	ipa_ac_mod = ac_mod->ipa_ac_mod;

	/* Check ipa_ac_mod API version. */
	if (ipa_ac_mod->api_ver != IPA_AC_MOD_API_VERSION) {
		logconferrx("module %s uses ipa_ac_mod API version %u, my ipa_ac_mod API version is %u",
		    ac_mod->mod_file, ipa_ac_mod->api_ver, IPA_AC_MOD_API_VERSION);
		return -1;
	}

	/* Check if module is thread-safe or vice versa. */
#ifdef WITH_PTHREAD
	if (!(ipa_ac_mod->mod_flags & IPA_MOD_FLAG_PTHREAD_SAFE)) {
		logconferrx("module %s must be thread-safe", ac_mod->mod_file);
		return -1;
	}
#else
	if (ipa_ac_mod->mod_flags & IPA_MOD_FLAG_PTHREAD_SAFE) {
		logconferrx("module %s must not be thread-safe", ac_mod->mod_file);
		return -1;
	}
#endif /* WITH_PTHREAD */

	if (strcmp(ipa_ac_mod->ac_name, "null") == 0) {
		logconferrx("module's accounting system name is \"null\", this is a name of the builtin accounting system");
		return -1;
	}

	if ( (ac_mod2 = ac_mod_by_name(ipa_ac_mod->ac_name)) != NULL) {
		conferrx("duplicated accounting system name \"%s\" in %s and %s modules",
		    ipa_ac_mod->ac_name, ac_mod->mod_file, ac_mod2->mod_file);
		return -1;
	}
	if ( (ac_mod2 = ac_mod_by_prefix(ipa_ac_mod->conf_prefix)) != NULL) {
		conferrx("duplicated configuration prefix \"%s\" in %s and %s modules",
		    ipa_ac_mod->conf_prefix, ac_mod->mod_file, ac_mod2->mod_file);
		return -1;
	}
	if ( (db_mod = db_mod_by_prefix(ipa_ac_mod->conf_prefix)) != NULL) {
		conferrx("duplicated configuration prefix \"%s\" in %s and %s modules",
		    ipa_ac_mod->conf_prefix, ac_mod->mod_file, db_mod->mod_file);
		return -1;
	}

	ipa_ac_mod->suppfunc = &suppfunc;
	ipa_ac_mod->memfunc = &memfunc;
	ipa_ac_mod->ac_create_rule = create_rule;
	ipa_ac_mod->ac_delete_rule = delete_rule;

	if (init_conf_tbls(ac_mod->mod_file, 1,
	    ipa_ac_mod->conf_sect_tbl, &ac_mod->conf_sect_hash,
	    ipa_ac_mod->conf_param_tbl, &ac_mod->conf_param_hash) < 0)
		return -1;

	if (ipa_ac_mod->conf_init() < 0) {
		conferrx("module %s: conf_init failed", ac_mod->mod_file);
		return -1;
	}

	SLIST_INSERT_HEAD(&ac_mod_list, ac_mod, link);

	++nac_mods;

	return 0;
}

/*
 * Parse the "db_mod" parameter.
 */
static int
parse_db_mod(void *arg)
{
	char	*mod_name, *sym;
	struct db_mod *db_mod, *db_mod2;
	struct ipa_db_mod *ipa_db_mod;
	const struct ac_mod *ac_mod;

	if ( (db_mod = mem_malloc(sizeof *db_mod, m_anon)) == NULL) {
		logconferrx("mem_malloc failed");
		return -1;
	}
	db_mod->mod_file = *(char **)arg;
	if ( (db_mod->mod_handle = dl_open(db_mod->mod_file)) == NULL) {
		logconferrx("dl_open(%s): %s", db_mod->mod_file, dl_error());
		return -1;
	}
	if ( (mod_name = get_mod_name(db_mod->mod_file)) == NULL)
		return -1;
	if (mem_asprintf(m_anon, &sym, "%s_db_mod", mod_name) < 0) {
		logconferrx("mem_asprintf failed");
		return -1;
	}
	if ( (db_mod->ipa_db_mod = (struct ipa_db_mod *)dl_lookup_sym(db_mod->mod_handle, sym)) == NULL) {
		logconferrx("given module is not an IPA database module or unknown symbol naming scheme is used");
		return -1;
	}
	mem_free(sym, m_anon);
	mem_free(mod_name, m_anon);
	ipa_db_mod = db_mod->ipa_db_mod;

	/* Check ipa_db_mod API version. */
	if (ipa_db_mod->api_ver != IPA_DB_MOD_API_VERSION) {
		logconferrx("module %s uses ipa_db_mod API version %u, my ipa_db_mod API version is %u",
		    db_mod->mod_file, ipa_db_mod->api_ver, IPA_DB_MOD_API_VERSION);
		return -1;
	}

	/* Check if module is thread-safe or vice versa. */
#ifdef WITH_PTHREAD
	if (!(ipa_db_mod->mod_flags & IPA_MOD_FLAG_PTHREAD_SAFE)) {
		logconferrx("module %s must be thread-safe", db_mod->mod_file);
		return -1;
	}
#else
	if (ipa_db_mod->mod_flags & IPA_MOD_FLAG_PTHREAD_SAFE) {
		logconferrx("module %s must not be thread-safe", db_mod->mod_file);
		return -1;
	}
#endif /* WITH_PTHREAD */

	if (strcmp(ipa_db_mod->db_name, "null") == 0) {
		logconferrx("module's database name is \"null\", this is a name of the builtin database");
		return -1;
	}

	if ( (db_mod2 = db_mod_by_name(ipa_db_mod->db_name)) != NULL) {
		conferrx("duplicated database name \"%s\" in %s and %s modules",
		    ipa_db_mod->db_name, db_mod->mod_file, db_mod2->mod_file);
		return -1;
	}
	if ( (db_mod2 = db_mod_by_prefix(ipa_db_mod->conf_prefix)) != NULL) {
		conferrx("duplicated configuration prefix \"%s\" in %s and %s modules",
		    ipa_db_mod->conf_prefix, db_mod->mod_file, db_mod2->mod_file);
		return -1;
	}
	if ( (ac_mod = ac_mod_by_prefix(ipa_db_mod->conf_prefix)) != NULL) {
		conferrx("duplicated configuration prefix \"%s\" in %s and %s modules",
		    ipa_db_mod->conf_prefix, db_mod->mod_file, ac_mod->mod_file);
		return -1;
	}

	ipa_db_mod->suppfunc = &suppfunc;
	ipa_db_mod->memfunc = &memfunc;

	if (init_conf_tbls(db_mod->mod_file, 1,
	    ipa_db_mod->conf_sect_tbl, &db_mod->conf_sect_hash,
	    ipa_db_mod->conf_param_tbl, &db_mod->conf_param_hash) < 0)
		return -1;

	if (ipa_db_mod->conf_init() < 0) {
		conferrx("module %s: conf_init failed", db_mod->mod_file);
		return -1;
	}

	SLIST_INSERT_HEAD(&db_mod_list, db_mod, link);

	++ndb_mods;

	return 0;
}

/*
 * Parse the "ac_list" parameter.
 */
static int
parse_ac_list(void *arg)
{
	char	*ptr;
	const char *ac_name;
	struct ac_elem *ac1, *ac2;
	struct ac_set *set1, *set2;
	struct ac_list *list;
	struct ac_mod *ac_mod;
	const struct ac_list **listp;

	switch (section) {
#ifdef WITH_RULES
	case IPA_CONF_SECT_RULE:
		listp = &currule->ac_list;
		break;
#endif
#ifdef WITH_AUTORULES
	case IPA_CONF_SECT_AUTORULE:
		listp = &curautorule->ac_list;
		break;
#endif
	case IPA_CONF_SECT_RULEPAT:
		listp = &currulepat->ac_list;
		break;
	default: /* IPA_CONF_SECT_GLOBAL */
		listp = &global_ac_list;
	}

	if (*listp != NULL) {
		logconferrx("cannot redefine this parameter");
		return -1;
	}

	set1 = NULL;

	list = NULL;

	for (ptr = *(char **)arg; ptr != NULL;) {
		/* Get the name of the next accounting system. */
		ac_name = ptr;
		if ( (ptr = strchr(ptr, ' ')) != NULL)
			*ptr++ = '\0';

		/* Handle "null" accounting system. */
		if (strcmp(ac_name, "null") == 0) {
			if (list != NULL || ptr != NULL) {
				logconferrx("builtin accounting system \"null\" cannot be used together with another accounting systems");
				return -1;
			}
			*listp = &ac_list_null;
			return 0;
		}

		if ( (ac_mod = ac_mod_by_name(ac_name)) == NULL) {
			logconferrx("cannot find module with \"%s\" accounting system name", ac_name);
			return -1;
		}

		if (set1 != NULL) {
			/* We already have set for current ac_list parameter. */
			STAILQ_FOREACH(ac1, list, link)
				if (strcmp(ac_name, ac1->ipa_ac_mod->ac_name) == 0) {
					logconferrx("duplicated accounting system \"%s\"",
					    ac_name);
					return -1;
				}
		} else {
			/* Create new set for ac_list parameter. */
			if ( (set1 = mem_malloc(sizeof *set1, m_anon)) == NULL) {
				logconferrx("mem_malloc failed");
				return -1;
			}
			list = &set1->list;
			STAILQ_INIT(list);
		}

		/* Add new ac element to ac_list. */
		if ( (ac1 = mem_malloc(sizeof *ac1, m_anon)) == NULL) {
			logconferrx("mem_malloc failed");
			return -1;
		}
		ac1->ipa_ac_mod = ac_mod->ipa_ac_mod;
		ac1->mod_file = ac_mod->mod_file;

		/* Increment reference counter if needed. */
		ac1->mod_ref_count = &ac_mod->mod_ref_count;
		switch (section) {
#ifdef WITH_RULES
		case IPA_CONF_SECT_RULE:
			/* FALLTHROUGH */
#endif
#ifdef WITH_AUTORULES
		case IPA_CONF_SECT_AUTORULE:
#endif
			ac_mod->mod_ref_count++;
			break;
		}

		STAILQ_INSERT_TAIL(list, ac1, link);
	}

	/*
	 * If we already have the same ac_list (with elements exactly
	 * in the same order), then free memory used by just allocated
	 * one (mod_ref_count was updated above).
	 */
	SLIST_FOREACH(set2, &ac_sets, link) {
		ac1 = STAILQ_FIRST(&set1->list);
		ac2 = STAILQ_FIRST(&set2->list);
		while (ac1 != NULL && ac2 != NULL) {
			if (ac1->ipa_ac_mod != ac2->ipa_ac_mod)
				break;
			ac1 = STAILQ_NEXT(ac1, link);
			ac2 = STAILQ_NEXT(ac2, link);
		}
		if (ac1 == NULL && ac2 == NULL) {
			/* Duplicated ac_list was found. */
			*listp = &set2->list;
			free_ac_set(set1);
			return 0;
		}
	}

	/* New ac_list --> add it to ac_sets. */
	*listp = list;
	SLIST_INSERT_HEAD(&ac_sets, set1, link);

	return 0;
}

/*
 * Parse the "db_list" parameter.
 */
static int
parse_db_list(void *arg)
{
	char	*ptr;
	const char *db_name;
	struct db_elem *db1, *db2;
	struct db_set *set1, *set2;
	struct db_list *list;
	const struct db_mod *db_mod;
	const struct db_list **listp;

	switch (section) {
#ifdef WITH_RULES
	case IPA_CONF_SECT_RULE:
		listp = &currule->db_list;
		break;
#endif
#ifdef WITH_AUTORULES
	case IPA_CONF_SECT_AUTORULE:
		listp = &curautorule->db_list;
		break;
#endif
#ifdef WITH_LIMITS
	case IPA_CONF_SECT_LIMIT:
		listp = &curlimit->db_list;
		break;
#endif
#ifdef WITH_THRESHOLDS
	case IPA_CONF_SECT_THRESHOLD:
		listp = &curthreshold->db_list;
		break;
#endif
	case IPA_CONF_SECT_RULEPAT:
		listp = &currulepat->db_list;
		break;
	default: /* IPA_CONF_SECT_GLOBAL */
		listp = &global_db_list;
	}

	if (*listp != NULL) {
		logconferrx("cannot redefine this parameter");
		return -1;
	}

	set1 = NULL;

	list = NULL;

	for (ptr = *(char **)arg; ptr != NULL;) {
		/* Get the name of the next database. */
		db_name = ptr;
		if ( (ptr = strchr(ptr, ',')) != NULL)
			*ptr++ = '\0';

		/* Handle "null" database. */
		if (strcmp(db_name, "null") == 0) {
			if (list != NULL && ptr != NULL) {
				logconferrx("builtin \"null\" database cannot be used together with another databases");;
				return -1;
			}
			*listp = &db_list_null;
			return 0;
		}

		if ( (db_mod = db_mod_by_name(db_name)) == NULL) {
			logconferrx("cannot find module with \"%s\" database name", db_name);
			return -1;
		}

		if (set1 != NULL) {
			/* We already have set for current db_list parameter. */
			STAILQ_FOREACH(db1, list, link)
				if (strcmp(db_name, db1->ipa_db_mod->db_name) == 0) {
					logconferrx("duplicated database \"%s\"",
					    db_name);
					return -1;
				}
		} else {
			/* Create new set for db_list parameter. */
			if ( (set1 = mem_malloc(sizeof *set1, m_anon)) == NULL) {
				logconferrx("mem_malloc failed");
				return -1;
			}
			list = &set1->list;
			STAILQ_INIT(list);
		}

		/* Add new db element to db_list. */
		if ( (db1 = mem_malloc(sizeof *db1, m_anon)) == NULL) {
			logconferrx("mem_malloc failed");
			return -1;
		}
		db1->ipa_db_mod = db_mod->ipa_db_mod;
		db1->mod_file = db_mod->mod_file;

		STAILQ_INSERT_TAIL(list, db1, link);
	}

	/*
	 * If we already have the same db_list (with elements exactly
	 * in the same order), then free memory used by just allocated one.
	 */
	SLIST_FOREACH(set2, &db_sets, link) {
		db1 = STAILQ_FIRST(&set1->list);
		db2 = STAILQ_FIRST(&set2->list);
		while (db1 != NULL && db2 != NULL) {
			if (db1->ipa_db_mod != db2->ipa_db_mod)
				break;
			db1 = STAILQ_NEXT(db1, link);
			db2 = STAILQ_NEXT(db2, link);
		}
		if (db1 == NULL && db2 == NULL) {
			/* Duplicated db_list was found. */
			*listp = &set2->list;
			free_db_set(set1);
			return 0;
		}
	}

	/* New db_list --> add it to db_sets. */
	*listp = list;
	SLIST_INSERT_HEAD(&db_sets, set1, link);

	return 0;
}

#ifdef WITH_RULES
/*
 * Parse the "ac_gather_add" parameter.
 */
static int
parse_ac_gather_add(void *arg)
{
	char *pat;
	struct rule *rule;

	rule = currule;

	if (rule->ac_gather_add_pat != NULL) {
		logconferrx("cannot redefine this parameter");
		return -1;
	}

	rule->ac_gather_add_pat = pat = *(char **)arg;

	if ( (re_errcode = regcomp(&rule->ac_gather_add_reg, pat, REG_EXTENDED|REG_NOSUB)) != 0) {
		re_form_errbuf();
		logconferrx("regcomp(\"%s\"): %s", pat, re_errbuf);
		return -1;
	}

	if (rule->ac_gather_sub_pat == NULL)
		SLIST_INSERT_HEAD(&ac_gather_list, rule, ac_gather_link);

	return 0;
}

/*
 * Parse the "ac_gather_sub" parameter.
 */
static int
parse_ac_gather_sub(void *arg)
{
	char *pat;
	struct rule *rule;

	rule = currule;

	if (rule->ac_gather_sub_pat != NULL) {
		logconferrx("cannot redefine this parameter");
		return -1;
	}

	rule->ac_gather_sub_pat = pat = *(char **)arg;

	if ( (re_errcode = regcomp(&rule->ac_gather_sub_reg, pat, REG_EXTENDED|REG_NOSUB)) != 0) {
		re_form_errbuf();
		logconferrx("regcomp(\"%s\"): %s", pat, re_errbuf);
		return -1;
	}

	if (rule->ac_gather_add_pat == NULL)
		SLIST_INSERT_HEAD(&ac_gather_list, rule, ac_gather_link);

	return 0;
}
#endif /* WITH_RULES */

/*
 * Parse "posix_re_pattern" parameter.
 */
static int
parse_posix_re_pattern(void *arg)
{
	posix_re_pattern = *(int *)arg;
	return 0;
}

/*
 * Parse "shell_path" parameter.
 */
static int
parse_shell_path(void *arg)
{
	shell_path = *(char **)arg;
	if (*shell_path != '/') {
		logconferrx("shell path should be absolute");
		return -1;
	}
	return 0;
}

/*
 * Parse "shell_arg1" parameter.
 */
static int
parse_shell_arg1(void *arg)
{
	shell_arg1 = *(char **)arg;
	return 0;
}

/*
 * Parse "ctl_enable" parameter.
 */
static int
parse_ctl_enable(void *arg)
{
	ctl_enable = *(int *)arg;
	return 0;
}

/*
 * Parse "ctl_socket_path" parameter.
 */
static int
parse_ctl_socket_path(void *arg)
{
	const struct sockaddr_un addr;

	ctl_socket_path = *(char **)arg;
	if (*ctl_socket_path != '/') {
		logconferrx("ctl socket path should be absolute");
		return -1;
	}
	if (strlen(ctl_socket_path) + 1 > sizeof addr.sun_path) {
		logconferrx("too long path for ctl socket path");
		return -1;
	}
	return 0;
}

/*
 * Parse "ctl_socket_perm" parameter.
 */
static int
parse_ctl_socket_perm(void *arg)
{
	const char *ptr = *(char **)arg;
	mode_t val;

	val = 0;
	for (; *ptr != '\0'; ++ptr)
		switch (*ptr) {
		case 'u':
			val |= S_IWUSR;
			break;
		case 'g':
			val |= S_IWGRP;
			break;
		case 'o':
#ifdef CTL_CHECK_CREDS
			val |= S_IWOTH;
			break;
#else
			logconferrx("cannot allow to write to socket for other users");
			return -1;
#endif
		default:
			logconferrx("wrong value");
			return -1;
		}
	ctl_socket_perm = val | S_IRUSR;
	return 0;
}

/*
 * Parse "ctl_timeout" parameter.
 */
static int
parse_ctl_timeout(void *arg)
{
	uint64_t timeval = *(uint64_t *)arg;

	if (timeval == UINT64_C(0) || timeval >= UINT64_C(30)) {
		logconferrx("argument should be greater than zero seconds and less than 30 seconds");
		return -1;
	}
	ctl_timeout = (u_int)timeval;
	return 0;
}

/*
 * Parse "debug_ctl" parameter.
 */
static int
parse_debug_ctl(void *arg)
{
	return debug_ctl = check_debug_level(*(uint32_t *)arg, 1);
}

#ifdef CTL_CHECK_CREDS
/*
 * Find ctl class with the given name.
 */
static const struct ctl_acl_class *
ctl_class_by_name(const char *class_name)
{
	const struct ctl_acl_class *class;

	STAILQ_FOREACH(class, &ctl_acl_classes, link)
		if (strcmp(class->class_name, class_name) == 0)
			return class;

	return NULL;
}

/*
 * Parse "ctl_acl_class" parameter.
 */
static int
parse_ctl_acl_class(void *arg)
{
	char	*ptr, *str = *(char **)arg;
	struct ctl_acl_elem *elem;
	struct ctl_acl_class *class;

	/* <class> [<elem1> <elem2> ...] */
	if ( (ptr = strchr(str, ' ')) != NULL)
		*ptr = '\0';

	/* Check if we already have class with the same name. */
	if (ctl_class_by_name(str) != NULL) {
		logconferrx("duplicated ctl class \"%s\"", str);
		return -1;
	}

	/* New class. */
	if ( (class = mzone_alloc(ctl_acl_class_mzone)) == NULL) {
		logconferrx("mzone_alloc failed");
		return -1;
	}

	if ( (class->class_name = mem_strdup(str, m_ctl)) == NULL) {
		logconferrx("mem_strdup failed");
		return -1;
	}

	/* Init ACL for new class. */
	STAILQ_INIT(&class->list);

	/* Link new class to all classes list. */
	STAILQ_INSERT_TAIL(&ctl_acl_classes, class, link);

	if (ptr == NULL)
		/* Empty class. */
		return 0;

	str = ptr + 1;

	do {
		if ( (elem = mzone_alloc(ctl_acl_elem_mzone)) == NULL) {
			logconferrx("mzone_alloc failed");
			return -1;
		}
		STAILQ_INSERT_TAIL(&class->list, elem, link);

		if (*str == '!') {
			elem->allowed = 0;
			++str;
		} else
			elem->allowed = 1;

		ptr = str;
		if ( (str = strchr(str, ' ')) != NULL)
			*str++ = '\0';
		if (*ptr == '%') {
			/* Group. */
			elem->user = NULL;
			if ( (elem->group = mem_strdup(++ptr, m_ctl)) == NULL) {
				logconferrx("mem_strdup failed");
				return -1;
			}
		} else {
			/* User. */
			elem->group = NULL;
			if ( (elem->user = mem_strdup(ptr, m_ctl)) == NULL) {
				logconferrx("mem_strdup failed");
				return -1;
			}
		}
	} while (str != NULL);

	return 0;
}

static int
parse_ctl_xxx_acl(const struct ctl_acl_class **classpp)
{
	if ( (*classpp = ctl_class_by_name(parser_args)) == NULL) {
		logconferrx("cannot find ctl class \"%s\"", parser_args);
		return -1;
	}
	return 0;
}

/*
 * Parse "ctl_dump_acl" parameter.
 */
/* ARGSUSED */
static int
parse_ctl_dump_acl(void *arg ATTR_UNUSED)
{
	return parse_ctl_xxx_acl(&ctl_dump_acl);
}

/*
 * Parse "ctl_freeze_acl" parameter.
 */
/* ARGSUSED */
static int
parse_ctl_freeze_acl(void *arg ATTR_UNUSED)
{
	return parse_ctl_xxx_acl(&ctl_freeze_acl);
}

/*
 * Parse "ctl_stat_acl" parameter.
 */
/* ARGSUSED */
static int
parse_ctl_stat_acl(void *arg ATTR_UNUSED)
{
	return parse_ctl_xxx_acl(&ctl_stat_acl);
}

/*
 * Parse "ctl_rule_acl" parameter.
 */
/* ARGSUSED */
static int
parse_ctl_rule_acl(void *arg ATTR_UNUSED)
{
	const struct ctl_acl_class **classpp;

	switch (section) {
#ifdef WITH_RULES
	case IPA_CONF_SECT_RULE:
		classpp = &currule->ctl_rule_acl;
		break;
#endif
#ifdef WITH_AUTORULES
	case IPA_CONF_SECT_AUTORULE:
		classpp = &curautorule->ctl_rule_acl;
		break;
#endif
	case IPA_CONF_SECT_RULEPAT:
		classpp = &currulepat->ctl_rule_acl;
		break;
	default: /* IPA_CONF_SECT_GLOBAL */
		classpp = &global_ctl_rule_acl;
	}

	return parse_ctl_xxx_acl(classpp);
}

#endif /* CTL_CHECK_CREDS */

#ifdef WITH_THRESHOLDS
/*
 * Parse "debug_threshold" parameter.
 */
static int
parse_debug_threshold(void *arg)
{
	int level;

	if ( (level = check_debug_level(*(uint32_t *)arg, 1)) < 0)
		return -1;

	switch (section) {
	case IPA_CONF_SECT_GLOBAL:
		global_debug_threshold = level;
		break;
#ifdef WITH_RULES
	case IPA_CONF_SECT_RULE:
		currule->debug_threshold = level;
		break;
#endif
#ifdef WITH_AUTORULES
	case IPA_CONF_SECT_AUTORULE:
		curautorule->debug_threshold  = level;
		break;
#endif
	default: /* IPA_CONF_SECT_RULEPAT */
		currulepat->debug_threshold = level;
	}

	return 0;
}

/*
 * Parse "debug_threshold_init" parameter.
 */
static int
parse_debug_threshold_init(void *arg)
{
	int level;

	if ( (level = check_debug_level(*(uint32_t *)arg, 1)) < 0)
		return -1;

	switch (section) {
	case IPA_CONF_SECT_GLOBAL:
		global_debug_threshold_init = level;
		break;
#ifdef WITH_RULES
	case IPA_CONF_SECT_RULE:
		currule->debug_threshold_init = level;
		break;
#endif
#ifdef WITH_AUTORULES
	case IPA_CONF_SECT_AUTORULE:
		curautorule->debug_threshold_init  = level;
		break;
#endif
	default: /* IPA_CONF_SECT_RULEPAT */
		currulepat->debug_threshold_init = level;
	}

	return 0;
}

/*
 * Parse "load_threshold" parameter.
 */
static int
parse_load_threshold(void *arg)
{
	if (section == IPA_CONF_SECT_THRESHOLD)
		curthreshold->load_threshold = *(int *)arg;
	else /* IPA_CONF_SECT_GLOBAL */
		global_load_threshold = *(int *)arg;

	return 0;
}

/*
 * Parse "threshold" section.
 */
static int
parse_threshold_sect(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 = mzone_alloc(threshold_mzone)) == NULL) {
		logconferrx("mzone_alloc 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->threshold_info = NULL;
	threshold->thresholdno = thresholdno++;

	threshold->thr = threshold->thr_dev = threshold->cnt_neg = UINT64_C(0);
	threshold->thr_dev_per_cent = 0;

	threshold->cnt_slice =  NULL;
	threshold->cnt_slice_sign = NULL;

	threshold->threshold_type = -1;

	threshold->load_threshold = -1;

	threshold->time_width = 0;
	threshold->time_slice = NULL;

	threshold->worktime = NULL;
	threshold->is_active = ACTIVE_FLAG;

	threshold->db_list = NULL;

	threshold->below_lim = threshold->equal_lim = threshold->above_lim = 0;

	init_cmds_in_threshold(threshold);

	threshold->wpid.type = WPID_TYPE_THRESHOLD;
	threshold->wpid.pid = 0;
	threshold->wpid.u.threshold = threshold;

#ifdef WITH_RULES
	threshold->rule = currule;
#endif

	if (section_top == IPA_CONF_SECT_RULE)
		++nstatthresholds;
	section_top = IPA_CONF_SECT_THRESHOLD;

	if (parser_local_sym_add("threshold", threshold->threshold_name, 0) < 0)
		return -1;

	if (mod_conf_event(IPA_CONF_EVENT_THRESHOLD_BEGIN,
	    threshold->thresholdno, threshold->threshold_name) < 0)
		return -1;

	curthreshold = threshold;

	return 0;
}

/*
 * Parse "threshold" parameter.
 */
static int
parse_threshold_param(void *arg)
{
	uint64_t *ptr = (uint64_t *)arg;

	if ( (curthreshold->thr = *(ptr + 1)) == UINT64_C(0)) {
		logconferrx("argument should be greater than zero");
		return -1;
	}

	curthreshold->cnt_type = (u_int)*ptr;

	return 0;
}

/*
 * Parse "threshold_deviation" parameter.
 */
/* ARGSUSED */
static int
parse_threshold_deviation(void *arg ATTR_UNUSED)
{
	u_int cnt_type;
	struct threshold *threshold;

	threshold = curthreshold;

	if (threshold->thr == UINT64_C(0)) {
		logconferrx("\"threshold\" parameter should be specified before");
		return -1;
	}

	if (get_value_or_per_cent(&threshold->thr_dev, &cnt_type) < 0)
		return -1;

	if (threshold->thr_dev == UINT64_C(0)) {
		logconferrx("argument should be greater than zero");
		return -1;
	}

	if (cnt_type == IPA_CONF_TYPE_PER_CENT)
		threshold->thr_dev_per_cent = (u_int)threshold->thr_dev;
	else {
		if (cnt_type != threshold->cnt_type) {
			logconferrx("different arguments types for \"threshold\" and \"threshold_deviation\" parameters");
			return -1;
		}
		if (threshold->thr_dev >= threshold->thr) {
			logconferrx("value of this parameter should be less than value of \"threshold\" parameter");
			return -1;
		}
		threshold->thr_dev_per_cent = 0;
	}

	return 0;
}

/*
 * Parse "threshold_time_width" parameter.
 */
static int
parse_threshold_time_width(void *arg)
{
	uint64_t timeval = *(uint64_t *)arg;

	if (timeval == UINT64_C(0)) {
		logconferrx("argument should be greater than zero seconds");
		return -1;
	}
	if (timeval > UINT_MAX) {
		logconferrx("too big value %"PRIu64" for u_int type", timeval);
		return -1;
	}

	if (section == IPA_CONF_SECT_THRESHOLD)
		curthreshold->time_width = (u_int)timeval;
	else /* IPA_CONF_SECT_GLOBAL */
		global_threshold_time_width = (u_int)timeval;

	return 0;
}

/*
 * Parse "threshold_time_slice" parameter.
 */
static int
parse_threshold_time_slice(void *arg)
{
	uint64_t timeval = *(uint64_t *)arg;
	struct tevent *tevent;

	if (timeval == UINT64_C(0)) {
		logconferrx("argument should be greater than zero seconds");
		return -1;
	}
	if (timeval > UINT_MAX) {
		logconferrx("too big value %"PRIu64" for u_int type", timeval);
		return -1;
	}
	if (SECONDS_IN_DAY % timeval != 0) {
		logconferrx("number of seconds in one day must be divisible by the value of \"threshold_time_slice\" parameter");
		return -1;
	}

	if ( (tevent = find_tevent((u_int)timeval)) == NULL) {
		logconferrx("find_tevent failed");
		return -1;
	}

	if (section == IPA_CONF_SECT_THRESHOLD)
		curthreshold->time_slice = tevent;
	else /* IPA_CONF_SECT_GLOBAL */
		global_threshold_time_slice = tevent;

	return 0;
}

/*
 * Parse "threshold_type" parameter.
 */
static int
parse_threshold_type(void *arg)
{
	uint32_t val = *(uint32_t *)arg;

	if (val > THRESHOLD_JUMP_OVER_ALLBITS) {
		logconferrx("incorrect value for this parameter");
		return -1;
	}

	if (section == IPA_CONF_SECT_THRESHOLD)
		curthreshold->threshold_type = (int)val;
	else /* IPA_CONF_SECT_GLOBAL */
		global_threshold_type = (int)val;

	return 0;
}

/*
 * Parse "threshold_balance" parameter.
 */
static int
parse_threshold_balance(void *arg)
{
	char	*ptr1 = *(char **)arg, *ptr2;
	u_int	i, val[3];

	for (i = 0, ptr2 = ptr1; i < 3; ptr1 = ptr2, ++i) {
		if (i != 2) {
			ptr2 = strchr(ptr2, ':');
			*ptr2 = '\0';
			++ptr2;
		}
		if (*ptr1 != '-') {
			if (strto_u_int(&val[i], ptr1, (char **)NULL) < 0)
				return -1;
			switch (val[i]) {
			case 0:
			case UINT_MAX:
				logconferrx("values should be greater than zero and less than %u", UINT_MAX);
				return -1;
			}
		} else
			val[i] = UINT_MAX;
	}

	if (section == IPA_CONF_SECT_THRESHOLD) {
		curthreshold->below_lim = curthreshold->below_cnt = val[0];
		curthreshold->equal_lim = curthreshold->equal_cnt = val[1];
		curthreshold->above_lim = curthreshold->above_cnt = val[2];
	} else { /* IPA_CONF_SECT_GLOBAL */
		global_threshold_below_lim = val[0];
		global_threshold_equal_lim = val[1];
		global_threshold_above_lim = val[2];
	}

	return 0;
}

/*
 * Check if threshold's time_width and time_slice can be used together.
 */
static int
validate_threshold_times(u_int time_width, const struct tevent *time_slice)
{
	if (time_width == 0 && time_slice == NULL)
		return 0;

	if ((time_width != 0 && time_slice == NULL) ||
	    (time_width == 0 && time_slice != NULL)) {
		logconferrx("\"threshold_time_width\" and \"threshold_time_slice\" parameters should be defined together in one section");
		return -1;
	}

	if (time_width < time_slice->event_step) {
		logconferrx("the value of \"threshold_time_width\" parameter must be greater than the value of \"threshold_time_slice\" parameter");
		return -1;
	}

	if (time_width % time_slice->event_step != 0) {
		logconferrx("the value of \"threshold_time_width\" parameter must be divisible by the value of \"threshold_time_slice\" parameter");
		return -1;
	}

	return 0;
}

/*
 * Inherit settings for a threshold from the "global" section.
 */
static int
inherit_threshold_from_global(const char *what, const char *sect_name,
    struct threshold *threshold)
{
	if (threshold->load_threshold < 0)
		threshold->load_threshold = global_load_threshold;
	if (threshold->threshold_type < 0)
		threshold->threshold_type = global_threshold_type;
	if (threshold->below_lim == 0) {
		threshold->below_lim = threshold->below_cnt = global_threshold_below_lim;
		threshold->equal_lim = threshold->equal_cnt = global_threshold_equal_lim;
		threshold->above_lim = threshold->above_cnt = global_threshold_above_lim;
	}
	if (threshold->time_width == 0) {
		threshold->time_width = global_threshold_time_width;
		threshold->time_slice = global_threshold_time_slice;
		if (threshold->time_width == 0) {
			conferrx("%s %s, threshold %s: \"threshold_time_width\" and \"threshold_time_slice\" parameters are not defined",
			    what, sect_name, threshold->threshold_name);
			return -1;
		}
	}
	return 0;
}
#endif /* WITH_THRESHOLDS */

#ifdef WITH_AUTORULES
/*
 * Parse "debug_autorule" parameter.
 */
static int
parse_debug_autorule(void *arg)
{
	return debug_autorule = check_debug_level(*(uint32_t *)arg, 1);
}

/*
 * Parse the "autorule" section.
 */
static int
parse_autorule(void *arg)
{
	const char *arule_name = *(char **)arg;
	struct autorule *autorule;

	for (autorule = autorules; autorule < autorules + nautorules; ++autorule)
		if (strcmp(arule_name, autorule->arule_name) == 0) {
			logconferrx("this section is duplicated");
			return -1;
		}
	if (marray_alloc(autorules_marray, &nautorules, 1) < 0) {
		logconferrx("marray_alloc failed");
		return -1;
	}
	autorule = autorules + nautorules;

	if ( (autorule->arule_name = mem_strdup(arule_name, m_anon)) == NULL) {
		logconferrx("mem_strdup failed");
		return -1;
	}
	autorule->aruleno = nautorules++;

	autorule->update_tevent = autorule->append_tevent = NULL;

	autorule->worktime = autorule->worktime_rule = NULL;
	autorule->is_active = ACTIVE_FLAG;

	autorule->ac_list = NULL;
	autorule->db_list = NULL;

	autorule->debug_exec = -1;

	init_cmds_in_autorule(autorule);

#ifdef WITH_LIMITS
	autorule->debug_limit = autorule->debug_limit_init = -1;
	limits_list = &autorule->limits;
	STAILQ_INIT(limits_list);
	limitno = 0;
#endif

#ifdef WITH_THRESHOLDS
	autorule->debug_threshold = autorule->debug_threshold_init = -1;
	thresholds_list = &autorule->thresholds;
	STAILQ_INIT(thresholds_list);
	thresholdno = 0;
#endif

#ifdef CTL_CHECK_CREDS
	autorule->ctl_rule_acl = NULL;
#endif

	section_first = section_top = IPA_CONF_SECT_AUTORULE;

	if (parser_local_sym_add("autorule", autorule->arule_name, 0) < 0)
		return -1;

	if (mod_conf_event(IPA_CONF_EVENT_AUTORULE_BEGIN,
	    autorule->aruleno, autorule->arule_name) < 0)
		return -1;

	curautorule = autorule;

	return 0;
}

#endif /* WITH_AUTORULES */

/*
 * Parse "sleep_after_dump" parameter.
 */
static int
parse_sleep_after_dump(void *arg)
{
	uint64_t timeval = *(uint64_t *)arg;

	if (timeval == UINT64_C(0) || timeval >= 5 * SECONDS_IN_MINUTE) {
		logconferrx("argument should be greater than zero seconds and less than 5 minutes");
		return -1;
	}
	sleep_after_dump = (u_int)timeval;
	return 0;
}

/*
 * Parse "sensitive_time" parameter.
 */
static int
parse_sensitive_time(void *arg)
{
	uint64_t timeval = *(uint64_t *)arg;

	if (timeval == UINT64_C(0)) {
		logconferrx("argument should be greater than zero");
		return -1;
	}
	if (timeval >= 5 * SECONDS_IN_MINUTE) {
		logconferrx("argument should be less than 5 minutes");
		return -1;
	}
	sensitive_time = (u_int)timeval;
	return 0;
}

static int
set_curcmdl(void)
{
	int	x, *set;
#ifdef WITH_LIMITS
	static void *curcmdsect;
#endif

	x = section == SECT_STARTUP ? RC_STARTUP : RC_SHUTDOWN;

	set = NULL;

	switch (section) {
	case SECT_STARTUP:
	case SECT_SHUTDOWN:
		switch (section_top) {
#ifdef WITH_RULES
		case IPA_CONF_SECT_RULE:
			set = &currule->rc[x].set;
			curcmdl = &currule->rc[x].cmdl;
# ifdef WITH_LIMITS
			curcmdsect = &currule->rc[x];
# endif
			break;
#endif
#ifdef WITH_AUTORULES
		case IPA_CONF_SECT_AUTORULE:
			set = &curautorule->rc[x].set;
			curcmdl = &curautorule->rc[x].cmdl;
# ifdef WITH_LIMITS
			curcmdsect = &curautorule->rc[x];
# endif
			break;
#endif
#ifdef WITH_LIMITS
		case IPA_CONF_SECT_LIMIT:
			set = &curlimit->rc[x].set;
			curcmdl = &curlimit->rc[x].cmdl;
			curcmdsect = &curlimit->rc[x];
			break;
#endif
#ifdef WITH_SUBLIMITS
		case SECT_SUBLIMIT:
			set = &cursublimit->rc[x].set;
			curcmdl = &cursublimit->rc[x].cmdl;
			curcmdsect = &cursublimit->rc[x];
			break;
#endif
#ifdef WITH_THRESHOLDS
		case IPA_CONF_SECT_THRESHOLD:
			curcmdl = &curthreshold->rc[x];
			break;
#endif
		case IPA_CONF_SECT_RULEPAT:
			set = &currulepat->rc[x].set;
			curcmdl = &currulepat->rc[x].cmdl;
#ifdef WITH_LIMITS
			curcmdsect = &currulepat->rc[x];
#endif
			break;
		default: /* IPA_CONF_SECT_ROOT */
			curcmdl = x == RC_STARTUP ? &cmds_startup : &cmds_shutdown;
		}
		break;
#ifdef WITH_LIMITS
	case SECT_RESTART:
		curcmdl = &curlimit->restart.cmdl;
		break;
	case SECT_REACH:
# ifdef WITH_SUBLIMITS
		if (section_top == SECT_SUBLIMIT)
			curcmdl = &cursublimit->reach;
		else
# endif
			curcmdl = &curlimit->reach;
		break;
	case SECT_EXPIRE:
		curcmdl = &curlimit->expire.cmdl;
		break;
	case SECT_IF_REACHED:
		curcmdl = &((struct cmds_limit *)curcmdsect)->cmdl_if_reached;
		break;
	case SECT_IF_NOT_REACHED:
		curcmdl = &((struct cmds_limit *)curcmdsect)->cmdl_if_not_reached;
		break;
	case SECT_IF_ALL_REACHED:
		curcmdl = &((struct cmds_rule *)curcmdsect)->cmdl_if_all_reached;
		break;
	case SECT_IF_ALL_NOT_REACHED:
		curcmdl = &((struct cmds_rule *)curcmdsect)->cmdl_if_all_not_reached;
		break;
	case SECT_IF_ANY_REACHED:
		curcmdl = &((struct cmds_rule *)curcmdsect)->cmdl_if_any_reached;
		break;
	case SECT_IF_ANY_NOT_REACHED:
		curcmdl = &((struct cmds_rule *)curcmdsect)->cmdl_if_any_not_reached;
		break;
#endif /* WITH_LIMITS */
#ifdef WITH_THRESHOLDS
	case SECT_BELOW_THRESHOLD:
		curcmdl = &curthreshold->below_threshold;
		break;
	case SECT_ABOVE_THRESHOLD:
		curcmdl = &curthreshold->above_threshold;
		break;
	case SECT_EQUAL_THRESHOLD:
		curcmdl = &curthreshold->equal_threshold;
		break;
#endif /* WITH_THRESHOLDS */
	}

	if (set != NULL) {
		if (*set != 0) {
			logconferrx("this section is duplicated");
			return -1;
		}
		*set = 1;
	}

	if (!STAILQ_EMPTY(&curcmdl->list)) {
		logconferrx("this section is duplicated");
		return -1;
	}

	return 0;
}

static const u_int sect_root[] = { IPA_CONF_SECT_ROOT, 0 };
static const u_int sect_rule[] = { IPA_CONF_SECT_RULE, 0 };
static const u_int sect_rulepat[] = { IPA_CONF_SECT_RULEPAT, 0 };
static const u_int sect_any_rule[] = { IPA_CONF_SECT_GLOBAL, IPA_CONF_SECT_RULE, IPA_CONF_SECT_AUTORULE, IPA_CONF_SECT_RULEPAT, 0 };
static const u_int sect_for_exec[] = { SECT_STARTUP, SECT_SHUTDOWN, SECT_RESTART, SECT_REACH, SECT_EXPIRE, SECT_IF_REACHED, SECT_IF_NOT_REACHED, SECT_IF_ANY_REACHED, SECT_IF_ALL_REACHED, SECT_IF_ANY_NOT_REACHED, SECT_IF_ALL_NOT_REACHED, SECT_BELOW_THRESHOLD, SECT_ABOVE_THRESHOLD, SECT_EQUAL_THRESHOLD, 0 };
#ifdef WITH_ANY_LIMITS
static const u_int sect_for_any_limit[] = { IPA_CONF_SECT_RULE, IPA_CONF_SECT_AUTORULE, IPA_CONF_SECT_RULEPAT, 0 };
#endif
#ifdef WITH_THRESHOLDS
static const u_int sect_threshold[] = { IPA_CONF_SECT_THRESHOLD, 0 };
static const u_int sect_global_threshold[] = { IPA_CONF_SECT_GLOBAL, IPA_CONF_SECT_THRESHOLD, 0 };
#endif
#ifdef WITH_LIMITS
static const u_int sect_limit[] = { IPA_CONF_SECT_LIMIT, 0 };
static const u_int sect_global_limit[] = { IPA_CONF_SECT_GLOBAL, IPA_CONF_SECT_LIMIT, 0 };
static const u_int sect_restart[] = { SECT_RESTART, 0 };
static const u_int sect_expire[] = { SECT_EXPIRE, 0 };
static const u_int sect_startup_shutdown[] = { SECT_STARTUP, SECT_SHUTDOWN, 0 };
static const u_int sect_for_reach[] = { IPA_CONF_SECT_LIMIT, SECT_SUBLIMIT, 0 };
#endif
static const u_int sect_for_startup_shutdown[] = { IPA_CONF_SECT_ROOT, IPA_CONF_SECT_RULE, IPA_CONF_SECT_AUTORULE, IPA_CONF_SECT_LIMIT, SECT_SUBLIMIT, IPA_CONF_SECT_THRESHOLD, IPA_CONF_SECT_RULEPAT, 0 };
#if defined(WITH_RULES) || defined(WITH_ANY_LIMITS)
static const u_int sect_for_info[] = { IPA_CONF_SECT_RULE, IPA_CONF_SECT_LIMIT, IPA_CONF_SECT_THRESHOLD, 0 };
#endif
static const u_int sect_for_db_list[] = { IPA_CONF_SECT_GLOBAL, IPA_CONF_SECT_RULE, IPA_CONF_SECT_AUTORULE, IPA_CONF_SECT_LIMIT, IPA_CONF_SECT_THRESHOLD, IPA_CONF_SECT_RULEPAT, 0 };
static const u_int sect_for_worktime[] = { IPA_CONF_SECT_GLOBAL, IPA_CONF_SECT_RULE, IPA_CONF_SECT_AUTORULE, IPA_CONF_SECT_LIMIT, IPA_CONF_SECT_THRESHOLD, IPA_CONF_SECT_RULEPAT, 0 };
#ifdef WITH_AUTORULES
static const u_int sect_autorule[] = { IPA_CONF_SECT_AUTORULE, 0 };
#endif

/*
 * Sections in ipa.conf(5).
 */
static ipa_conf_sect conf_sect_tbl[] = {
#ifdef WITH_RULES
	{ "rule",		IPA_CONF_SECT_RULE,	1,	NULL,	NULL,	IPA_CONF_TYPE_MISC,	sect_root, parse_rule },
#endif
	{ "rulepat",		IPA_CONF_SECT_RULEPAT,	1,	NULL,	NULL,	IPA_CONF_TYPE_STRING,	sect_root, parse_rulepat },
	{ "global",		IPA_CONF_SECT_GLOBAL,	0,	NULL,	NULL,	IPA_CONF_TYPE_MISC,	sect_root, parse_global },
#ifdef WITH_AUTORULES
	{ "autorule",		IPA_CONF_SECT_AUTORULE,	1,	NULL,	NULL,	IPA_CONF_TYPE_MISC,	sect_root, parse_autorule },
#endif
#ifdef WITH_LIMITS
	{ "limit",		IPA_CONF_SECT_LIMIT,	1,	NULL,	NULL,	IPA_CONF_TYPE_MISC,	sect_for_any_limit, parse_limit_sect },
	{ "restart",		SECT_RESTART,		0,	NULL,	NULL,	IPA_CONF_TYPE_MISC,	sect_limit, NULL },
	{ "reach",		SECT_REACH,		0,	NULL,	NULL,	IPA_CONF_TYPE_MISC,	sect_for_reach, NULL },
	{ "expire",		SECT_EXPIRE,		0,	NULL,	NULL,	IPA_CONF_TYPE_MISC,	sect_limit, NULL },
# ifdef WITH_SUBLIMITS
	{ "sublimit",		SECT_SUBLIMIT,	       -1,	NULL,	NULL,	IPA_CONF_TYPE_MISC,	sect_limit, parse_sublimit },
# endif
	{ "if_reached",		SECT_IF_REACHED,	0,	NULL,	NULL,	IPA_CONF_TYPE_MISC,	sect_startup_shutdown, NULL },
	{ "if_not_reached",	SECT_IF_NOT_REACHED,	0,	NULL,	NULL,	IPA_CONF_TYPE_MISC,	sect_startup_shutdown, NULL },
	{ "if_any_reached",	SECT_IF_ANY_REACHED,	0,	NULL,	NULL,	IPA_CONF_TYPE_MISC,	sect_startup_shutdown, NULL },
	{ "if_all_reached",	SECT_IF_ALL_REACHED,	0,	NULL,	NULL,	IPA_CONF_TYPE_MISC,	sect_startup_shutdown, NULL },
	{ "if_any_not_reached",	SECT_IF_ANY_NOT_REACHED,0,	NULL,	NULL,	IPA_CONF_TYPE_MISC,	sect_startup_shutdown, NULL },
	{ "if_all_not_reached",	SECT_IF_ALL_NOT_REACHED,0,	NULL,	NULL,	IPA_CONF_TYPE_MISC,	sect_startup_shutdown, NULL },
#endif /* WITH_LIMITS */
#ifdef WITH_THRESHOLDS
	{ "threshold",		IPA_CONF_SECT_THRESHOLD,1,	NULL,	NULL,	IPA_CONF_TYPE_MISC,	sect_for_any_limit, parse_threshold_sect },
	{ "below_threshold",	SECT_BELOW_THRESHOLD,	0,	NULL,	NULL,	IPA_CONF_TYPE_MISC,	sect_threshold, NULL },
	{ "above_threshold",	SECT_ABOVE_THRESHOLD,	0,	NULL,	NULL,	IPA_CONF_TYPE_MISC,	sect_threshold, NULL },
	{ "equal_threshold",	SECT_EQUAL_THRESHOLD,	0,	NULL,	NULL,	IPA_CONF_TYPE_MISC,	sect_threshold, NULL },
#endif
	{ "startup",		SECT_STARTUP,		0,	NULL,	NULL,	IPA_CONF_TYPE_MISC,	sect_for_startup_shutdown, NULL },
	{ "shutdown",		SECT_SHUTDOWN,		0,	NULL,	NULL,	IPA_CONF_TYPE_MISC,	sect_for_startup_shutdown, NULL },
	{  NULL,		0,			0,	NULL,	NULL,	IPA_CONF_TYPE_MISC,	0, NULL }
};

#ifdef WITH_LIMITS
#define PAT_TIME_EXP	"^(\
(\\+[mhDWM] ?)?([[:digit:]]+[smhDW]( ?[[:digit:]]+[smhDW])*)?|\
([[:digit:]]+[smhDW]( ?[[:digit:]]+[smhDW])*)?( ?\\+[mhDWM])?)$"
#endif

#ifdef WITH_THRESHOLDS
#define PAT_THR_BALANCE	"^(-|[[:digit:]]+):(-|[[:digit:]]+):(-|[[:digit:]]+)$"
#endif

#define PAT_WORKTIME1	"[SMTWHFA] (\\*|[[:digit:]]{1,2}:[[:digit:]]{1,2}-[[:digit:]]{1,2}:[[:digit:]]{1,2}( [[:digit:]]{1,2}:[[:digit:]]{1,2}-[[:digit:]]{1,2}:[[:digit:]]{1,2})*)"
#define PAT_WORKTIME	"^"PAT_WORKTIME1"( "PAT_WORKTIME1")*$"
#define PAT_LIST	"^[^ \"]+( [^ \"]+)*$"

#ifdef CTL_CHECK_CREDS
# define PAT_ACL	"^[[:alnum:]]+$"
# define PAT_CTL_ACL_CLASS "^[[:alnum:]]+( !?%?[-_[:alnum:]]+)*$"
#endif

/*
 * Parameters in ipa.conf(5).
 */
static ipa_conf_param conf_param_tbl[] = {
	{ "update_time",	-1, NULL,		NULL,		IPA_CONF_TYPE_TIME,	sect_any_rule, parse_update_time },
	{ "append_time",	-1, NULL,		NULL,		IPA_CONF_TYPE_TIME,	sect_any_rule, parse_append_time },
	{ "wakeup_time",	-1, NULL,		NULL,		IPA_CONF_TYPE_TIME,	sect_root, parse_wakeup_time },
	{ "freeze_time",	-1, NULL,		NULL,		IPA_CONF_TYPE_TIME,	sect_root, parse_freeze_time },
	{ "sleep_after_dump",	-1, NULL,		NULL,		IPA_CONF_TYPE_TIME,	sect_root, parse_sleep_after_dump },
	{ "sensitive_time",	-1, NULL,		NULL,		IPA_CONF_TYPE_TIME,	sect_root, parse_sensitive_time },
	{ "exec",		-1, NULL,		NULL,		IPA_CONF_TYPE_MISC,	sect_for_exec, parse_exec },
	{ "sync_exec",		-1, NULL,		NULL,		IPA_CONF_TYPE_BOOLEAN,	sect_for_exec, parse_sync_exec },
	{ "ac_mod",		 1, NULL,		NULL,		IPA_CONF_TYPE_STRING,	sect_root, parse_ac_mod },
	{ "db_mod",		 1, NULL,		NULL,		IPA_CONF_TYPE_STRING,	sect_root, parse_db_mod },
	{ "ac_list",		-1, PAT_LIST,		&reg_list,	IPA_CONF_TYPE_MISC,	sect_any_rule, parse_ac_list },
	{ "db_list",		-1, NULL,		&reg_list,	IPA_CONF_TYPE_MISC,	sect_for_db_list, parse_db_list },
	{ "include",		 1, NULL,		NULL,		IPA_CONF_TYPE_STRING,	NULL, parse_include },
	{ "include_files",	 1, NULL,		NULL,		IPA_CONF_TYPE_STRING,	NULL, parse_include_files },
#if defined(WITH_RULES) || defined(WITH_ANY_LIMITS)
	{ "info",		 1, NULL,		NULL,		IPA_CONF_TYPE_STRING,	sect_for_info, parse_info },
#endif
#ifdef WITH_RULES
	{ "ac_gather_add",	 1, NULL,		NULL,		IPA_CONF_TYPE_STRING,	sect_rule, parse_ac_gather_add },
	{ "ac_gather_sub",	 1, NULL,		NULL,		IPA_CONF_TYPE_STRING,	sect_rule, parse_ac_gather_sub },
#endif
#ifdef WITH_LIMITS
	{ "limit",		-1, NULL,		NULL,		IPA_CONF_TYPE_VALUE,	sect_limit, parse_limit_param },
	{ "restart",		-1, PAT_TIME_EXP,	&reg_texp,	IPA_CONF_TYPE_MISC,	sect_restart, parse_restart },
	{ "expire",		-1, NULL,		&reg_texp,	IPA_CONF_TYPE_MISC,	sect_expire, parse_expire },
	{ "load_limit",		 1, NULL,		NULL,		IPA_CONF_TYPE_BOOLEAN,	sect_global_limit, parse_load_limit },
	{ "debug_limit",	 1, NULL,		NULL,		IPA_CONF_TYPE_UINT32,	sect_any_rule, parse_debug_limit },
	{ "debug_limit_init",	 1, NULL,		NULL,		IPA_CONF_TYPE_UINT32,	sect_any_rule, parse_debug_limit_init },
#endif
#ifdef WITH_THRESHOLDS
	{ "threshold",		-1, NULL,		NULL,		IPA_CONF_TYPE_VALUE,	sect_threshold, parse_threshold_param },
	{ "threshold_deviation",-1, NULL,		NULL,		IPA_CONF_TYPE_MISC,	sect_threshold, parse_threshold_deviation },
	{ "threshold_time_width",-1,NULL,		NULL,		IPA_CONF_TYPE_TIME,	sect_global_threshold, parse_threshold_time_width },
	{ "threshold_time_slice",-1,NULL,		NULL,		IPA_CONF_TYPE_TIME,	sect_global_threshold, parse_threshold_time_slice },
	{ "threshold_type",	 1, NULL,		NULL,		IPA_CONF_TYPE_UINT32,	sect_global_threshold, parse_threshold_type },
	{ "threshold_balance",	 1, PAT_THR_BALANCE,	&reg_thr_balance, IPA_CONF_TYPE_MISC,	sect_global_threshold, parse_threshold_balance },
	{ "load_threshold",	 1, NULL,		NULL,		IPA_CONF_TYPE_BOOLEAN,	sect_global_threshold, parse_load_threshold },
	{ "debug_threshold",	 1, NULL,		NULL,		IPA_CONF_TYPE_UINT32,	sect_any_rule, parse_debug_threshold },
	{ "debug_threshold_init",1, NULL,		NULL,		IPA_CONF_TYPE_UINT32,	sect_any_rule, parse_debug_threshold_init },
#endif
	{ "worktime",		-1, PAT_WORKTIME,	&reg_worktime,	IPA_CONF_TYPE_MISC,	sect_for_worktime, parse_worktime },
#ifdef WITH_AUTORULES
	{ "worktime_rule",	-1, NULL,		&reg_worktime,	IPA_CONF_TYPE_MISC,	sect_autorule, parse_worktime_rule },
#endif
	{ "posix_re_pattern",	 1, NULL,		NULL,		IPA_CONF_TYPE_BOOLEAN,	sect_root, parse_posix_re_pattern },
	{ "only_abs_paths",	 1, NULL,		NULL,		IPA_CONF_TYPE_BOOLEAN,	sect_root, parse_only_abs_paths },
	{ "keep_rules_order",	 1, NULL,		NULL,		IPA_CONF_TYPE_BOOLEAN,	sect_root, parse_keep_rules_order },
	{ "check_next_rulepat",	 1, NULL,		NULL,		IPA_CONF_TYPE_BOOLEAN,	sect_rulepat, parse_check_next_rulepat },
	{ "shell_path",		 1, NULL,		NULL,		IPA_CONF_TYPE_STRING,	sect_root, parse_shell_path },
	{ "shell_arg1",		 1, NULL,		NULL,		IPA_CONF_TYPE_STRING,	sect_root, parse_shell_arg1 },
	{ "debug_time",		 1, NULL,		NULL,		IPA_CONF_TYPE_UINT32,	sect_root, parse_debug_time },
	{ "debug_ac_null",	 1, NULL,		NULL,		IPA_CONF_TYPE_UINT32,	sect_root, parse_debug_ac_null },
	{ "debug_db_null",	 1, NULL,		NULL,		IPA_CONF_TYPE_UINT32,	sect_root, parse_debug_db_null },
	{ "debug_exec",		 1, NULL,		NULL,		IPA_CONF_TYPE_UINT32,	sect_any_rule, parse_debug_exec },
	{ "debug_worktime",	 1, NULL,		NULL,		IPA_CONF_TYPE_UINT32,	sect_root, parse_debug_worktime },
	{ "ctl_enable",		 1, NULL,		NULL,		IPA_CONF_TYPE_BOOLEAN,	sect_root, parse_ctl_enable },
	{ "ctl_socket_path",	 1, NULL,		NULL,		IPA_CONF_TYPE_STRING,	sect_root, parse_ctl_socket_path },
	{ "ctl_socket_perm",	 1, NULL,		NULL,		IPA_CONF_TYPE_MISC,	sect_root, parse_ctl_socket_perm },
	{ "ctl_timeout",	-1, NULL,		NULL,		IPA_CONF_TYPE_TIME,	sect_root, parse_ctl_timeout },
	{ "debug_ctl",		 1, NULL,		NULL,		IPA_CONF_TYPE_UINT32,	sect_root, parse_debug_ctl },
#ifdef CTL_CHECK_CREDS
	{ "ctl_rule_acl",	 1, PAT_ACL,		&reg_acl,	IPA_CONF_TYPE_MISC,	sect_any_rule, parse_ctl_rule_acl },
	{ "ctl_dump_acl",	 1, NULL,		&reg_acl,	IPA_CONF_TYPE_MISC,	sect_root, parse_ctl_dump_acl },
	{ "ctl_freeze_acl",	 1, NULL,		&reg_acl,	IPA_CONF_TYPE_MISC,	sect_root, parse_ctl_freeze_acl },
	{ "ctl_stat_acl",	 1, NULL,		&reg_acl,	IPA_CONF_TYPE_MISC,	sect_root, parse_ctl_stat_acl },
	{ "ctl_acl_class",	-1, PAT_CTL_ACL_CLASS,	&reg_ctl_acl_class, IPA_CONF_TYPE_MISC,	sect_root, parse_ctl_acl_class },
#endif
#ifdef WITH_AUTORULES
	{ "debug_autorule",	 1, NULL,		NULL,		IPA_CONF_TYPE_UINT32,	sect_root, parse_debug_autorule },
#endif
	{  NULL,		 0, NULL,		NULL,		0,			0, NULL }
};

/*
 * Main function for parsing configuration file(s).
 */
int
configure(PARSING_MODE mode)
{
	int	real_config;
	char	*ptr;
	const u_int *id_ptr;
	const char *prefix;		/* Configuration prefix. */

#ifdef WITH_RULES
	const struct worktime *rule_wt;
#endif

	struct rulepat *rulepat;

#ifdef WITH_RULES
	struct rule *rule;
#endif

#ifdef WITH_LIMITS
	struct limit *limit;
	struct cmd_list	*prevcmdl;
#endif

#ifdef WITH_THRESHOLDS
	struct threshold *threshold;
#endif

#ifdef WITH_AUTORULES
	struct autorule	*autorule;
#endif

	/* 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 ac_mod *ac_mod;
	const struct db_mod *db_mod;

	struct time_param *time_param;
	struct debug_param *debug_param;

	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 ipa.conf(5) section. */

	const struct get_arg_tbl *get_arg_ptr;

	if (mode == TEST_PARSING || mode == CMD_PARSING)
		use_log = real_config = 0;
	else
		real_config = 1;

	xvlogmsgx = vconferrx_priority;

	/* Set wrappers for log functions. */
	mvlogmsgx = mvlogmsgx_conf_wrapper;
	parser_vlogmsgx = parser_vlogmsgx_wrapper;

	if (memfunc_init() < 0) {
		logmsgx(IPA_LOG_ERR, "configure: memfunc_init failed");
		return -1;
	}

	if ((m_anon = mem_type_new_local(MTYPE_NAME(anonymous), "Anonymous memory", 0)) == NULL ||
	    (m_tmp = mem_type_new_local(MTYPE_NAME(tmp), "Temporal memory", 0)) == NULL ||
	    (m_ctl = mem_type_new_local(MTYPE_NAME(ctl), "Memory for ctl", 0)) == NULL ||
	    (m_cmd = mem_type_new_local(MTYPE_NAME(cmd), "Memory for commands", 0)) == NULL ||
	    (m_parser = mem_type_new_local(MTYPE_NAME(parser), "Memory allocated by parser", MEMTYPE_FLAGS)) == NULL) {
		logmsgx(IPA_LOG_ERR, "configure: mem_type_new_local failed");
		return -1;
	}

	nac_mods = ndb_mods = 0;

	global_ac_list = NULL;
	global_db_list = NULL;

	for (debug_param = global_debug_param; debug_param->param_name != NULL; ++debug_param)
		*debug_param->param_value = -1;

	SLIST_INIT(&ac_mod_list);
	SLIST_INIT(&db_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;

	if (mode != RECONFIG_PARSING) {
		init_rules_hash();
#ifdef WITH_ANY_LIMITS
		init_wpid_hash();
#endif
	}

	if ( (rule_mzone = mzone_init(MZONE_NAME(rule), "Rules", MEMFLAG_OPTIMIZE,
	    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;
	STAILQ_INIT(&rulepats_list);
	rulepatno = 0;

#ifdef WITH_RULES
	SLIST_INIT(&ac_gather_list);
#endif

	nstatrules = 0;

	if ( (tint_mzone = mzone_init(MZONE_NAME(tint), "Time intervals", 0,
	    sizeof(struct tint), TINT_NSIZE, TINT_NALLOC)) == NULL)
		goto parsing_failed;
	STAILQ_INIT(&tint_sets);

	if ( (worktime_mzone = mzone_init(MZONE_NAME(worktime), "Worktimes", 0,
	    sizeof(struct worktime), WORKTIME_NSIZE, WORKTIME_NALLOC)) == NULL)
		goto parsing_failed;
	init_worktime_default();
	SLIST_INIT(&worktimes_list);
	SLIST_INSERT_HEAD(&worktimes_list, &worktime_default, link);

	if ( (tevent_mzone = mzone_init(MZONE_NAME(tevent), "Time events", 0,
	    sizeof(struct tevent), TEVENT_NSIZE, TEVENT_NALLOC)) == NULL)
		goto parsing_failed;
	SLIST_INIT(&tevents_list);

	if ( (cmd_mzone = mzone_init(MZONE_NAME(cmd), "Commands", MEMFLAG_OPTIMIZE,
	    sizeof(struct cmd), CMD_NSIZE, CMD_NALLOC)) == NULL)
		goto parsing_failed;

	init_cmd_list(&cmds_startup);
	init_cmd_list(&cmds_shutdown);

#ifdef WITH_LIMITS
	if ( (limit_mzone = mzone_init(MZONE_NAME(limit), "Limits", MEMFLAG_OPTIMIZE,
	    sizeof(struct limit), LIMIT_NSIZE, LIMIT_NALLOC)) == NULL)
		goto parsing_failed;
	global_load_limit = -1;
#endif

#ifdef WITH_SUBLIMITS
	if ( (sublimit_mzone = mzone_init(MZONE_NAME(sublimit), "Sublimits", MEMFLAG_OPTIMIZE,
	    sizeof(struct sublimit), SUBLIMIT_NSIZE, SUBLIMIT_NALLOC)) == NULL)
		goto parsing_failed;
#endif

	nstatlimits = ndynlimits = nstatsublimits = ndynsublimits = 0;

#ifdef WITH_THRESHOLDS
	if ( (threshold_mzone = mzone_init(MZONE_NAME(threshold), "Thresholds", MEMFLAG_OPTIMIZE,
	    sizeof(struct threshold), THRESHOLD_NSIZE, THRESHOLD_NALLOC)) == NULL)
		goto parsing_failed;
	global_threshold_type = -1;
	global_load_threshold = -1;
	global_threshold_time_width = 0;
	global_threshold_time_slice = NULL;
	global_threshold_below_lim = global_threshold_equal_lim =
	    global_threshold_above_lim = 0;
#endif
	nstatthresholds = ndynthresholds = 0;

#ifdef WITH_AUTORULES
	if ( (autorules_marray = marray_init(MARRAY_NAME(autorules), "Autorules",
	    0, (void *)&autorules, sizeof(struct autorule),
	    AUTORULE_NSIZE, AUTORULE_NALLOC)) == NULL)
		goto parsing_failed;
	nautorules = 0;
	rules_ptr_marray = NULL;
#endif

	global_update_tevent = global_append_tevent = NULL;
	global_worktime = NULL;
	global_section_set = 0;
	only_abs_paths = keep_rules_order = posix_re_pattern = -1;

	for (time_param = common_time_param; time_param->param_name != NULL; ++time_param)
		*time_param->param_value = 0;

	for (debug_param = common_debug_param; debug_param->param_name != NULL; ++debug_param)
		*debug_param->param_value = -1;

	ctl_enable = -1;
	ctl_socket_path = NULL;
	ctl_socket_perm = 0;
	ctl_timeout = 0;
#ifdef CTL_CHECK_CREDS
	if ( (ctl_acl_elem_mzone = mzone_init(MZONE_NAME(ctl_acl_elem), "ACL elements", 0,
	    sizeof(struct ctl_acl_elem), CTL_ACL_ELEM_NSIZE, CTL_ACL_ELEM_NALLOC)) == NULL)
		goto parsing_failed;
	if ( (ctl_acl_class_mzone = mzone_init(MZONE_NAME(ctl_acl_class), "ACL classes", 0,
	    sizeof(struct ctl_acl_class), CTL_ACL_CLASS_NSIZE, CTL_ACL_CLASS_NALLOC)) == NULL)
		goto parsing_failed;
	global_ctl_rule_acl = ctl_dump_acl = ctl_freeze_acl = ctl_stat_acl = NULL;
	STAILQ_INIT(&ctl_acl_classes);
#endif

	section = section_first = section_top = IPA_CONF_SECT_ROOT;

	parsing_mode = mode;

	memfunc.m_parser = m_parser;

	if (real_config)
		logmsgx(IPA_LOG_INFO, "use configuration file %s, parsing...", ipa_conf_file);

	if (check_conf_file(ipa_conf_file, 0) < 0)
		goto parsing_failed;

	/* Init parser. */
	if (parser_init() < 0) {
		logmsgx(IPA_LOG_ERR, "configure: parser_init failed");
		goto parsing_failed;
	}

	/* Init first pbuf. */
	if ( (pbuf = parser_new_pbuf(0)) == NULL)
		goto parsing_failed;
	pbuf->fname = ipa_conf_file;
	if ( (pbuf->fp = fopen(ipa_conf_file, "r")) == NULL) {
		conferrx("fopen(%s, \"r\"): %s", ipa_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, mode != RECONFIG_PARSING,
	    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 != ipa_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 ( (db_mod = db_mod_by_prefix(prefix)) != NULL) {
						conf_sect_hash_ptr = db_mod->conf_sect_hash;
						conf_param_hash_ptr = db_mod->conf_param_hash;
						conf_sect_tbl_ptr = db_mod->ipa_db_mod->conf_sect_tbl;
						conf_param_tbl_ptr = db_mod->ipa_db_mod->conf_param_tbl;
						curmodfile = db_mod->mod_file;
					} else if ( (ac_mod = ac_mod_by_prefix(prefix)) != NULL) {
						conf_sect_hash_ptr = ac_mod->conf_sect_hash;
						conf_param_hash_ptr = ac_mod->conf_param_hash;
						conf_sect_tbl_ptr = ac_mod->ipa_ac_mod->conf_sect_tbl;
						conf_param_tbl_ptr = ac_mod->ipa_ac_mod->conf_param_tbl;
						curmodfile = ac_mod->mod_file;
						db_mod = NULL;
					} 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 (db_mod != NULL) {
					if (db_mod_conf_event(db_mod, IPA_CONF_EVENT_CUSTOM_SECT_BEGIN, section, (void *)NULL) < 0)
						goto parsing_failed;
				} else {
					if (ac_mod_conf_event(ac_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;
			}

			if (in_own_sect && section >= SECT_RESTART &&
			    section <= SECT_ABOVE_THRESHOLD) {
				/* Do additional settings for ipa.conf(5)'s sections. */
#ifdef WITH_LIMITS
				switch (section) {
				case SECT_IF_ALL_REACHED:
				case SECT_IF_ALL_NOT_REACHED:
				case SECT_IF_ANY_REACHED:
				case SECT_IF_ANY_NOT_REACHED:
					switch (section_top) {
					case IPA_CONF_SECT_RULE:
					case IPA_CONF_SECT_AUTORULE:
					case IPA_CONF_SECT_RULEPAT:
						break;
					default:
						goto section_not_expected;
					}
					prevcmdl = curcmdl;
					break;
				case SECT_IF_REACHED:
				case SECT_IF_NOT_REACHED:
					switch (section_top) {
					case IPA_CONF_SECT_LIMIT:
					case SECT_SUBLIMIT:
						break;
					default:
						goto section_not_expected;
					}
					prevcmdl = curcmdl;
					break;
				}
#endif /* WITH_LIMITS */
				if (set_curcmdl() < 0)
					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 ( (db_mod = db_mod_by_prefix(prefix)) != NULL) {
						conf_param_hash_ptr = db_mod->conf_param_hash;
						conf_param_tbl_ptr = db_mod->ipa_db_mod->conf_param_tbl;
						curmodfile = db_mod->mod_file;
					} else if ( (ac_mod = ac_mod_by_prefix(prefix)) != NULL) {
						conf_param_hash_ptr = ac_mod->conf_param_hash;
						conf_param_tbl_ptr = ac_mod->ipa_ac_mod->conf_param_tbl;
						curmodfile = ac_mod->mod_file;
						db_mod = NULL;
					} 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 */
			cursect = NULL;
			if (!in_own_sect) {
				/* Register config event in module. */
				if (db_mod != NULL) {
					if (db_mod_conf_event(db_mod, IPA_CONF_EVENT_CUSTOM_SECT_END, section, (void *)NULL) < 0)
						goto parsing_failed;
				} else {
					if (ac_mod_conf_event(ac_mod, IPA_CONF_EVENT_CUSTOM_SECT_END, section, (void *)NULL) < 0)
						goto parsing_failed;
				}
			} else {
				switch (section) {
#ifdef WITH_RULES
				case IPA_CONF_SECT_RULE:
					section_top = IPA_CONF_SECT_ROOT;
					if (mod_conf_event(IPA_CONF_EVENT_RULE_END, currule->ruleno, (void *)NULL) < 0)
						goto parsing_failed;
					break;
#endif
				case IPA_CONF_SECT_RULEPAT:
					section_top = IPA_CONF_SECT_ROOT;
					if (mod_conf_event(IPA_CONF_EVENT_RULEPAT_END, currulepat->rulepatno, (void *)NULL) < 0)
						goto parsing_failed;
					break;
#ifdef WITH_AUTORULES
				case IPA_CONF_SECT_AUTORULE:
					section_top = IPA_CONF_SECT_ROOT;
					if (mod_conf_event(IPA_CONF_EVENT_AUTORULE_END, curautorule->aruleno, (void *)NULL) < 0)
						goto parsing_failed;
					break;
#endif
				case IPA_CONF_SECT_GLOBAL:
					section_top = IPA_CONF_SECT_ROOT;
#ifdef WITH_THRESHOLDS
					if (validate_threshold_times(global_threshold_time_width, global_threshold_time_slice) < 0)
						goto parsing_failed;
#endif
					if (mod_conf_event(IPA_CONF_EVENT_GLOBAL_END, 0, (void *)NULL) < 0)
						goto parsing_failed;
					break;
#ifdef WITH_LIMITS
				case IPA_CONF_SECT_LIMIT:
					section_top = section_first;
					if (curlimit->lim == UINT64_C(0)) {
						logconferrx("parameter \"limit\" must be specified");
						goto parsing_failed;
					}
					(void)parser_local_sym_del("limit");
					if (mod_conf_event(IPA_CONF_EVENT_LIMIT_END, curlimit->limitno, (void *)NULL) < 0)
						goto parsing_failed;
					break;
# ifdef WITH_SUBLIMITS
				case SECT_SUBLIMIT:
					section_top = IPA_CONF_SECT_LIMIT;
					(void)parser_local_sym_del("sublimit");
					break;
# endif
				case SECT_RESTART:
					if (curlimit->restart.restart.upto == TEXP_UPTO_NOTSET) {
						logconferrx("parameter \"restart\" must be specified");
						goto parsing_failed;
					}
					break;
				case SECT_EXPIRE:
					if (curlimit->expire.expire.upto == TEXP_UPTO_NOTSET) {
						logconferrx("parameter \"expire\" must be specified");
						goto parsing_failed;
					}
					break;
				case SECT_IF_REACHED:
				case SECT_IF_NOT_REACHED:
				case SECT_IF_ALL_REACHED:
				case SECT_IF_ALL_NOT_REACHED:
				case SECT_IF_ANY_REACHED:
				case SECT_IF_ANY_NOT_REACHED:
					curcmdl = prevcmdl;
					break;
#endif /* WITH_LIMITS */
#ifdef WITH_THRESHOLDS
				case IPA_CONF_SECT_THRESHOLD:
					section_top = section_first;
					if (curthreshold->thr == UINT64_C(0)) {
						logconferrx("parameter \"threshold\" must be specified");
						goto parsing_failed;
					}
					if (validate_threshold_times(curthreshold->time_width, curthreshold->time_slice) < 0)
						goto parsing_failed;
					(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 */
				}
			}
			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 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;
			}
		}
	}

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 (parser_deinit() < 0)
		goto parsing_failed;

	if (mzone_is_empty(worktime_mzone)) {
		mzone_deinit(worktime_mzone);
		mzone_deinit(tint_mzone);
		worktime_mzone = tint_mzone = NULL;
	}

	if (mzone_is_empty(cmd_mzone)) {
		mzone_deinit(cmd_mzone);
		cmd_mzone = NULL;
	}

	if (mzone_is_empty(rulepat_mzone)) {
		mzone_deinit(rulepat_mzone);
		rulepat_mzone = NULL;
	}

#ifdef WITH_LIMITS
	if (mzone_is_empty(limit_mzone)) {
		mzone_deinit(limit_mzone);
		limit_mzone = NULL;
	}
#endif

#ifdef WITH_SUBLIMITS
	if (mzone_is_empty(sublimit_mzone)) {
		mzone_deinit(sublimit_mzone);
		sublimit_mzone = NULL;
	}
#endif

#ifdef WITH_THRESHOLDS
	if (mzone_is_empty(threshold_mzone)) {
		mzone_deinit(threshold_mzone);
		threshold_mzone = NULL;
	}
#endif

#ifdef CTL_CHECK_CREDS
	if (mzone_is_empty(ctl_acl_elem_mzone)) {
		mzone_deinit(ctl_acl_elem_mzone);
		ctl_acl_elem_mzone = NULL;
	}
	if (mzone_is_empty(ctl_acl_class_mzone)) {
		mzone_deinit(ctl_acl_class_mzone);
		ctl_acl_class_mzone = NULL;
	}
#endif

	SLIST_FOREACH(ac_mod, &ac_mod_list, link) {
		if (mimic_real_config)
			if (ac_mod->ipa_ac_mod->conf_mimic_real() < 0) {
				conferrx("module %s: conf_mimic_real failed", ac_mod->mod_file);
				goto parsing_failed;
			}
		deinit_conf_tbls(1,
		    ac_mod->ipa_ac_mod->conf_sect_tbl, ac_mod->conf_sect_hash,
		    ac_mod->ipa_ac_mod->conf_param_tbl, ac_mod->conf_param_hash);
		if (ac_mod->ipa_ac_mod->conf_deinit() < 0) {
			conferrx("module %s: conf_deinit failed", ac_mod->mod_file);
			goto parsing_failed;
		}
	}

	SLIST_FOREACH(db_mod, &db_mod_list, link) {
		if (mimic_real_config)
			if (db_mod->ipa_db_mod->conf_mimic_real() < 0) {
				conferrx("module %s: conf_mimic_real failed", db_mod->mod_file);
				goto parsing_failed;
			}
		deinit_conf_tbls(1,
		    db_mod->ipa_db_mod->conf_sect_tbl, db_mod->conf_sect_hash,
		    db_mod->ipa_db_mod->conf_param_tbl, db_mod->conf_param_hash);
		if (db_mod->ipa_db_mod->conf_deinit() < 0) {
			conferrx("module %s: conf_deinit failed", db_mod->mod_file);
			goto parsing_failed;
		}
	}

	if (real_config)
		mimic_real_config = 1;

	if (mimic_real_config || mode == CMD_PARSING) {
		if (shell_path == NULL)
			shell_path = shell_path_default;
		if (shell_arg1 == NULL)
			shell_arg1 = shell_arg1_default;
	}

	if (mimic_real_config) {
		/* Set global parameters. */
		global_section_set = 1;
		if (only_abs_paths < 0)
			only_abs_paths = 0;
		if (keep_rules_order < 0)
			keep_rules_order = 0;
		if (wakeup_time == 0)
			wakeup_time = WAKEUP_TIME_DEF;
		if (sensitive_time == 0)
			sensitive_time = SENSITIVE_TIME_DEF;
		if (global_update_tevent == NULL)
			if ( (global_update_tevent = find_tevent(UPDATE_TIME_DEF)) == NULL) {
				conferrx("find_tevent_failed for default value of \"update_time\" parameter");
				goto parsing_failed;
			}
		if (global_ac_list == NULL)
			global_ac_list = &ac_list_null;
		if (global_db_list == NULL)
			global_db_list = &db_list_null;
		if (global_worktime == NULL)
			global_worktime = &worktime_default;
#ifdef WITH_LIMITS
		if (global_load_limit < 0)
			global_load_limit = 0;
#endif
#ifdef WITH_THRESHOLDS
		if (global_threshold_type < 0)
			global_threshold_type = 0;
		if (global_load_threshold < 0)
			global_load_threshold = 0;
		if (global_threshold_below_lim == 0)
			global_threshold_below_lim = global_threshold_equal_lim =
			    global_threshold_above_lim = UINT_MAX;
#endif
		for (debug_param = global_debug_param; debug_param->param_name != NULL; ++debug_param)
			if (*debug_param->param_value < 0)
				*debug_param->param_value = 0;

		if (cmds_startup.sync_exec < 0)
			cmds_startup.sync_exec = 1;
		if (cmds_shutdown.sync_exec < 0)
			cmds_shutdown.sync_exec = 1;

		/* 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->update_tevent == NULL)
					rulepat->update_tevent = global_update_tevent;
				if (rulepat->append_tevent == NULL)
					rulepat->append_tevent = global_append_tevent;
				if (rulepat->worktime == NULL)
					rulepat->worktime = global_worktime;
				if (rulepat->ac_list == NULL)
					rulepat->ac_list = global_ac_list;
				if (rulepat->db_list == NULL)
					rulepat->db_list = global_db_list;
				if (rulepat->debug_exec < 0)
					rulepat->debug_exec = global_debug_exec;
#ifdef CTL_CHECK_CREDS
				if (rulepat->ctl_rule_acl == NULL)
					rulepat->ctl_rule_acl = global_ctl_rule_acl;
#endif
#ifdef WITH_LIMITS
				if (rulepat->debug_limit < 0)
					rulepat->debug_limit = global_debug_limit;
				if (rulepat->debug_limit_init < 0)
					rulepat->debug_limit_init = global_debug_limit_init;
				STAILQ_FOREACH(limit, &rulepat->limits, link) {
					inherit_limit_from_global(limit);
					set_sync_exec_in_limit(limit);
				}
#endif /* WITH_LIMITS */
#ifdef WITH_THRESHOLDS
				if (rulepat->debug_threshold < 0)
					rulepat->debug_threshold = global_debug_threshold;
				if (rulepat->debug_threshold_init < 0)
					rulepat->debug_threshold_init = global_debug_threshold_init;
				STAILQ_FOREACH(threshold, &rulepat->thresholds, link) {
					if (inherit_threshold_from_global("rulepat",
					    rulepat->pat, threshold) < 0)
						goto parsing_failed;
					set_sync_exec_in_threshold(threshold);
				}
#endif /* WITH_THRESHOLDS */
			}
			set_sync_exec_in_rulepat(rulepat);
		}
	}

#ifdef WITH_AUTORULES
	STAILQ_INIT(&autorules_list);
	if (nautorules != 0) {
		const struct ac_list *ac_list;

		marray_minimize(autorules_marray);
		for (autorule = autorules; autorule < autorules + nautorules; ++autorule) {
			STAILQ_INSERT_TAIL(&autorules_list, autorule, link);
			ac_list = autorule->ac_list != NULL ? autorule->ac_list : global_ac_list;
			if (ac_list != NULL && !STAILQ_EMPTY(ac_list))
				if (STAILQ_NEXT(STAILQ_FIRST(ac_list), link) != NULL) {
					conferrx("autorule %s: only one accounting system can be used in autorule",
					    autorule->arule_name);
					goto parsing_failed;
				}
			if (mimic_real_config) {
				/* Inherit some settings for autorule{} from global{}. */
				if (autorule->ac_list == NULL) {
					autorule->ac_list = global_ac_list;
					ac_inc_ref_count(global_ac_list);
				}
				if (autorule->update_tevent == NULL)
					autorule->update_tevent = global_update_tevent;
				if (autorule->worktime == NULL)
					autorule->worktime = global_worktime;
				set_sync_exec_in_autorule(autorule);
			}
# ifdef WITH_LIMITS
			STAILQ_FOREACH(limit, &autorule->limits, link) {
				if (check_worktime_subset(autorule->worktime_rule, limit->worktime) < 0) {
					conferrx("autorule %s, limit %s: limit's worktime must be a subset of autorule's worktime_rule",
					    autorule->arule_name, limit->limit_name);
					goto parsing_failed;
				}
				if (mimic_real_config) {
					inherit_limit_from_global(limit);
					set_sync_exec_in_limit(limit);
				}
			}
# endif /* WITH_LIMITS */
# ifdef WITH_THRESHOLDS
			STAILQ_FOREACH(threshold, &autorule->thresholds, link) {
				if (check_worktime_subset(autorule->worktime_rule, threshold->worktime) < 0) {
					conferrx("autorule %s, threshold %s: threshold's worktime must be a subset of autorule's worktime_rule",
					    autorule->arule_name, threshold->threshold_name);
					goto parsing_failed;
				}
				if (mimic_real_config) {
					if (inherit_threshold_from_global("autorule",
					    autorule->arule_name, threshold) < 0)
						goto parsing_failed;
					set_sync_exec_in_threshold(threshold);
				}
			}
# endif /* WITH_THRESHOLDS */
		}
	} else
		marray_deinit(autorules_marray);
#endif /* WITH_AUTORULES */

#ifdef WITH_RULES
	/* Init ac_gather_rev_mzone, needed only if there are static rules. */
	if (!SLIST_EMPTY(&ac_gather_list)) {
		if ( (ac_gather_rev_mzone = mzone_init(MZONE_NAME(ac_gather_rev), "Reverse ac_gather", 0,
		    sizeof(struct ac_gather_rev), AC_GATHER_REV_NSIZE, AC_GATHER_REV_NALLOC)) == NULL)
			goto parsing_failed;
		has_ac_gather = 1;
	} else {
		ac_gather_rev_mzone = NULL;
		has_ac_gather = 0;
	}

	TAILQ_FOREACH(rule, &rules_list, list) {
		if (init_ac_gather_rev(rule) < 0)
			goto parsing_failed;

		STAILQ_FOREACH(rulepat, &rulepats_list, link)
			if (regexec(&rulepat->reg, rule->rule_name, 0, (regmatch_t *)NULL, 0) == 0) {
# ifdef WITH_RULES
				rule_wt = rule->worktime != NULL ? rule->worktime : rulepat->worktime;
# endif
				if (mimic_real_config) {
					/* Inherit some settings for rule{} from rulepat{}. */
					if (rule->update_tevent == NULL)
						rule->update_tevent = rulepat->update_tevent;
					if (rule->append_tevent == NULL)
						rule->append_tevent = rulepat->append_tevent;
					rule->worktime = rule_wt;
					if (rule->ac_list == NULL)
						if ( (rule->ac_list = rulepat->ac_list) != NULL)
							ac_inc_ref_count(rule->ac_list);
					if (rule->db_list == NULL)
						rule->db_list = rulepat->db_list;
					if (rule->debug_exec < 0)
						rule->debug_exec = rulepat->debug_exec;
# ifdef WITH_LIMITS
					if (rule->debug_limit < 0)
						rule->debug_limit = rulepat->debug_limit;
					if (rule->debug_limit_init < 0)
						rule->debug_limit_init = rulepat->debug_limit_init;
# endif
# ifdef WITH_THRESHOLDS
					if (rule->debug_threshold < 0)
						rule->debug_threshold = rulepat->debug_threshold;
					if (rule->debug_threshold_init < 0)
						rule->debug_threshold_init = rulepat->debug_threshold_init;
# endif
# ifdef CTL_CHECK_CREDS
					if (rule->ctl_rule_acl == NULL)
						rule->ctl_rule_acl = rulepat->ctl_rule_acl;
# endif
					if (mod_conf_inherit(rulepat, rule) < 0) {
						conferrx("configure: mod_conf_inherit failed");
						goto parsing_failed;
					}
				}
				if (mimic_real_config || mode == CMD_PARSING) {
					if (rule->rc[RC_STARTUP].set == 0 && rulepat->rc[RC_STARTUP].set != 0) {
						if (copy_cmds_rule(rule, &rule->rc[RC_STARTUP], &rulepat->rc[RC_STARTUP]) < 0) {
							conferrx("rule %s: cannot copy all commands from rulepat %s { startup {}}",
							    rule->rule_name, parser_buf_to_string(rulepat->pat));
							goto parsing_failed;
						}
						rule->rc[RC_STARTUP].set = 1;
					}
					if (rule->rc[RC_SHUTDOWN].set == 0 && rulepat->rc[RC_SHUTDOWN].set != 0) {
						if (copy_cmds_rule(rule, &rule->rc[RC_SHUTDOWN], &rulepat->rc[RC_SHUTDOWN]) < 0) {
							conferrx("rule %s: cannot copy all commands from rulepat %s { shutdown {}}",
							    rule->rule_name, parser_buf_to_string(rulepat->pat));
							goto parsing_failed;
						}
						rule->rc[RC_SHUTDOWN].set = 1;
					}
#ifdef WITH_LIMITS
					if (STAILQ_EMPTY(&rule->limits) && !STAILQ_EMPTY(&rulepat->limits)) {
						if (copy_limits(rule, &rulepat->limits, 0) < 0) {
							conferrx("rule %s: cannot copy all limits from rulepat %s",
							    rule->rule_name, parser_buf_to_string(rulepat->pat));
							goto parsing_failed;
						}
						rule->free_mask &= ~RULE_FREE_LIMITS;
					}
#endif
#ifdef WITH_THRESHOLDS
					if (STAILQ_EMPTY(&rule->thresholds) && !STAILQ_EMPTY(&rulepat->thresholds)) {
						if (copy_thresholds(rule, &rulepat->thresholds, 0) < 0) {
							conferrx("rule %s: cannot copy all thresholds from rulepat %s",
							    rule->rule_name, parser_buf_to_string(rulepat->pat));
							goto parsing_failed;
						}
						rule->free_mask &= ~RULE_FREE_THRESHOLDS;
					}
#endif
				}
				if (rulepat->check_next_rulepat == 0)
					break;
			}

		if (rulepat == NULL) {
# ifdef WITH_RULES
			rule_wt = rule->worktime != NULL ? rule->worktime : global_worktime;
# endif
			if (mimic_real_config) {
				/* Inherit some settings for rule{} from global{}. */
				if (rule->update_tevent == NULL)
					rule->update_tevent = global_update_tevent;
				if (rule->append_tevent == NULL)
					rule->append_tevent = global_append_tevent;
				rule->worktime = rule_wt;
				if (rule->ac_list == NULL) {
					rule->ac_list = global_ac_list;
					ac_inc_ref_count(rule->ac_list);
				}
				if (rule->db_list == NULL)
					rule->db_list = global_db_list;
				if (rule->debug_exec < 0)
					rule->debug_exec = global_debug_exec;
# ifdef WITH_LIMITS
				if (rule->debug_limit < 0)
					rule->debug_limit = global_debug_limit;
				if (rule->debug_limit_init < 0)
					rule->debug_limit_init = global_debug_limit_init;
# endif
# ifdef WITH_THRESHOLDS
				if (rule->debug_threshold < 0)
					rule->debug_threshold = global_debug_threshold;
				if (rule->debug_threshold_init < 0)
					rule->debug_threshold_init = global_debug_threshold_init;
# endif
# ifdef CTL_CHECK_CREDS
				if (rule->ctl_rule_acl == NULL)
					rule->ctl_rule_acl = global_ctl_rule_acl;
# endif
				set_sync_exec_in_rule(rule);
			}
		}

# ifdef WITH_LIMITS
		STAILQ_FOREACH(limit, &rule->limits, link) {
			if (check_worktime_subset(rule_wt, limit->worktime) < 0) {
				conferrx("rule %s, limit %s: limit's worktime must be a subset of rule's worktime",
				    rule->rule_name, limit->limit_name);
				goto parsing_failed;
			}
			if (mimic_real_config) {
				if (rule->free_mask & RULE_FREE_LIMITS) {
					/* Limits were not copied from rulepat. */
					inherit_limit_from_global(limit);
					set_sync_exec_in_limit(limit);
				}
				if (limit->worktime == NULL)
					limit->worktime = rule->worktime;
				if (limit->db_list == NULL)
					limit->db_list = rule->db_list;
			}
		}
# endif /* WITH_LIMITS */

# ifdef WITH_THRESHOLDS
		STAILQ_FOREACH(threshold, &rule->thresholds, link) {
			if (check_worktime_subset(rule_wt, threshold->worktime) < 0) {
				conferrx("rule %s, threshold %s: threshold's worktime must be a subset of rule's worktime",
				    rule->rule_name, threshold->threshold_name);
				goto parsing_failed;
			}
			if (mimic_real_config) {
				if (rule->free_mask & RULE_FREE_THRESHOLDS) {
					/* Thresholds were not copied from rulepat. */
					if (inherit_threshold_from_global("rule",
					    rule->rule_name, threshold) < 0)
						goto parsing_failed;
					set_sync_exec_in_threshold(threshold);
				}
				if (threshold->worktime == NULL)
					threshold->worktime = rule->worktime;
				if (threshold->db_list == NULL)
					threshold->db_list = rule->db_list;
			}
		}
# endif /* WITH_THRESHOLDS */
	}

	if (ac_gather_rev_mzone != NULL) {
		if (mzone_is_empty(ac_gather_rev_mzone)
#ifdef WITH_AUTORULES
		    && nautorules == 0
#endif
		    ) {
			mzone_deinit(ac_gather_rev_mzone);
			ac_gather_rev_mzone = NULL;
		}
	}
#endif /* WITH_RULES */

	if (real_config || mimic_real_config) {
		for (debug_param = common_debug_param; debug_param->param_name != NULL; ++debug_param)
			if (*debug_param->param_value < 0)
				*debug_param->param_value = 0;
		if (ctl_enable < 0)
			ctl_enable = 0;
		if (ctl_socket_path == NULL)
			ctl_socket_path = ctl_socket_path_default;
		if (ctl_socket_perm == 0)
			ctl_socket_perm = CTL_SOCKET_PERM_DEF;
		if (ctl_timeout == 0)
			ctl_timeout = CTL_TIMEOUT_DEF;
	}

	if (real_config)
		logmsgx(IPA_LOG_INFO, "loaded %u autorule(s), %u static rule(s), %u limit(s), %u sublimit(s), %u threshold(s)",
		    nautorules, nstatrules, nstatlimits, nstatsublimits, nstatthresholds);

	mvlogmsgx = mvlogmsgx_wrapper;

	xvlogmsgx = vlogmsgx;

	return 0;

section_not_expected:
	logconferrx("this section is not expected here");

parsing_failed:
	conferrx("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 ac_mod *ac_mod, *ac_mod_next;
	struct db_mod *db_mod, *db_mod_next;

	for (ac_mod = SLIST_FIRST(&ac_mod_list); ac_mod != NULL; ac_mod = ac_mod_next) {
		ac_mod_next = SLIST_NEXT(ac_mod, link);
		if (dl_close(ac_mod->mod_handle) < 0) {
			logmsgx(IPA_LOG_ERR, "module %s: dl_close failed: %s",
			    ac_mod->mod_file, dl_error());
			return -1;
		}
		mem_free(ac_mod->mod_file, m_parser);
		mem_free(ac_mod, m_anon);
	}

	for (db_mod = SLIST_FIRST(&db_mod_list); db_mod != NULL; db_mod = db_mod_next) {
		db_mod_next = SLIST_NEXT(db_mod, link);
		if (dl_close(db_mod->mod_handle) < 0) {
			logmsgx(IPA_LOG_ERR, "module %s: dl_close failed: %s",
			    db_mod->mod_file, dl_error());
			return -1;
		}
		mem_free(db_mod->mod_file, m_parser);
		mem_free(db_mod, m_anon);
	}

	return 0;
}

/*
 * Free all resources allocated during work.
 */
int
free_all(void)
{
	u_int n;

	if (shell_path != shell_path_default)
		mem_free(shell_path, m_parser);

	if (shell_arg1 != shell_arg1_default)
		mem_free(shell_arg1, m_parser);

	free_rules();

	if (!TAILQ_EMPTY(&rules_list)) {
		logmsgx(IPA_LOG_ERR, "internal error: free_all: rules_list is not empty");
		return -1;
	}
	if (!TAILQ_EMPTY_HEAD_VALID(&rules_list)) {
		logmsgx(IPA_LOG_ERR, "internal error: free_all: empty rules_list list is in inconsistent state");
		return -1;
	}

	if (!rules_hash_is_empty()) {
		logmsgx(IPA_LOG_ERR, "internal error: free_all: rules_hash is not empty");
		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);

	free_rulepats();

#ifdef WITH_LIMITS
	if (limit_mzone != NULL) {
		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_SUBLIMITS
	if (sublimit_mzone != NULL) {
		if ( (n = mzone_nused(sublimit_mzone)) != 0) {
			logmsgx(IPA_LOG_ERR, "internal error: free_all: sublimit_mzone is not empty: %u", n);
			return -1;
		}
		mzone_deinit(sublimit_mzone);
	}
#endif

#ifdef WITH_THRESHOLDS
	if (threshold_mzone != NULL) {
		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

	mzone_deinit(tevent_mzone);

	free_cmd_list(&cmds_startup);
	free_cmd_list(&cmds_shutdown);
	if (cmd_mzone != NULL) {
		if ( (n = mzone_nused(cmd_mzone)) != 0) {
			logmsgx(IPA_LOG_ERR, "internal error: free_all: cmd_mzone is not empty: %u", n);
			return -1;
		}
		mzone_deinit(cmd_mzone);
	}

	free_worktimes();

#ifdef WITH_RULES
	if (ac_gather_rev_mzone != NULL) {
		if ( (n = mzone_nused(ac_gather_rev_mzone)) != 0) {
			logmsgx(IPA_LOG_ERR, "internal error: free_all: ac_gather_rev_mzone is not empty: %u", n);
			return -1;
		}
		mzone_deinit(ac_gather_rev_mzone);
	}
#endif

	free_ac_lists();
	free_db_lists();

	mem_free(tdiff_buf, m_anon);
	mem_free(parser_str_buf, m_parser);
	tdiff_buf = parser_str_buf = NULL;

#ifdef WITH_ANY_LIMITS
	mem_free(ipa_tm_buf, m_anon);
	ipa_tm_buf = NULL;
#endif

	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;
}

/*
 * Reconfiguring routine.
 */
int
reconfigure(void)
{
	int error = 0;

	logmsgx(IPA_LOG_INFO, "deiniting old configuration...");

	if (deinit_all() < 0) {
		logmsgx(IPA_LOG_ERR, "reconfigure: deinit_all failed");
		error = 1;
	}

	if (!TAILQ_EMPTY(&rules_active)) {
		logmsgx(IPA_LOG_ERR, "internal error: reconfigure: rules_active is not empty");
		return -1;
	}
	if (!TAILQ_EMPTY_HEAD_VALID(&rules_active)) {
		logmsgx(IPA_LOG_ERR, "internal error: reconfigure: empty rules_active list is in inconsistent state");
		return -1;
	}

	if (!TAILQ_EMPTY(&rules_inactive)) {
		logmsgx(IPA_LOG_ERR, "internal error: reconfigure: rules_inactive is not empty");
		return -1;
	}
	if (!TAILQ_EMPTY_HEAD_VALID(&rules_inactive)) {
		logmsgx(IPA_LOG_ERR, "internal error: reconfigure: empty rules_inactive list is in inconsistent state");
		return -1;
	}

#ifdef WITH_ANY_LIMITS
	if (!wpid_hash_is_empty()) {
		logmsgx(IPA_LOG_ERR, "internal error: reconfigure: wpid_hash is not empty");
		return -1;
	}
#endif

	if (free_all() < 0) {
		logmsgx(IPA_LOG_ERR, "reconfigure: free_all failed");
		error = 1;
	}

	if (error)
		return -1;

	logmsgx(IPA_LOG_INFO, "-----------------------------------------------------");
	logmsgx(IPA_LOG_INFO, "rereading configuration file...");

	if (configure(RECONFIG_PARSING) < 0) {
		logmsgx(IPA_LOG_ERR, "reconfigure: configure failed");
		return -1;
	}

	logmsgx(IPA_LOG_INFO, "using new configuration");

	return 0;
}

static void
print_time_u_int(u_int a)
{
	const uint64_t t = a;

	print_time(&t);
}

static void
show_cmd_list(const struct cmd_list *cmdl, int indent)
{
	const struct cmd *cmd;

	if (cmdl->sync_exec >= 0) {
		printf("%*s%*s%*ssync_exec = ", conf_indent, "", indent, "",
		    CONF_INDENT, "");
		print_boolean(cmdl->sync_exec);
		print_eol();
	}
	STAILQ_FOREACH(cmd, &cmdl->list, link) {
		printf("%*s%*s%*sexec ", conf_indent, "", indent, "",
		    CONF_INDENT, "");
		if (cmd->user != NULL)
			printf("%s ", cmd->user);
		print_string(cmd->str);
		print_eol();
	}
}

static void
show_commands(const struct cmd_list *cmd_list, const char *sect_name)
{
	if (!STAILQ_EMPTY(&cmd_list->list)) {
		printf("%*s%s {\n", conf_indent, "", sect_name);
		show_cmd_list(cmd_list, 0);
		printf("%*s}\n", conf_indent, "");
	}
}

static void
show_rule_rc(const struct cmds_rule *rc)
{
	int x;

	for (x = 0; x < 2; ++rc, ++x)
		if (rc->set != 0) {
			printf("%*s%s {\n", CONF_INDENT, "",
			    x == RC_STARTUP ? "startup" : "shutdown");
			if (!STAILQ_EMPTY(&rc->cmdl.list))
				show_cmd_list(&rc->cmdl, 0);
#ifdef WITH_LIMITS
			if (!STAILQ_EMPTY(&rc->cmdl_if_all_reached.list)) {
				printf("%*sif_all_reached {\n", 2 * CONF_INDENT, "");
				show_cmd_list(&rc->cmdl_if_all_reached, CONF_INDENT);
				printf("%*s}\n", 2 * CONF_INDENT, "");
			}
			if (!STAILQ_EMPTY(&rc->cmdl_if_all_not_reached.list)) {
				printf("%*sif_all_not_reached {\n", 2 * CONF_INDENT, "");
				show_cmd_list(&rc->cmdl_if_all_not_reached, CONF_INDENT);
				printf("%*s}\n", 2 * CONF_INDENT, "");
			}
			if (!STAILQ_EMPTY(&rc->cmdl_if_any_reached.list)) {
				printf("%*sif_any_reached {\n", 2 * CONF_INDENT, "");
				show_cmd_list(&rc->cmdl_if_any_reached, CONF_INDENT);
				printf("%*s}\n", 2 * CONF_INDENT, "");
			}
			if (!STAILQ_EMPTY(&rc->cmdl_if_any_not_reached.list)) {
				printf("%*sif_any_not_reached {\n", 2 * CONF_INDENT, "");
				show_cmd_list(&rc->cmdl_if_any_not_reached, CONF_INDENT);
				printf("%*s}\n", 2 * CONF_INDENT, "");
			}
#endif /* WITH_LIMITS */
			printf("%*s}\n", CONF_INDENT, "");
		}
}

static void
show_generic_worktime(const struct worktime *wt, const char *param_name)
{
	u_int	wday;
	const char *wday_char;
	const struct tint *tint;
	const struct tint_list *list;

	if (wt == NULL)
		return;

	wday_char = "SMTWHFA";

	printf("%*s%s =", conf_indent, "", param_name);
	for (wday = 0; wday < DAYS_IN_WEEK; ++wday_char, ++wday) {
		list = wt->tint_list[wday];
		if (!STAILQ_EMPTY(list)) {
			printf(" %c", *wday_char);
			tint = STAILQ_FIRST(list);
			if (tint->sec1 == 0 && tint->sec2 == SECONDS_IN_DAY)
				printf(" *");
			else
				for (; tint != NULL; tint = STAILQ_NEXT(tint, link)) {
					printf(" ");
					printf(tint_to_buf(tint));
				}
		}
	}
	print_eol();
}

/*
 * Output "worktime" parameter.
 */
static void
show_worktime(const struct worktime *wt)
{
	show_generic_worktime(wt, "worktime");
}

static void
mod_conf_show(u_int sect_id, u_int no)
{
	const struct ac_mod *ac_mod;
	const struct db_mod *db_mod;

	need_nl = 0;
	SLIST_FOREACH(ac_mod, &ac_mod_list, link)
		ac_mod->ipa_ac_mod->conf_show(sect_id, no);
	if (sect_id == IPA_CONF_SECT_ROOT)
		print_nl();
	SLIST_FOREACH(db_mod, &db_mod_list, link)
		db_mod->ipa_db_mod->conf_show(sect_id, no);
	if (sect_id == IPA_CONF_SECT_ROOT)
		print_nl();
}

static void
show_ac_list(const struct ac_list *list)
{
	const struct ac_elem *ac;

	if (list != NULL) {
		printf("%*sac_list =", CONF_INDENT, "");
		if (list != &ac_list_null)
			STAILQ_FOREACH(ac, list, link)
				printf(" %s", ac->ipa_ac_mod->ac_name);
		else
			printf(" null");
		print_eol();
	}
}

static void
show_db_list(const struct db_list *list)
{
	const struct db_elem *db;

	if (list != NULL) {
		printf("%*sdb_list =", conf_indent, "");
		if (list != &db_list_null)
			STAILQ_FOREACH(db, list, link)
				printf(" %s", db->ipa_db_mod->db_name);
		else
			printf(" null");
		print_eol();
	}
}

#if defined(WITH_RULES) || defined(WITH_ANY_LIMITS)
static void
print_info(const char *info)
{
	if (info != NULL) {
		printf("%*sinfo = ", conf_indent, "");
		print_string(info);
		print_eol();
	}
}
#endif /* WITH_RULES || WITH_ANY_LIMITS */

#ifdef WITH_RULES
static void
show_ac_gather_xxx(const char *pat, const char *param_name)
{
	if (pat != NULL) {
		printf("%*s%s = ", CONF_INDENT, "", param_name);
		print_string(pat);
		print_eol();
	}
}
#endif /* WITH_RULES */

#ifdef WITH_LIMITS
/*
 * Convert expired time to human readable string.
 */
static void
print_texp(const struct texp *texp)
{
	u_int t, a = texp->seconds;

	if (texp->upto != TEXP_UPTO_SIMPLE && texp->side == 0)
		printf(" +%c", texp->upto);
	t = a / SECONDS_IN_DAY;
	if (t != 0) {
		printf(" %dD", t);
		a -= t * SECONDS_IN_DAY;
	}
	if (a != 0 || (a == 0 && texp->upto == TEXP_UPTO_SIMPLE)) {
		printf(" ");
		print_time_u_int(a);
	}
	if (texp->upto != TEXP_UPTO_SIMPLE && texp->side == 1)
		printf(" +%c", texp->upto);
}

static void
show_limit_rc(const struct cmds_limit *rc)
{
	int x;

	for (x = 0; x < 2; ++rc, ++x)
		if (rc->set != 0) {
			printf("%*s%s {\n", conf_indent, "", x == RC_STARTUP ? "startup" : "shutdown" );
			if (!STAILQ_EMPTY(&rc->cmdl.list))
				show_cmd_list(&rc->cmdl, 0);
			if (!STAILQ_EMPTY(&rc->cmdl_if_reached.list)) {
				printf("%*s%*sif_reached {\n", conf_indent, "", CONF_INDENT, "");
				show_cmd_list(&rc->cmdl_if_reached, CONF_INDENT);
				printf("%*s%*s}\n", conf_indent, "", CONF_INDENT, "");
			}
			if (!STAILQ_EMPTY(&rc->cmdl_if_not_reached.list)) {
				printf("%*s%*sif_not_reached {\n", conf_indent, "", CONF_INDENT, "");
				show_cmd_list(&rc->cmdl_if_not_reached, CONF_INDENT);
				printf("%*s%*s}\n", conf_indent, "", CONF_INDENT, "");
			}
			printf("%*s}\n", conf_indent, "");
		}
}

static void
show_reach_sect(const struct cmd_list *cmdl)
{
	if (!STAILQ_EMPTY(&cmdl->list)) {
		printf("%*sreach {\n", conf_indent, "");
		show_cmd_list(cmdl, 0);
		printf("%*s}\n", conf_indent, "");
	}
}

static void
show_load_limit(int val)
{
	if (val >= 0) {
		printf("%*sload_limit = ", conf_indent, "");
		print_boolean(val);
		print_eol();
	}
}

static void
show_limits(const struct limits_list *list)
{
	const struct limit *limit;
#ifdef WITH_SUBLIMITS
	const struct sublimit *sublimit;
#endif

	STAILQ_FOREACH(limit, list, link) {
		conf_indent = 2 * CONF_INDENT;
		printf("%*slimit %s {\n", CONF_INDENT, "", limit->limit_name);
		show_db_list(limit->db_list);
		print_info(limit->limit_info);
		printf("%*slimit = ", 2 * CONF_INDENT, "");
		print_value(&limit->lim, limit->cnt_type);
		print_eol();
		show_load_limit(limit->load_limit);
		show_worktime(limit->worktime);
		if (limit->restart.restart.upto != TEXP_UPTO_NOTSET) {
			printf("%*srestart {\n%*srestart =", 2 * CONF_INDENT, "", 3 * CONF_INDENT, "");
			print_texp(&limit->restart.restart);
			print_eol();
			show_cmd_list(&limit->restart.cmdl, 0);
			printf("%*s}\n", 2 * CONF_INDENT, "");
		}
		show_reach_sect(&limit->reach);
		if (limit->expire.expire.upto != TEXP_UPTO_NOTSET) {
			printf("%*sexpire {\n%*sexpire =", 2 * CONF_INDENT, "", 3 * CONF_INDENT, "");
			print_texp(&limit->expire.expire);
			print_eol();
			show_cmd_list(&limit->expire.cmdl, 0);
			printf("%*s}\n", 2 * CONF_INDENT, "");
		}
		mod_conf_show(IPA_CONF_SECT_LIMIT, limit->limitno);
		show_limit_rc(limit->rc);
# ifdef WITH_SUBLIMITS
		conf_indent = 3 * CONF_INDENT;
		STAILQ_FOREACH(sublimit, &limit->sublimits, link) {
			printf("%*ssublimit ", 2 * CONF_INDENT, "");
			if (sublimit->lim_per_cent != 0)
				printf("%u%%", sublimit->lim_per_cent);
			else
				print_value(&sublimit->lim, sublimit->cnt_type);
			printf(" {\n");
			show_reach_sect(&sublimit->reach);
			show_limit_rc(sublimit->rc);
			printf("%*s}\n", 2 * CONF_INDENT, "");
		}
# endif /* WITH_SUBLIMITS */
		printf("%*s}\n", CONF_INDENT, "");
	}
}
#endif /* WITH_LIMITS */

#ifdef WITH_THRESHOLDS
static void
show_load_threshold(int val)
{
	if (val >= 0) {
		printf("%*sload_threshold = ", conf_indent, "");
		print_boolean(val);
		print_eol();
	}
}

static void
show_threshold_type(int val)
{
	if (val >= 0) {
		printf("%*sthreshold_type = %d", conf_indent, "", val);
		print_eol();
	}
}

static void
show_threshold_time_width(u_int val)
{
	if (val != 0) {
		printf("%*sthreshold_time_width = ", conf_indent, "");
		print_time_u_int(val);
		print_eol();
	}
}

static void
show_threshold_time_slice(const struct tevent *tevent)
{
	if (tevent != NULL) {
		printf("%*sthreshold_time_slice = ", conf_indent, "");
		print_time_u_int(tevent->event_step);
		print_eol();
	}
}

static void
show_threshold_balance(u_int a, u_int b, u_int c)
{
	if (a != 0) {
		printf("%*sthreshold_balance = ", conf_indent, "");
		if (a != UINT_MAX)
			printf("%u:", a);
		else
			printf("-:");
		if (b != UINT_MAX)
			printf("%u:", b);
		else
			printf("-:");
		if (c != UINT_MAX)
			printf("%u", c);
		else
			printf("-");
		print_eol();
	}
}

static void
show_thresholds(const struct thresholds_list *list)
{
	const struct threshold *threshold;

	conf_indent = 2 * CONF_INDENT;
	STAILQ_FOREACH(threshold, list, link) {
		printf("%*sthreshold %s {\n", CONF_INDENT, "", threshold->threshold_name);
		show_db_list(threshold->db_list);
		print_info(threshold->threshold_info);
		printf("%*sthreshold = ", 2 * CONF_INDENT, "");
		print_value(&threshold->thr, threshold->cnt_type);
		print_eol();
		if (threshold->thr_dev != 0) {
			printf("%*sthreshold_deviation = ", 2 * CONF_INDENT, "");
			if (threshold->thr_dev_per_cent != 0)
				printf("%u%%", threshold->thr_dev_per_cent);
			else
				print_value(&threshold->thr_dev, threshold->cnt_type);
			print_eol();
		}
		show_threshold_type(threshold->threshold_type);
		show_threshold_balance(threshold->below_lim,
		    threshold->equal_lim, threshold->above_lim);
		show_load_threshold(threshold->load_threshold);
		show_worktime(threshold->worktime);
		show_threshold_time_width(threshold->time_width);
		show_threshold_time_slice(threshold->time_slice);
		mod_conf_show(IPA_CONF_SECT_THRESHOLD, threshold->thresholdno);
		show_commands(&threshold->rc[RC_STARTUP], "startup");
		show_commands(&threshold->rc[RC_SHUTDOWN], "shutdown");
		show_commands(&threshold->below_threshold, "below_threshold");
		show_commands(&threshold->equal_threshold, "equal_threshold");
		show_commands(&threshold->above_threshold, "above_threshold");
		printf("%*s}\n", CONF_INDENT, "");
	}
}
#endif /* WITH_THRESHOLDS */

static void
show_ctl_socket_perm(void)
{
	if (ctl_socket_perm != 0) {
		printf("ctl_socket_perm = ");
		if (ctl_socket_perm & S_IWUSR)
			printf("u");
		if (ctl_socket_perm & S_IWGRP)
			printf("g");
#ifdef CTL_CHECK_CREDS
		if (ctl_socket_perm & S_IWOTH)
			printf("o");
#endif
		print_eol();
	}
}

#ifdef CTL_CHECK_CREDS
static void
show_ctl_xxx_acl(const struct ctl_acl_class *class, const char *param_name)
{
	if (class != NULL)
		printf("%*s%s = %s;\n", conf_indent, "", param_name, class->class_name);
}

static void
show_ctl_rule_acl(const struct ctl_acl_class *class)
{
	show_ctl_xxx_acl(class, "ctl_rule_acl");
}

static void
show_ctl_acl_classes(void)
{
	const struct ctl_acl_elem *elem;
	const struct ctl_acl_class *class;

	STAILQ_FOREACH(class, &ctl_acl_classes, link) {
		printf("ctl_acl_class = %s", class->class_name);
		STAILQ_FOREACH(elem, &class->list, link) {
			if (elem->allowed)
				printf(" ");
			else
				printf(" !");
			if (elem->user != NULL)
				printf("%s", elem->user);
			else
				printf("%%%s", elem->group);
		}
		print_eol();
		need_nl = 1;
	}
}
#endif /* CTL_CHECK_CREDS */

static void
show_update_time(const struct tevent *tevent)
{
	if (tevent != NULL) {
		printf("%*supdate_time = ", conf_indent, "");
		print_time_u_int(tevent->event_step);
		print_eol();
	}
}

static void
show_append_time(const struct tevent *tevent)
{
	if (tevent != NULL) {
		printf("%*sappend_time = ", conf_indent, "");
		print_time_u_int(tevent->event_step);
		print_eol();
	}
}

/*
 * Main function for outputting indented configuration file.
 */
void
show_config(void)
{
#ifdef WITH_RULES
	const struct rule *rule;
#endif
#ifdef WITH_AUTORULES
	const struct autorule *autorule;
#endif
	const struct rulepat *rulepat;
	const struct ac_mod *ac_mod;
	const struct db_mod *db_mod;
	const struct time_param *time_param;
	const struct debug_param *debug_param;

	printf("\
/*\n\
 * This output is not identical to the original content of the configuration\n\
 * file(s), this is just how ipa(8) and IPA modules see their configurations.\n\
 * Any \"include\" or \"include_files\" parameters are not printed and all\n\
 * 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(ac_mod, &ac_mod_list, link) {
		printf("ac_mod ");
		print_string(ac_mod->mod_file);
		print_eol();
		need_nl = 1;
	}

	SLIST_FOREACH(db_mod, &db_mod_list, link) {
		printf("db_mod ");
		print_string(db_mod->mod_file);
		print_eol();
		need_nl = 1;
	}
	print_nl();

	for (debug_param = common_debug_param; debug_param->param_name != NULL; ++debug_param)
		if (*debug_param->param_value >= 0) {
			printf("%s = %d;\n", debug_param->param_name, *debug_param->param_value);
			need_nl = 1;
		}
	print_nl();

	if (shell_path != NULL) {
		printf("shell_path = ");
		print_string(shell_path);
		print_eol();
		need_nl = 1;
	}
	if (shell_arg1 != NULL) {
		printf("shell_arg1 = ");
		print_string(shell_arg1);
		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();

	for (time_param = common_time_param; time_param->param_name != NULL; ++time_param)
		if (*time_param->param_value > 0) {
			printf("%s = ", time_param->param_name);
			print_time_u_int(*time_param->param_value);
			print_eol();
			need_nl = 1;
		}
	print_nl();

	if (only_abs_paths >= 0) {
		printf("only_abs_paths = ");
		print_boolean(only_abs_paths);
		print_eol();
		need_nl = 1;
	}
	print_nl();

	if (keep_rules_order >= 0) {
		printf("keep_rules_order = ");
		print_boolean(keep_rules_order);
		print_eol();
		need_nl = 1;
	}
	print_nl();

	if (ctl_enable >= 0) {
		printf("ctl_enable = ");
		print_boolean(ctl_enable);
		print_eol();
		need_nl = 1;
	}
	if (ctl_socket_path != NULL) {
		printf("ctl_socket_path = ");
		print_string(ctl_socket_path);
		print_eol();
		need_nl = 1;
	}
	if (ctl_timeout != 0) {
		printf("ctl_timeout = ");
		print_time_u_int(ctl_timeout);
		print_eol();
		need_nl = 1;
	}
	show_ctl_socket_perm();
#ifdef CTL_CHECK_CREDS
	show_ctl_acl_classes();
	show_ctl_xxx_acl(ctl_dump_acl, "ctl_dump_acl");
	show_ctl_xxx_acl(ctl_freeze_acl, "ctl_freeze_acl");
	show_ctl_xxx_acl(ctl_stat_acl, "ctl_stat_acl");
#endif
	print_nl();

	mod_conf_show(IPA_CONF_SECT_ROOT, 0);

	if (!STAILQ_EMPTY(&cmds_startup.list)) {
		show_commands(&cmds_startup, "startup");
		printf("\n");
	}

	if (!STAILQ_EMPTY(&cmds_shutdown.list)) {
		show_commands(&cmds_shutdown, "shutdown");
		printf("\n");
	}

	if (global_section_set) {
		conf_indent = CONF_INDENT;
		printf("global {\n");
		show_ac_list(global_ac_list);
		show_db_list(global_db_list);
		show_append_time(global_append_tevent);
		show_update_time(global_update_tevent);
		show_worktime(global_worktime);
#ifdef WITH_LIMITS
		show_load_limit(global_load_limit);
#endif
#ifdef WITH_THRESHOLDS
		show_threshold_type(global_threshold_type);
		show_threshold_balance(global_threshold_below_lim,
		    global_threshold_equal_lim, global_threshold_above_lim);
		show_load_threshold(global_load_threshold);
		show_threshold_time_width(global_threshold_time_width);
		show_threshold_time_slice(global_threshold_time_slice);
#endif
#ifdef CTL_CHECK_CREDS
		show_ctl_rule_acl(global_ctl_rule_acl);
#endif
		for (debug_param = global_debug_param; debug_param->param_name != NULL; ++debug_param)
			if (*debug_param->param_value >= 0) {
				printf("%*s%s = %d;\n", CONF_INDENT, "", debug_param->param_name, *debug_param->param_value);
				need_nl = 1;
			}
		mod_conf_show(IPA_CONF_SECT_GLOBAL, 0);
		printf("}\n\n");
	}

#ifdef WITH_AUTORULES
	STAILQ_FOREACH(autorule, &autorules_list, link) {
		conf_indent = CONF_INDENT;
		printf("autorule %s {\n", autorule->arule_name);
		show_ac_list(autorule->ac_list);
		show_db_list(autorule->db_list);
		show_append_time(autorule->append_tevent);
		show_update_time(autorule->update_tevent);
		show_worktime(autorule->worktime);
		show_generic_worktime(autorule->worktime_rule, "worktime_rule");
# ifdef CTL_CHECK_CREDS
		show_ctl_rule_acl(autorule->ctl_rule_acl);
# endif
		mod_conf_show(IPA_CONF_SECT_AUTORULE, autorule->aruleno);
		show_rule_rc(autorule->rc);
# ifdef WITH_LIMITS
		if (autorule->debug_limit >= 0)
			printf("%*sdebug_limit = %d;\n", CONF_INDENT, "",
			    autorule->debug_limit);
		if (autorule->debug_limit_init >= 0)
			printf("%*sdebug_limit_init = %d;\n", CONF_INDENT, "",
			    autorule->debug_limit_init);
		show_limits(&autorule->limits);
# endif
# ifdef WITH_THRESHOLDS
		if (autorule->debug_threshold >= 0)
			printf("%*sdebug_threshold = %d;\n", CONF_INDENT, "",
			    autorule->debug_threshold);
		if (autorule->debug_threshold_init >= 0)
			printf("%*sdebug_threshold_init = %d;\n", CONF_INDENT, "",
			    autorule->debug_threshold_init);
		show_thresholds(&autorule->thresholds);
# endif
		printf("}\n\n");
	}
#endif /* WITH_AUTORULES */

	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_ac_list(rulepat->ac_list);
		show_db_list(rulepat->db_list);
		show_append_time(rulepat->append_tevent);
		show_update_time(rulepat->update_tevent);
		show_worktime(rulepat->worktime);
#ifdef CTL_CHECK_CREDS
		show_ctl_rule_acl(rulepat->ctl_rule_acl);
#endif
		mod_conf_show(IPA_CONF_SECT_RULEPAT, rulepat->rulepatno);
		if (rulepat->debug_exec >= 0)
			printf("%*sdebug_exec = %d;\n", CONF_INDENT, "",
			    rulepat->debug_exec);
		show_rule_rc(rulepat->rc);
# ifdef WITH_LIMITS
		if (rulepat->debug_limit >= 0)
			printf("%*sdebug_limit = %d;\n", CONF_INDENT, "",
			    rulepat->debug_limit);
		if (rulepat->debug_limit_init >= 0)
			printf("%*sdebug_limit_init = %d;\n", CONF_INDENT, "",
			    rulepat->debug_limit_init);
		show_limits(&rulepat->limits);
# endif
# ifdef WITH_THRESHOLDS
		if (rulepat->debug_threshold >= 0)
			printf("%*sdebug_threshold = %d;\n", CONF_INDENT, "",
			    rulepat->debug_threshold);
		if (rulepat->debug_threshold_init >= 0)
			printf("%*sdebug_threshold_init = %d;\n", CONF_INDENT, "",
			    rulepat->debug_threshold_init);
		show_thresholds(&rulepat->thresholds);
# endif
		printf("}\n\n");
	}

#ifdef WITH_RULES
	TAILQ_FOREACH(rule, &rules_list, list) {
		conf_indent = CONF_INDENT;
		printf("rule %s {\n", rule->rule_name);
		show_ac_list(rule->ac_list);
		show_ac_gather_xxx(rule->ac_gather_add_pat, "ac_gather_add");
		show_ac_gather_xxx(rule->ac_gather_sub_pat, "ac_gather_sub");
		show_db_list(rule->db_list);
		print_info(rule->rule_info);
		show_append_time(rule->append_tevent);
		show_update_time(rule->update_tevent);
		show_worktime(rule->worktime);
# ifdef CTL_CHECK_CREDS
		show_ctl_rule_acl(rule->ctl_rule_acl);
# endif
		mod_conf_show(IPA_CONF_SECT_RULE, rule->ruleno);
		if (rule->debug_exec >= 0)
			printf("%*sdebug_exec = %d;\n", CONF_INDENT, "", rule->debug_exec);
		show_rule_rc(rule->rc);
# ifdef WITH_LIMITS
		if (rule->debug_limit >= 0)
			printf("%*sdebug_limit = %d;\n", CONF_INDENT, "", rule->debug_limit);
		if (rule->debug_limit_init >= 0)
			printf("%*sdebug_limit_init = %d;\n", CONF_INDENT, "", rule->debug_limit_init);
		show_limits(&rule->limits);
# endif
# ifdef WITH_THRESHOLDS
		if (rule->debug_threshold >= 0)
			printf("%*sdebug_threshold = %d;\n", CONF_INDENT, "", rule->debug_threshold);
		if (rule->debug_threshold_init >= 0)
			printf("%*sdebug_threshold_init = %d;\n", CONF_INDENT, "", rule->debug_threshold_init);
		show_thresholds(&rule->thresholds);
# endif
		printf("}\n\n");
	}
#endif /* WITH_RULES */
}


syntax highlighted by Code2HTML, v. 0.9.1