/*-
 * Copyright (c) 2005 Andrey Simonenko
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include "config.h"

#ifndef lint
static const char rcsid[] ATTR_UNUSED =
  "@(#)$Id: ipastat_rules.c,v 1.1.4.2 2007/05/11 16:29:59 simon Exp $";
#endif /* !lint */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/queue.h>

#include "ipa_mod.h"

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

#include "ipastat_log.h"

#include "ipastat_conf.h"
#include "ipastat_main.h"
#include "ipastat_rules.h"
#include "ipastat_st.h"

#ifndef RULES_HASH_BUCKETS
# define RULES_HASH_BUCKETS 128		/* Must be power of 2. */
#endif

int		dynamic_rules = -1;	/* dynamic_rules parameter. */

struct opt_rule	*cur_opt_rule = NULL;	/* Current optional rule. */

ipa_mzone	*rule_mzone;		/* Mzone for all struct rule{}. */

ipa_mzone	*rulepat_mzone;		/* Mzone for all struct rulepat{}. */

struct rules_list rules_list =
	STAILQ_HEAD_INITIALIZER(rules_list);	/* List of all rules. */

struct rulepats_list rulepats_list =
	STAILQ_HEAD_INITIALIZER(rulepats_list);	/* List of rulepats. */

u_int		nrules = 0;		/* Number of rules. */

struct opt_rules_list opt_rules_list = STAILQ_HEAD_INITIALIZER(opt_rules_list);

SLIST_HEAD(rules_hash, rule);

static struct rules_hash rules_hash[RULES_HASH_BUCKETS];

/*
 * Init one rule.
 */
static int
init_rule(const struct opt_rule *opt_rule)
{
	struct rule *rule;

	rule = opt_rule->rule;

	if (!rule->inited) {
		rule->inited = 1;

		/* Init in modules. */
		if (st_init_rule(rule) < 0) {
			logmsgx(IPA_LOG_ERR, "rule %s: init_rule: st_init_rule failed",
			    rule->rule_name);
			return -1;
		}
	}

	return 0;
}

/*
 * Init rules and limits or thresholds.
 */
int
init_rules(void)
{
	const struct opt_rule *opt_rule;

	STAILQ_FOREACH(opt_rule, &opt_rules_list, link) {
		if (init_rule(opt_rule) < 0) {
			logmsgx(IPA_LOG_ERR, "init_rules: init_rule failed");
			return -1;
		}
#ifdef WITH_LIMITS
		if (has_opt_limits)
			if (init_limits(opt_rule) < 0) {
				logmsgx(IPA_LOG_ERR, "init_rules: init_limits failed");
				return -1;
			}
#endif
#ifdef WITH_THRESHOLDS
		if (has_opt_thresholds)
			if (init_thresholds(opt_rule) < 0) {
				logmsgx(IPA_LOG_ERR, "init_rules: init_thresholds failed");
				return -1;
			}
#endif
	}

	return 0;
}

/*
 * Release memory used by one rule.
 */
static void
free_rule(struct rule *rule)
{
#ifdef WITH_LIMITS
	free_limits(&rule->limits);
#endif

#ifdef WITH_THRESHOLDS
	free_thresholds(&rule->thresholds);
#endif

	if (rule->free_mask & RULE_FREE_NAME)
		mem_free(rule->rule_name, m_anon);

	mem_free(rule->rule_info, m_result);

	mzone_free(rule_mzone, rule);
}

/*
 * Call free_rule() for each rule.
 */
void
free_rules(void)
{
	struct rule *rule, *rule_next;

	for (rule = STAILQ_FIRST(&rules_list); rule != NULL; rule = rule_next) {
		rule_next = STAILQ_NEXT(rule, list);
		free_rule(rule);
	}
	STAILQ_INIT(&rules_list);
}

/*
 * Deinit one rule.
 */
static int
deinit_rule(const struct opt_rule *opt_rule)
{
	int	error = 0;
	struct rule *rule;

	rule = opt_rule->rule;

	if (rule->inited) {
		rule->inited = 0;

		/* Deinit in modules. */
		if (st_deinit_rule(rule) < 0) {
			logmsgx(IPA_LOG_ERR, "rule %s: deinit_rule: st_deinit_rule failed",
			    rule->rule_name);
			error = -1;
		}
	}

	return error;
}

