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

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

#include "ipa_mod.h"

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

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

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

u_int		nac_mods;		/* Number of ac_mods. */

int		debug_ac_null;		/* debug_ac_null parameter. */

const struct ac_list *global_ac_list;	/* global { ac_list } */

/*
 * ac_sets is the list of all used "ac_list" parameters.
 * If some rules use the same "ac_list" parameter, then
 * they share the same struct ac_list{}.
 */
struct ac_sets ac_sets = SLIST_HEAD_INITIALIZER(ac_sets);

/*
 * List of all accounting modules.
 */
struct ac_mod_list ac_mod_list;

/*
 * Find an accounting module by configuration prefix.
 */
struct ac_mod *
ac_mod_by_prefix(const char *prefix)
{
	struct ac_mod *ac_mod;

	SLIST_FOREACH(ac_mod, &ac_mod_list, link)
		if (strcmp(ac_mod->ipa_ac_mod->conf_prefix, prefix) == 0)
			break;
	return ac_mod;
}

/*
 * Find an accounting module by a name.
 */
struct ac_mod *
ac_mod_by_name(const char *name)
{
	struct ac_mod *ac_mod;

	SLIST_FOREACH(ac_mod, &ac_mod_list, link)
		if (strcmp(ac_mod->ipa_ac_mod->ac_name, name) == 0)
			return ac_mod;

	return NULL;
}

/*
 * Increment ref_count for all accounting modules
 * in the given ac_list.
 */
void
ac_inc_ref_count(const struct ac_list *list)
{
	const struct ac_elem *ac;

	STAILQ_FOREACH(ac, list, link)
		(*ac->mod_ref_count)++;
}

/*
 * Decrement ref_count for all accounting modules
 * in the given ac_list.
 */
int
ac_dec_ref_count(const struct ac_list *list)
{
	const struct ac_elem *ac;

	STAILQ_FOREACH(ac, list, link) {
		if (*ac->mod_ref_count == 0) {
			logmsgx(IPA_LOG_INFO, "internal error: ac_dec_ref_count: mod_ref_count is zero for module %s",
			    ac->mod_file);
			return -1;
		}
		(*ac->mod_ref_count)--;
	}
	return 0;
}

/*
 * Release memory held by ac_set, including struct ac_set{}.
 */
void
free_ac_set(struct ac_set *set)
{
	struct ac_elem *ac, *ac_next;

	for (ac = STAILQ_FIRST(&set->list); ac != NULL; ac = ac_next) {
		ac_next = STAILQ_NEXT(ac, link);
		mem_free(ac, m_anon);
	}
	mem_free(set, m_anon);
}

/*
 * Release memory held by all ac_set.
 */
void
free_ac_lists(void)
{
	struct ac_set *set;

	while ( (set = SLIST_FIRST(&ac_sets)) != NULL) {
		SLIST_REMOVE_HEAD(&ac_sets, link);
		free_ac_set(set);
	}
}

/*
 * Pre-init all ac_mods.
 */
int
pre_init_ac_mods(void)
{
	const struct ac_mod *ac_mod;

	SLIST_FOREACH(ac_mod, &ac_mod_list, link)
		if (ac_mod->ipa_ac_mod->ac_pre_init() < 0) {
			logmsgx(IPA_LOG_ERR, "module %s: ac_pre_init failed", ac_mod->mod_file);
			return -1;
		}

	return 0;
}

/*
 * Init all ac_mods.
 */
int
init_ac_mods(void)
{
	const struct ac_mod *ac_mod;

	SLIST_FOREACH(ac_mod, &ac_mod_list, link)
		if (ac_mod->ipa_ac_mod->ac_init() < 0) {
			logmsgx(IPA_LOG_ERR, "module %s: ac_init failed", ac_mod->mod_file);
			return -1;
		}

	return 0;
}

/*
 * Deinit all ac_mods.
 */
int
deinit_ac_mods(void)
{
	int	error = 0;
	const struct ac_mod *ac_mod;

	SLIST_FOREACH(ac_mod, &ac_mod_list, link) {
		if (ac_mod->mod_ref_count != 0) {
			logmsgx(IPA_LOG_ERR, "internal error: deinit_ac_mods: mod_ref_count is %u for module %s!",
			    ac_mod->mod_ref_count, ac_mod->mod_file);
			error = -1;
		}
		if (ac_mod->ipa_ac_mod->ac_deinit() < 0) {
			logmsgx(IPA_LOG_ERR, "module %s: ac_deinit failed", ac_mod->mod_file);
			error = -1;
		}
	}

	return error;
}

#ifdef WITH_RULES
/*
 * Init one static rule in accounting systems.
 */
