/*-
* Copyright (c) 2000-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_db.c,v 1.1.4.2 2007/05/11 16:29:59 simon Exp $";
#endif /* !lint */
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.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 "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 ndb_mods; /* Number of db_mods. */
int debug_db_null; /* debug_db_null parameter. */
const struct db_list *global_db_list; /* global { db_list } */
/*
* db_sets is the list of all used "db_list" parameters.
* If some rules use the same "db_list" parameter, then
* they share the same struct db_list{}.
*/
struct db_sets db_sets = SLIST_HEAD_INITIALIZER(db_sets);
/*
* List of all database modules.
*/
struct db_mod_list db_mod_list;
/*
* Find a database module by configuration prefix.
*/
struct db_mod *
db_mod_by_prefix(const char *prefix)
{
struct db_mod *db_mod;
SLIST_FOREACH(db_mod, &db_mod_list, link)
if (strcmp(db_mod->ipa_db_mod->conf_prefix, prefix) == 0)
break;
return db_mod;
}
/*
* Find a database module by a name.
*/
struct db_mod *
db_mod_by_name(const char *name)
{
struct db_mod *db_mod;
SLIST_FOREACH(db_mod, &db_mod_list, link)
if (strcmp(db_mod->ipa_db_mod->db_name, name) == 0)
return db_mod;
return NULL;
}
/*
* Release memory held by db_set, including struct db_set{}.
*/
void
free_db_set(struct db_set *set)
{
struct db_elem *db, *db_next;
for (db = STAILQ_FIRST(&set->list); db != NULL; db = db_next) {
db_next = STAILQ_NEXT(db, link);
mem_free(db, m_anon);
}
mem_free(set, m_anon);
}
/*
* Release memory held by all db_set.
*/
void
free_db_lists(void)
{
struct db_set *set;
while ( (set = SLIST_FIRST(&db_sets)) != NULL) {
SLIST_REMOVE_HEAD(&db_sets, link);
free_db_set(set);
}
}
#ifdef WITH_LIMITS
# ifdef WITH_RULES
/*
* Init limits for one static rule in databases.
*/
static int
db_init_statlimits(const struct rule *rule)
{
struct limit *limit;
const struct db_elem *db;
STAILQ_FOREACH(limit, &rule->limits, link) {
if (!STAILQ_EMPTY(limit->db_list)) {
STAILQ_FOREACH(db, limit->db_list, link)
if (db->ipa_db_mod->db_init_statlimit != NULL) {
if (db->ipa_db_mod->db_init_statlimit(
rule->ruleno, rule->rule_name, rule->rule_info,
limit->limitno, limit->limit_name, limit->limit_info)
< 0) {
logmsgx(IPA_LOG_ERR, "module %s: db_init_statlimit(%s, %s) failed",
db->mod_file, rule->rule_name, limit->limit_name);
return -1;
}
} else {
logmsgx(IPA_LOG_ERR, "module %s: db_init_statlimit(%s, %s): module does not support limits from static rules",
db->mod_file, rule->rule_name, limit->limit_name);
return -1;
}
} else {
if (debug_db_null)
logmsgx(IPA_LOG_INFO, "rule %s, limit %s: this limit uses builtin \"null\" database",
rule->rule_name, limit->limit_name);
}
mem_free(limit->limit_info, m_parser);
limit->limit_info = NULL;
}
return 0;
}
# endif /* WITH_RULES */
# ifdef WITH_AUTORULES
/*
* Init limits for one dynamic rule in databases.
*/
static int
db_init_dynlimits(const struct autorule *autorule, const struct rule *rule)
{
struct limit *limit;
const struct db_elem *db;
STAILQ_FOREACH(limit, &rule->limits, link) {
if (!STAILQ_EMPTY(limit->db_list)) {
STAILQ_FOREACH(db, limit->db_list, link)
if (db->ipa_db_mod->db_init_dynlimit != NULL) {
if (db->ipa_db_mod->db_init_dynlimit(
autorule->aruleno,
rule->ruleno, rule->rule_name, rule->rule_info,
limit->limitno, limit->limit_name, limit->limit_info)
< 0) {
logmsgx(IPA_LOG_ERR, "module %s: db_init_dynlimit(%s, %s, %s) failed",
db->mod_file, autorule->arule_name, rule->rule_name, limit->limit_name);
return -1;
}
} else {
logmsgx(IPA_LOG_ERR, "module %s: db_init_dynlimit(%s, %s, %s): module does not support limits from dynamic rules",
db->mod_file, autorule->arule_name, rule->rule_name, limit->limit_name);
return -1;
}
} else {
if (debug_db_null)
logmsgx(IPA_LOG_INFO, "rule %s, limit %s: this limit uses builtin \"null\" database",
rule->rule_name, limit->limit_name);
}
}
return 0;
}
# endif /* WITH_AUTORULES */
/*
* Deinit limit in one rule in databases.
*/
int
db_deinit_limit(const struct rule *rule, const struct limit *limit)
{
int error = 0;
const struct db_elem *db;
STAILQ_FOREACH(db, limit->db_list, link)
if (db->ipa_db_mod->db_deinit_limit != NULL)
if (db->ipa_db_mod->db_deinit_limit(rule->ruleno, limit->limitno) < 0) {
logmsgx(IPA_LOG_ERR, "module %s: db_deinit_limit(%s, %s) failed",
db->mod_file, rule->rule_name, limit->limit_name);
error = -1;
}
return error;
}
/*
* Update statistics for one limit.
*/
int
db_update_limit(const struct rule *rule, const struct limit *limit)
{
int error = 0;
const struct db_elem *db;
STAILQ_FOREACH(db, limit->db_list, link)
if (db->ipa_db_mod->db_update_limit(rule->ruleno, limit->limitno,
&limit->cnt, &curdate) < 0) {
logmsgx(IPA_LOG_ERR, "module %s: db_update_limit(%s, %s) failed",
db->mod_file, rule->rule_name, limit->limit_name);
error = -1;
}
return error;
}
/*
* Register limit event for one limit.
*/
int
db_limit_event(const struct rule *rule, struct limit *limit,
u_int event, const ipa_tm *tm)
{
int error = 0;
const struct db_elem *db;
if (ctl_enable) {
limit->event_date_set |= 1 << event;
limit->event_date[event] = *tm;
}
STAILQ_FOREACH(db, limit->db_list, link)
if (db->ipa_db_mod->db_limit_event(rule->ruleno, limit->limitno,
event, tm, &curdate) < 0) {
logmsgx(IPA_LOG_ERR, "module %s: db_limit_event(%s, %s, %s) failed",
db->mod_file, rule->rule_name, limit->limit_name, limit_event_msg[event]);
error = -1;
}
return error;
}
/*
* Get a state of the limit: use first database which
* supports db_get_limit_state method and save its name in
* db_name. If any of databases used by the limit doesn't
* support db_get_limit_state method, then set db_name to NULL.
*/
int
db_get_limit_state(const struct rule *rule, const struct limit *limit,
struct ipa_limit_state *state, const char **db_name)
{
const struct db_elem *db;
STAILQ_FOREACH(db, limit->db_list, link)
if (db->ipa_db_mod->db_get_limit_state != NULL) {
if (db->ipa_db_mod->db_get_limit_state(rule->ruleno, limit->limitno, state) < 0) {
logmsgx(IPA_LOG_ERR, "module %s: db_get_limit_state(%s, %s) failed",
db->mod_file, rule->rule_name, limit->limit_name);
return -1;
}
*db_name = db->ipa_db_mod->db_name;
return 0;
}
*db_name = NULL;
return 0;
}
/*
* Set a state of the limit in databases.
*/
int
db_set_limit_state(const struct rule *rule, struct limit *limit,
struct ipa_limit_state *state, int new_state)
{
int error = 0;
const struct db_elem *db;
state->event_date_set |= IPA_LIMIT_EVENT_UPDATED_SET;
state->event_date[IPA_LIMIT_EVENT_UPDATED] = curdate;
if (ctl_enable) {
limit->event_date_set = state->event_date_set;
memcpy(&limit->event_date, &state->event_date, sizeof limit->event_date);
}
STAILQ_FOREACH(db, limit->db_list, link)
if (db->ipa_db_mod->db_set_limit_state(rule->ruleno, limit->limitno, state, new_state) < 0) {
logmsgx(IPA_LOG_ERR, "module %s: db_set_limit_state(%s, %s, %d) failed",
db->mod_file, rule->rule_name, limit->limit_name, new_state);
error = -1;
}
return error;
}
/*
* Set a limit active/inactive in databases it uses.
*/
int
db_set_limit_active(const struct rule *rule, const struct limit *limit,
int active)
{
const struct db_elem *db;
/* Limit uses own db_list. */
STAILQ_FOREACH(db, limit->db_list, link)
if (db->ipa_db_mod->db_set_limit_active != NULL)
if (db->ipa_db_mod->db_set_limit_active(rule->ruleno,
limit->limitno, active) < 0) {
logmsgx(IPA_LOG_ERR, "module %s: db_set_limit_active(%s, %s, %d) failed",
db->mod_file, rule->rule_name, limit->limit_name, active);
return -1;
}
return 0;
}
#endif /* WITH_LIMITS */
#ifdef WITH_THRESHOLDS
# ifdef WITH_RULES
/*
* Init thresholds for one static rule in databases.
*/
static int
db_init_statthresholds(const struct rule *rule)
{
struct threshold *threshold;
const struct db_elem *db;
STAILQ_FOREACH(threshold, &rule->thresholds, link) {
if (!STAILQ_EMPTY(threshold->db_list)) {
STAILQ_FOREACH(db, threshold->db_list, link)
if (db->ipa_db_mod->db_init_statthreshold != NULL) {
if (db->ipa_db_mod->db_init_statthreshold(
rule->ruleno, rule->rule_name, rule->rule_info,
threshold->thresholdno, threshold->threshold_name, threshold->threshold_info)
< 0) {
logmsgx(IPA_LOG_ERR, "module %s: db_init_statthreshold(%s, %s) failed",
db->mod_file, rule->rule_name, threshold->threshold_name);
return -1;
}
} else {
logmsgx(IPA_LOG_ERR, "module %s: db_init_statthreshold(%s, %s): module does not support thresholds from static rules",
db->mod_file, rule->rule_name, threshold->threshold_name);
return -1;
}
} else {
if (debug_db_null)
logmsgx(IPA_LOG_INFO, "rule %s, threshold %s: this threshold uses builtin \"null\" database",
rule->rule_name, threshold->threshold_name);
}
mem_free(threshold->threshold_info, m_parser);
threshold->threshold_info = NULL;
}
return 0;
}
# endif /* WITH_RULES */
# ifdef WITH_AUTORULES
/*
* Init thresholds for one dynamic rule in databases.
*/
static int
db_init_dynthresholds(const struct autorule *autorule, const struct rule *rule)
{
struct threshold *threshold;
const struct db_elem *db;
STAILQ_FOREACH(threshold, &rule->thresholds, link) {
if (!STAILQ_EMPTY(threshold->db_list)) {
STAILQ_FOREACH(db, threshold->db_list, link)
if (db->ipa_db_mod->db_init_dynthreshold != NULL) {
if (db->ipa_db_mod->db_init_dynthreshold(
autorule->aruleno,
rule->ruleno, rule->rule_name, rule->rule_info,
threshold->thresholdno, threshold->threshold_name, threshold->threshold_info)
< 0) {
logmsgx(IPA_LOG_ERR, "module %s: db_init_dynthreshold(%s, %s, %s) failed",
db->mod_file, autorule->arule_name, rule->rule_name, threshold->threshold_name);
return -1;
}
} else {
logmsgx(IPA_LOG_ERR, "module %s: db_init_dynthreshold(%s, %s, %s): module does not support thresholds from dynamic rules",
db->mod_file, autorule->arule_name, rule->rule_name, threshold->threshold_name);
return -1;
}
} else {
if (debug_db_null)
logmsgx(IPA_LOG_INFO, "rule %s, threshold %s: this threshold uses builtin \"null\" database",
rule->rule_name, threshold->threshold_name);
}
}
return 0;
}
# endif /* WITH_AUTORULES */
/*
* Deinit threshold in one rule in databases.
*/
int
db_deinit_threshold(const struct rule *rule, const struct threshold *threshold)
{
int error = 0;
const struct db_elem *db;
STAILQ_FOREACH(db, threshold->db_list, link)
if (db->ipa_db_mod->db_deinit_threshold != NULL)
if (db->ipa_db_mod->db_deinit_threshold(rule->ruleno, threshold->thresholdno) < 0) {
logmsgx(IPA_LOG_ERR, "module %s: db_deinit_threshold(%s, %s) failed",
db->mod_file, rule->rule_name, threshold->threshold_name);
error = -1;
}
return error;
}
/*
* Update statistics for one threshold in databases.
*/
int
db_update_threshold(const struct rule *rule, const struct threshold *threshold)
{
int error = 0;
const struct db_elem *db;
STAILQ_FOREACH(db, threshold->db_list, link)
if (db->ipa_db_mod->db_update_threshold(rule->ruleno, threshold->thresholdno,
&threshold->cnt, &threshold->tm_from, &threshold->tm_updated) < 0) {
logmsgx(IPA_LOG_ERR, "module %s: db_update_threshold(%s, %s) failed",
db->mod_file, rule->rule_name, threshold->threshold_name);
error = -1;
}
return error;
}
/*
* Get a state of the threshold: use first database which
* supports db_get_threshold_state method and save its name in
* db_name. If any of databases used by the threshold doesn't
* support db_get_threshold_state method, then set db_name to NULL.
*/
int
db_get_threshold_state(const struct rule *rule, const struct threshold *threshold,
struct ipa_threshold_state *state, const char **db_name)
{
const struct db_elem *db;
STAILQ_FOREACH(db, threshold->db_list, link)
if (db->ipa_db_mod->db_get_threshold_state != NULL) {
if (db->ipa_db_mod->db_get_threshold_state(rule->ruleno, threshold->thresholdno, state) < 0) {
logmsgx(IPA_LOG_ERR, "module %s: db_get_threshold_state(%s, %s) failed",
db->mod_file, rule->rule_name, threshold->threshold_name);
return -1;
}
*db_name = db->ipa_db_mod->db_name;
return 0;
}
*db_name = NULL;
return 0;
}
/*
* Set a state of the threshold in databases.
*/
int
db_set_threshold_state(const struct rule *rule, const struct threshold *threshold,
const struct ipa_threshold_state *state)
{
int error = 0;
const struct db_elem *db;
STAILQ_FOREACH(db, threshold->db_list, link)
if (db->ipa_db_mod->db_set_threshold_state(rule->ruleno, threshold->thresholdno, state) < 0) {
logmsgx(IPA_LOG_ERR, "module %s: db_set_threshold_state(%s, %s) failed",
db->mod_file, rule->rule_name, threshold->threshold_name);
error = -1;
}
return error;
}
/*
* Set a threshold active/inactive in databases it uses.
*/
int
db_set_threshold_active(const struct rule *rule,
const struct threshold *threshold, int active)
{
const struct db_elem *db;
/* Threshold uses own db_list. */
STAILQ_FOREACH(db, threshold->db_list, link)
if (db->ipa_db_mod->db_set_threshold_active != NULL)
if (db->ipa_db_mod->db_set_threshold_active(rule->ruleno,
threshold->thresholdno, active) < 0) {
logmsgx(IPA_LOG_ERR, "module %s: db_set_threshold_active(%s, %s, %d) failed",
db->mod_file, rule->rule_name, threshold->threshold_name, active);
return -1;
}
return 0;
}
#endif /* WITH_THRESHOLDS */
#ifdef WITH_RULES
/*
* Init one static rule in databases, also init its limits and thresholds.
*/
int
db_init_statrule(struct rule *rule)
{
const struct db_elem *db;
if (!STAILQ_EMPTY(rule->db_list)) {
STAILQ_FOREACH(db, rule->db_list, link)
if (db->ipa_db_mod->db_init_statrule != NULL) {
if (db->ipa_db_mod->db_init_statrule(rule->ruleno,
rule->rule_name, rule->rule_info) < 0) {
logmsgx(IPA_LOG_ERR, "module %s: db_init_statrule(%s) failed",
db->mod_file, rule->rule_name);
return -1;
}
} else {
logmsgx(IPA_LOG_ERR, "module %s: db_init_statrule(%s): module does not support static rules",
db->mod_file, rule->rule_name);
return -1;
}
} else {
if (debug_db_null)
logmsgx(IPA_LOG_INFO, "rule %s: this rule uses builtin \"null\" database",
rule->rule_name);
}
mem_free(rule->rule_info, m_parser);
rule->rule_info = NULL;
#ifdef WITH_LIMITS
if (db_init_statlimits(rule) < 0)
return -1;
#endif
#ifdef WITH_THRESHOLDS
if (db_init_statthresholds(rule) < 0)
return -1;
#endif
return 0;
}
#endif /* WITH_RULES */
/*
* Pre-init all db_mods.
*/
int
pre_init_db_mods(void)
{
const struct db_mod *db_mod;
SLIST_FOREACH(db_mod, &db_mod_list, link)
if (db_mod->ipa_db_mod->db_pre_init() < 0) {
logmsgx(IPA_LOG_ERR, "module %s: db_pre_init failed",
db_mod->mod_file);
return -1;
}
return 0;
}
/*
* Init all db_mods.
*/
int
init_db_mods(void)
{
const struct db_mod *db_mod;
SLIST_FOREACH(db_mod, &db_mod_list, link)
if (db_mod->ipa_db_mod->db_init() < 0) {
logmsgx(IPA_LOG_ERR, "module %s: db_init failed",
db_mod->mod_file);
return -1;
}
return 0;
}
/*
* Deinit all db_mods.
*/
int
deinit_db_mods(void)
{
int error = 0;
const struct db_mod *db_mod;
SLIST_FOREACH(db_mod, &db_mod_list, link)
if (db_mod->ipa_db_mod->db_deinit() < 0) {
logmsgx(IPA_LOG_ERR, "module %s: db_deinit failed", db_mod->mod_file);
error = -1;
}
return error;
}
/*
* Deinit one rule in databases.
*/
int
db_deinit_rule(const struct rule *rule)
{
int error = 0;
const struct db_elem *db;
STAILQ_FOREACH(db, rule->db_list, link) {
if (db->ipa_db_mod->db_deinit_rule(rule->ruleno) < 0) {
logmsgx(IPA_LOG_ERR, "module %s: db_deinit_rule(%s) failed",
db->mod_file, rule->rule_name);
error = -1;
}
}
return error;
}
/*
* Update statistics for one rule.
*/
int
db_update_rule(const struct rule *rule, const uint64_t *cnt)
{
int error = 0;
const struct db_elem *db;
STAILQ_FOREACH(db, rule->db_list, link)
if (db->ipa_db_mod->db_update_rule(rule->ruleno, cnt, &curdate) < 0) {
logmsgx(IPA_LOG_ERR, "module %s: db_update_rule(%s) failed",
db->mod_file, rule->rule_name);
error = -1;
}
return error;
}
/*
* Append an accounting record for one rule.
*/
int
db_append_rule(struct rule *rule, const uint64_t *cnt, int flush_cnt)
{
int error = 0;
const struct db_elem *db;
STAILQ_FOREACH(db, rule->db_list, link)
if (db->ipa_db_mod->db_append_rule(rule->ruleno, cnt, &curdate) < 0) {
logmsgx(IPA_LOG_ERR, "module %s: db_append_rule(%s) failed",
db->mod_file, rule->rule_name);
error = -1;
}
if (rule->append_tevent != NULL)
rule->append_sec = rule->append_tevent->event_sec;
if (flush_cnt)
rule->cnt = UINT64_C(0);
return error;
}
/*
* Set a rule active/inactive in databases it uses.
*/
int
db_set_rule_active(const struct rule *rule, int active)
{
const struct db_elem *db;
STAILQ_FOREACH(db, rule->db_list, link)
if (db->ipa_db_mod->db_set_rule_active != NULL)
if (db->ipa_db_mod->db_set_rule_active(rule->ruleno, active) < 0) {
logmsgx(IPA_LOG_ERR, "module %s: db_set_rule_active(%s, %d) failed",
db->mod_file, rule->rule_name, active);
return -1;
}
return 0;
}
#ifdef WITH_AUTORULES
/*
* Init one dynamic rule in databases, also init its limits and thresholds.
*/
int
db_init_dynrule(const struct autorule *autorule, struct rule *rule)
{
const struct db_elem *db;
if (!STAILQ_EMPTY(rule->db_list)) {
STAILQ_FOREACH(db, rule->db_list, link)
if (db->ipa_db_mod->db_init_dynrule != NULL) {
if (db->ipa_db_mod->db_init_dynrule(autorule->aruleno, rule->ruleno,
rule->rule_name, rule->rule_info) < 0) {
logmsgx(IPA_LOG_ERR, "module %s: db_init_dynrule(%s, %s) failed",
db->mod_file, autorule->arule_name, rule->rule_name);
return -1;
}
} else {
logmsgx(IPA_LOG_ERR, "module %s: db_init_dynrule(%s, %s): module does not support dynamic rules",
db->mod_file, autorule->arule_name, rule->rule_name);
return -1;
}
} else {
if (debug_db_null)
logmsgx(IPA_LOG_INFO, "rule %s: this rule uses builtin \"null\" database",
rule->rule_name);
}
mem_free(rule->rule_info, m_anon);
rule->rule_info = NULL;
#ifdef WITH_LIMITS
if (db_init_dynlimits(autorule, rule) < 0)
return -1;
#endif
#ifdef WITH_THRESHOLDS
if (db_init_dynthresholds(autorule, rule) < 0)
return -1;
#endif
return 0;
}
#endif /* WITH_AUTORULES */
syntax highlighted by Code2HTML, v. 0.9.1