/*
 * Deinit previously inited rules, limits or thresholds.
 */
int
deinit_rules(void)
{
	int	error = 0;
	struct opt_rule *opt_rule;

	STAILQ_FOREACH(opt_rule, &opt_rules_list, link) {
		if (deinit_rule(opt_rule) < 0) {
			logmsgx(IPA_LOG_ERR, "deinit_rules: deinit_rule failed");
			error = -1;
		}
#ifdef WITH_LIMITS
		if (has_opt_limits)
			if (deinit_limits(opt_rule) < 0) {
				logmsgx(IPA_LOG_ERR, "deinit_rules: deinit_limits failed");
				error = -1;
			}
#endif
#ifdef WITH_THRESHOLDS
		if (has_opt_thresholds)
			if (deinit_thresholds(opt_rule) < 0) {
				logmsgx(IPA_LOG_ERR, "deinit_rules: deinit_thresholds failed");
				error = -1;
			}
#endif
	}

	return error;
}

/*
 * Return hash_value for the given rule_name;
 */
static u_int
get_rule_hash_value(const char *rule_name)
{
	u_int hash_value;

	for (hash_value = 0; *rule_name != '\0'; ++rule_name)
		hash_value += (u_char)*rule_name;

	return hash_value;
}

#define get_rules_bucket(x) ((x) & (RULES_HASH_BUCKETS - 1))

/*
 * Add rule to rules_hash.
 */
void
add_rule_to_hash(struct rule *rule)
{
	rule->hash_value = get_rule_hash_value(rule->rule_name);
	SLIST_INSERT_HEAD(&rules_hash[get_rules_bucket(rule->hash_value)], rule, hlink);
}

void
init_rules_hash(void)
{
	struct rules_hash *hash;

	for (hash = rules_hash; hash < rules_hash + RULES_HASH_BUCKETS; ++hash)
		SLIST_INIT(hash);
}

/*
 * Return pointer to rule with the given name.
 */
struct rule *
rule_by_name(const char *rule_name)
{
	u_int	hash_value;
	struct rule *rule;
	const struct rules_hash *hash;

	hash_value = get_rule_hash_value(rule_name);
	hash = &rules_hash[get_rules_bucket(hash_value)];

	SLIST_FOREACH(rule, hash, hlink)
		if (rule->hash_value == hash_value &&
		    strcmp(rule->rule_name, rule_name) == 0)
			return rule;

	return NULL;
}

struct rule *
alloc_rule(void)
{
	struct rule *rule;

	if ( (rule = mzone_alloc(rule_mzone)) == NULL) {
		xlogmsgx(IPA_LOG_ERR, "alloc_rule: mzone_alloc failed");
		return NULL;
	}
	rule->rule_info = NULL;
	rule->ruleno = nrules++;

	rule->st_list = NULL;

#ifdef WITH_LIMITS
	STAILQ_INIT(&rule->limits);
#endif

#ifdef WITH_THRESHOLDS
	STAILQ_INIT(&rule->thresholds);
#endif

	rule->inited = 0;

	STAILQ_INSERT_TAIL(&rules_list, rule, list);

	return rule;
}

/*
 * Add one optional rule given in the -q -r option.
 */
int
add_opt_rule(const char *rule_name)
{
	struct opt_rule *opt_rule;

	if ( (opt_rule = mem_malloc(sizeof *opt_rule, m_anon)) == NULL) {
		logmsgx(IPA_LOG_ERR, "add_opt_rule: mem_malloc failed");
		return -1;
	}

	opt_rule->rule_name = rule_name;

	opt_rule->opt_st = cur_opt_st;

	opt_rule->type = OPT_RULE_RULE;

	opt_rule->data = NULL;

#ifdef WITH_LIMITS
	STAILQ_INIT(&opt_rule->opt_limits);
#endif

#ifdef WITH_THRESHOLDS
	STAILQ_INIT(&opt_rule->opt_thresholds);
#endif

	STAILQ_INSERT_TAIL(&opt_rules_list, opt_rule, link);

	cur_opt_rule = opt_rule;

	return 0;
}

/*
 * Parse all rules, limits and thresholds names given in the
 * command line.
 */