int
ac_init_statrule(const struct rule *rule)
{
	const struct ac_elem *ac;

	if (!STAILQ_EMPTY(rule->ac_list))
		STAILQ_FOREACH(ac, rule->ac_list, link)
			if (ac->ipa_ac_mod->ac_init_statrule != NULL) {
				if (ac->ipa_ac_mod->ac_init_statrule(rule->ruleno, rule->rule_name) < 0) {
					logmsgx(IPA_LOG_ERR, "module %s: ac_init_statrule(%s) failed",
					    ac->mod_file, rule->rule_name);
					return -1;
				}
			} else {
				logmsgx(IPA_LOG_ERR, "module %s: ac_init_statrule(%s): module does not support static rules",
				    ac->mod_file, rule->rule_name);
				return -1;
			}
	else if (rule->ac_gather_add_pat == NULL && rule->ac_gather_sub_pat == NULL) {
		if (debug_ac_null)
			logmsgx(IPA_LOG_INFO, "rule %s: this rule uses builtin \"null\" accounting system",
			    rule->rule_name);
	}
	return 0;
}
#endif /* WITH_RULES */

/*
 * Deinit one rule in accounting systems, which it uses.
 */
int
ac_deinit_rule(const struct rule *rule)
{
	int	error = 0;
	const struct ac_elem *ac;

	STAILQ_FOREACH(ac, rule->ac_list, link)
		if (ac->ipa_ac_mod->ac_deinit_rule(rule->ruleno) < 0) {
			logmsgx(IPA_LOG_ERR, "module %s: ac_deinit_rule(%s) failed",
			    ac->mod_file, rule->rule_name);
			error = -1;
		}

	return error;
}

/*
 * Call ac_get_stat for all used accounting modules, if ref_count of
 * a module is greater than zero.
 */
int
ac_get_stat(void)
{
	const struct ac_mod *ac_mod;

	SLIST_FOREACH(ac_mod, &ac_mod_list, link)
		if (ac_mod->mod_ref_count > 0 && ac_mod->ipa_ac_mod->ac_get_stat != NULL)
			if (ac_mod->ipa_ac_mod->ac_get_stat() < 0) {
				logmsgx(IPA_LOG_ERR, "module %s: ac_get_stat failed", ac_mod->mod_file);
				return -1;
			}

	return 0;
}

/*
 * Set a rule active/inactive in accounting systems it uses.
 */
int
ac_set_rule_active(const struct rule *rule, int active)
{
	const struct ac_elem *ac;

	STAILQ_FOREACH(ac, rule->ac_list, link) {
		if (ac->ipa_ac_mod->ac_set_rule_active != NULL)
			if (ac->ipa_ac_mod->ac_set_rule_active(rule->ruleno, active) < 0) {
				logmsgx(IPA_LOG_ERR, "module %s: ac_set_rule_active(%s, %d) failed",
				    ac->mod_file, rule->rule_name, active);
				return -1;
			}
		/* Increment/decrement reference counter. */
		*ac->mod_ref_count += active ? 1 : -1;
	}

	return 0;
}

#ifdef WITH_LIMITS
/*
 * Register limit event for one limit.
 */
int
ac_limit_event(const struct rule *rule, const struct limit *limit, u_int event)
{
	int	error = 0;
	const struct ac_elem *ac;

	STAILQ_FOREACH(ac, rule->ac_list, link)
		if (ac->ipa_ac_mod->ac_limit_event != NULL)
			if (ac->ipa_ac_mod->ac_limit_event(rule->ruleno, limit->limitno, event) < 0) {
				logmsgx(IPA_LOG_ERR, "module %s: ac_limit_event(%s, %s, LIMIT_EVENT_%s) failed",
				    ac->mod_file, rule->rule_name, limit->limit_name, limit_event_msg[event]);
				error = -1;
			}
	return error;
}

/*
 * Set a limit active/inactive in accounting systems it uses.
 */
int
ac_set_limit_active(const struct rule *rule, const struct limit *limit,
    int active)
{
	const struct ac_elem *ac;

	/* Limit shares the same ac_list with its rule. */
	STAILQ_FOREACH(ac, rule->ac_list, link)
		if (ac->ipa_ac_mod->ac_set_limit_active != NULL)
			if (ac->ipa_ac_mod->ac_set_limit_active(rule->ruleno,
			    limit->limitno, active) < 0) {
				logmsgx(IPA_LOG_ERR, "module %s: ac_set_limit_active(%s, %s, %d) failed",
				    ac->mod_file, rule->rule_name, limit->limit_name, active);
				return -1;
			}

	return 0;
}
#endif /* WITH_LIMITS */

#ifdef WITH_THRESHOLDS
/*
 * Register limit event for one threshold.
 */
int
ac_threshold_event(const struct rule *rule, const struct threshold *threshold, u_int event)
{
	int	error = 0;
	const struct ac_elem *ac;

	STAILQ_FOREACH(ac, rule->ac_list, link)
		if (ac->ipa_ac_mod->ac_threshold_event != NULL)
			if (ac->ipa_ac_mod->ac_threshold_event(rule->ruleno, threshold->thresholdno, event) < 0) {
				logmsgx(IPA_LOG_ERR, "module %s: ac_threshold_event(%s, %s, THRESHOLD_EVENT_%s) failed",
				    ac->mod_file, rule->rule_name, threshold->threshold_name, threshold_event_msg[event]);
				error = -1;
			}
	return error;
}

/*
 * Set a threshold active/inactive in accounting systems it uses.
 */
int
ac_set_threshold_active(const struct rule *rule,
    const struct threshold *threshold, int active)
{
	const struct ac_elem *ac;

	/* Threshold shares the same ac_list with its rule. */
	STAILQ_FOREACH(ac, rule->ac_list, link)
		if (ac->ipa_ac_mod->ac_set_threshold_active != NULL)
			if (ac->ipa_ac_mod->ac_set_threshold_active(rule->ruleno,
			    threshold->thresholdno, active) < 0) {
				logmsgx(IPA_LOG_ERR, "module %s: ac_set_threshold_active(%s, %s, %d) failed",
				    ac->mod_file, rule->rule_name, threshold->threshold_name, active);
				return -1;
			}

	return 0;
}
#endif /* WITH_THRESHOLDS */

#ifdef WITH_AUTORULES
/*
 * Init one autorule in accounting system, which it uses.
 */
int
ac_init_autorule(const struct autorule *autorule)
{
	const struct ac_elem *ac;

	/* Any autorule can use only one accounting system. */
	if (!STAILQ_EMPTY(autorule->ac_list)) {
		ac = STAILQ_FIRST(autorule->ac_list);
		if (ac->ipa_ac_mod->ac_init_autorule != NULL) {
			if (ac->ipa_ac_mod->ac_init_autorule(autorule->aruleno, autorule->arule_name) < 0) {
				logmsgx(IPA_LOG_ERR, "module %s: ac_init_autorule(%s) failed",
				    ac->mod_file, autorule->arule_name);
				return -1;
			}
		} else {
			logmsgx(IPA_LOG_ERR, "module %s: ac_init_autorule(%s): module does not support dynamic rules",
			    autorule->arule_name, ac->mod_file);
			return -1;
		}
	} else {
		if (debug_ac_null)
			logmsgx(IPA_LOG_INFO, "autorule %s: this autorule uses builtin \"null\" accounting system",
			    autorule->arule_name);
	}
	return 0;
}

/*
 * Deinit one autorule in accounting system, which it uses.
 */
int
ac_deinit_autorule(const struct autorule *autorule)
{
	const struct ac_elem *ac;

	STAILQ_FOREACH(ac, autorule->ac_list, link)
		if (ac->ipa_ac_mod->ac_deinit_autorule != NULL)
			if (ac->ipa_ac_mod->ac_deinit_autorule(autorule->aruleno) < 0) {
				logmsgx(IPA_LOG_ERR, "module %s: ac_deinit_autorule(%s) failed",
				    ac->mod_file, autorule->arule_name);
				return -1;
			}
	return 0;
}

/*
 * Init one dynamic rule in accounting systems.
 */
int
ac_init_dynrule(const struct autorule *autorule, const struct rule *rule)
{
	const struct ac_elem *ac;

	/* Any autorule can use only one accounting system. */
	if (!STAILQ_EMPTY(rule->ac_list)) {
		ac = STAILQ_FIRST(rule->ac_list);
		if (ac->ipa_ac_mod->ac_init_dynrule(autorule->aruleno, rule->ruleno, rule->rule_name) < 0) {
			logmsgx(IPA_LOG_ERR, "module %s: ac_dyninit_rule(%s, %s) failed",
			    ac->mod_file, autorule->arule_name, rule->rule_name);
			return -1;
		}
	}

	return 0;
}

int
ac_set_autorule_active(const struct autorule *autorule, int active)
{
	const struct ac_elem *ac;

	STAILQ_FOREACH(ac, autorule->ac_list, link) {
		if (ac->ipa_ac_mod->ac_set_autorule_active != NULL)
			if (ac->ipa_ac_mod->ac_set_autorule_active(autorule->aruleno, active) < 0) {
				logmsgx(IPA_LOG_ERR, "module %s: ac_set_autorule_active(%s, %d) failed",
				    ac->mod_file, autorule->arule_name, active);
				return -1;
			}
		/* Increment/decrement reference counter. */
		*ac->mod_ref_count += active ? 1 : -1;
	}

	return 0;
}
#endif /* WITH_AUTORULES */


syntax highlighted by Code2HTML, v. 0.9.1