int
parse_opt_rules(void)
{
	struct rule *rule;
	struct opt_rule *opt_rule;
	const struct rulepat *rulepat;

	STAILQ_FOREACH(opt_rule, &opt_rules_list, link) {
		if ( (rule = rule_by_name(opt_rule->rule_name)) == NULL) {
			/* This rule is not given in configuration file. */
			if (!dynamic_rules) {
				logmsgx(IPA_LOG_ERR, "parse_opt_rules: unknown rule %s",
				    opt_rule->rule_name);
				return -1;
			}
			if ( (rule = alloc_rule()) == NULL) {
				logmsgx(IPA_LOG_ERR, "parse_opt_rules: alloc_rule failed");
				return -1;
			}
			rule->rule_name = (char *)opt_rule->rule_name;
			rule->free_mask = 0;
			add_rule_to_hash(rule);
		}

		if (opt_rule->opt_st != NULL)
			rule->st_list = opt_rule->opt_st->st_list;

		STAILQ_FOREACH(rulepat, &rulepats_list, link)
			if (regexec(&rulepat->reg, rule->rule_name, 0, (regmatch_t *)NULL, 0) == 0) {
				/* Inherit some settings for rule{} from rulepat{}. */
				if (rule->st_list == NULL)
					rule->st_list = rulepat->st_list;
#ifdef WITH_LIMITS
					if (STAILQ_EMPTY(&rule->limits) && !STAILQ_EMPTY(&rulepat->limits))
						if (copy_limits(rule, &rulepat->limits) < 0) {
							logmsgx(IPA_LOG_ERR, "parse_opt_rules: rule %s: cannot copy all limits from rulepat %s",
							    rule->rule_name, parser_buf_to_string(rulepat->pat));
							return -1;
						}
#endif
#ifdef WITH_THRESHOLDS
					if (STAILQ_EMPTY(&rule->thresholds) && !STAILQ_EMPTY(&rulepat->thresholds))
						if (copy_thresholds(rule, &rulepat->thresholds) < 0) {
							logmsgx(IPA_LOG_ERR, "parse_opt_rule: rule %s: cannot copy all thresholds from rulepat %s",
							    rule->rule_name, parser_buf_to_string(rulepat->pat));
							return -1;
						}
#endif
				if (mod_conf_inherit(rulepat, rule) < 0) {
					logmsgx(IPA_LOG_ERR, "parse_opt_rules: mod_conf_inherit failed");
					return -1;
				}
				if (rulepat->check_next_rulepat == 0)
					break;
			}

		if (rulepat == NULL) {
			/* Inherit some settings for rule{} from global{}. */
			if (rule->st_list == NULL)
				rule->st_list = global_st_list;
		}

		opt_rule->rule = rule;

#ifdef WITH_LIMITS
		if (parse_opt_limits(opt_rule) < 0) {
			logmsgx(IPA_LOG_ERR, "parse_opt_rules: parse_opt_limits failed");
			return -1;
		}
#endif

#ifdef WITH_THRESHOLDS
		if (parse_opt_thresholds(opt_rule) < 0) {
			logmsgx(IPA_LOG_ERR, "parse_opt_rules: parse_opt_thresholds failed");
			return -1;
		}
#endif
	}

	return 0;
}

/*
 * Release memory used by all opt_rule structures.
 */
void
free_opt_rules(void)
{
	struct opt_rule *opt_rule, *opt_rule_next;

	for (opt_rule = STAILQ_FIRST(&opt_rules_list); opt_rule != NULL; opt_rule = opt_rule_next) {
		opt_rule_next = STAILQ_NEXT(opt_rule, link);
#ifdef WITH_LIMITS
		free_opt_limits(&opt_rule->opt_limits);
#endif
#ifdef WITH_THRESHOLDS
		free_opt_thresholds(&opt_rule->opt_thresholds);
#endif
		mem_free(opt_rule, m_anon);
	}
}

/*
 * Release memory used by all rulepats.
 */
void
free_rulepats(void)
{
	struct rulepat *rulepat;

	STAILQ_FOREACH(rulepat, &rulepats_list, link) {
		mem_free(rulepat->pat, m_parser);
		regfree(&rulepat->reg);
#ifdef WITH_LIMITS
		free_limits(&rulepat->limits);
#endif
#ifdef WITH_THRESHOLDS
		free_thresholds(&rulepat->thresholds);
#endif
	}

	mzone_deinit(rulepat_mzone);
}


syntax highlighted by Code2HTML, v. 0.9.1