/*-
* 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_rules.c,v 1.2.2.3 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 "pathnames.h"
#include "ipa_ac.h"
#include "ipa_db.h"
#include "ipa_ctl.h"
#include "ipa_cmd.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"
#ifndef RULES_HASH_BUCKETS
# define RULES_HASH_BUCKETS 128 /* Must be power of 2. */
#endif
u_int nstatrules; /* Number of static rules. */
int keep_rules_order; /* keep_rules_order parameter. */
ipa_mzone *rule_mzone; /* Mzone for all struct rule{}. */
#ifdef WITH_RULES
int has_ac_gather; /* Non-zero if there is at least one "ac_gather_*" parameter. */
ipa_mzone *ac_gather_rev_mzone; /* Mzone for all struct ac_gather_rev{}. */
struct ac_gather_list ac_gather_list; /* List of all rules with "ac_gather_*" parameters. */
#endif
ipa_mzone *rulepat_mzone; /* Mzone for all struct rulepat{}. */
struct rulepats_list rulepats_list; /* List of all rulepats. */
u_int rules_inactive_check_sec; /* Time when to check inactive rules queue. */
struct rules_list rules_list =
TAILQ_HEAD_INITIALIZER(rules_list); /* List of all rules. */
struct rules_queue rules_active =
TAILQ_HEAD_INITIALIZER(rules_active); /* Active rules queue. */
struct rules_queue rules_inactive =
TAILQ_HEAD_INITIALIZER(rules_inactive); /* Inactive rules queue. */
LIST_HEAD(rules_hash, rule);
static struct rules_hash rules_hash[RULES_HASH_BUCKETS];
/*
* Add chunk to the rule's counter and to counters of its limits and
* thresholds. Check if there is statistics in rule's negative
* counter, check for counter overflowing. Add chunk to all rules
* that are listed in this rule's ac_gather_rev list.
*/
int
add_chunk_to_rule(struct rule *rule, const uint64_t *chunk_ptr)
{
uint64_t chunk;
chunk = *chunk_ptr;
if (rule->cnt_neg >= chunk)
rule->cnt_neg -= chunk;
else {
chunk -= rule->cnt_neg;
rule->cnt_neg = UINT64_C(0);
if (rule->cnt <= UINT64_MAX - chunk)
rule->cnt += chunk;
else {
if (db_update_rule(rule, &uint64_max) < 0) {
logmsgx(IPA_LOG_ERR, "rule %s: add_chunk_to_rule: db_update_rule failed",
rule->rule_name);
return -1;
}
rule->cnt = UINT64_MAX - rule->cnt;
rule->cnt = chunk - rule->cnt;
if (db_append_rule(rule, &rule->cnt, 0) < 0) {
logmsgx(IPA_LOG_ERR, "rule %s: add_chunk_to_rule: db_append_rule failed",
rule->rule_name);
return -1;
}
}
}
#ifdef WITH_LIMITS
if (!STAILQ_EMPTY(&rule->limits))
if (add_chunk_to_limits(rule, chunk_ptr) < 0) {
logmsgx(IPA_LOG_ERR, "rule %s: add_chunk_to_rule: add_chunk_to_limits failed",
rule->rule_name);
return -1;
}
#endif
#ifdef WITH_THRESHOLDS
if (!STAILQ_EMPTY(&rule->thresholds))
if (add_chunk_to_thresholds(rule, chunk_ptr) < 0) {
logmsgx(IPA_LOG_ERR, "rule %s: add_chunk_to_rule: add_chunk_to_thresholds failed",
rule->rule_name);
return -1;
}
#endif
#ifdef WITH_RULES
if (!SLIST_EMPTY(&rule->ac_gather_rev)) {
const struct ac_gather_rev *ac_gather_rev;
SLIST_FOREACH(ac_gather_rev, &rule->ac_gather_rev, link)
if (IS_ACTIVE(ac_gather_rev->rule)) {
if (ac_gather_rev->addition) {
if (add_chunk_to_rule(ac_gather_rev->rule, chunk_ptr) < 0) {
logmsgx(IPA_LOG_ERR, "rule %s: add_chunk_to_rule: add_chunk_to_rule for %s failed",
rule->rule_name, ac_gather_rev->rule->rule_name);
return -1;
}
} else {
if (sub_chunk_from_rule(ac_gather_rev->rule, chunk_ptr) < 0) {
logmsgx(IPA_LOG_ERR, "rule %s: add_chunk_to_rule: sub_chunk_to_rule for %s failed",
rule->rule_name, ac_gather_rev->rule->rule_name);
return -1;
}
}
}
}
#endif
return 0;
}
/*
* Subtract chunk from the rule's counter and from counters of its
* limits and thresholds. Check if there is statistics in negative
* rule's counter, check for counter overflowing. Subtract chunk from
* all rules that are listed in this rule's ac_gather_rev list.
*/
int
sub_chunk_from_rule(struct rule *rule, const uint64_t *chunk_ptr)
{
uint64_t chunk;
chunk = *chunk_ptr;
if (rule->cnt >= chunk)
rule->cnt -= chunk;
else {
chunk -= rule->cnt;
if (rule->cnt_neg > UINT64_MAX - chunk) {
logmsgx(IPA_LOG_ERR, "rule %s: sub_chunk_from_rule: negative statistics counter overflowed",
rule->rule_name);
logmsgx(IPA_LOG_ERR, "rule %s: sub_chunk_from_rule: this means that something is wrong in configuration or in modules",
rule->rule_name);
return -1;
}
rule->cnt_neg += chunk;
rule->cnt = UINT64_C(0);
}
#ifdef WITH_LIMITS
if (!STAILQ_EMPTY(&rule->limits))
if (sub_chunk_from_limits(rule, chunk_ptr) < 0) {
logmsgx(IPA_LOG_ERR, "rule %s: sub_chunk_from_rule: sub_chunk_from_limits failed",
rule->rule_name);
return -1;
}
#endif
#ifdef WITH_THRESHOLDS
if (!STAILQ_EMPTY(&rule->thresholds))
if (sub_chunk_from_thresholds(rule, chunk_ptr) < 0) {
logmsgx(IPA_LOG_ERR, "rule %s: sub_chunk_from_rule: sub_chunk_from_thresholds failed",
rule->rule_name);
return -1;
}
#endif
#ifdef WITH_RULES
if (!SLIST_EMPTY(&rule->ac_gather_rev)) {
const struct ac_gather_rev *ac_gather_rev;
SLIST_FOREACH(ac_gather_rev, &rule->ac_gather_rev, link)
if (IS_ACTIVE(ac_gather_rev->rule)) {
if (ac_gather_rev->addition) {
if (sub_chunk_from_rule(ac_gather_rev->rule, chunk_ptr) < 0) {
logmsgx(IPA_LOG_ERR, "rule %s: sub_chunk_to_rule: sub_chunk_to_rule for %s failed",
rule->rule_name, ac_gather_rev->rule->rule_name);
return -1;
}
} else {
if (add_chunk_to_rule(ac_gather_rev->rule, chunk_ptr) < 0) {
logmsgx(IPA_LOG_ERR, "rule %s: sub_chunk_to_rule: add_chunk_to_rule for %s failed",
rule->rule_name, ac_gather_rev->rule->rule_name);
return -1;
}
}
}
}
#endif
return 0;
}
/*
* Add a rule to rules_active queue.
*/
void
queue_active_rule(struct rule *rule1)
{
struct rule *rule2;
#ifdef WITH_AUTORULES
/* Dynamic rules are always linked to the head. */
if (RULE_IS_DYNAMIC(rule1)) {
TAILQ_INSERT_HEAD(&rules_active, rule1, queue);
return;
}
#endif
if (keep_rules_order)
/* Keep rules order or have ac_gather_*. */
TAILQ_FOREACH(rule2, &rules_active, queue)
if (rule1->orderno > rule2->orderno) {
TAILQ_INSERT_BEFORE(rule2, rule1, queue);
return;
}
/* Don't keep rules order or no dependency was found. */
TAILQ_INSERT_TAIL(&rules_active, rule1, queue);
}
#ifdef WITH_RULES
/*
* Add all static rules to active rules queue, this function is called
* after configuration/reconfiguration.
*/
void
init_rules_active(void)
{
u_int orderno;
struct rule *rule1, *rule2;
const struct ac_gather_rev *ac_gather_rev;
TAILQ_FOREACH(rule1, &rules_list, list) {
rule1->is_active = ACTIVE_FLAG;
if (!keep_rules_order && has_ac_gather) {
TAILQ_FOREACH(rule2, &rules_active, queue) {
/*
* The given rule must be below than rule, which
* has this rule in its ac_gather_rev.
*/
SLIST_FOREACH(ac_gather_rev, &rule2->ac_gather_rev, link)
if (ac_gather_rev->rule == rule1)
goto next_rule2;
/*
* Rules listed in ac_gather_rev of the given rule
* must below than this rule.
*/
SLIST_FOREACH(ac_gather_rev, &rule1->ac_gather_rev, link)
if (ac_gather_rev->rule == rule2) {
TAILQ_INSERT_BEFORE(rule2, rule1, queue);
goto next_rule1;
}
next_rule2:
;
}
}
/*
* Keep rules order or no dependency was found,
* add it to the end of the active rules queue.
* Side effect: by default static rules are initially
* placed in rules_active queue in the original order.
*/
TAILQ_INSERT_TAIL(&rules_active, rule1, queue);
next_rule1:
;
}
/* Let's combine these two flags for queue_active_rule(). */
keep_rules_order |= has_ac_gather;
if (keep_rules_order) {
orderno = 0;
TAILQ_FOREACH_REVERSE(rule1, &rules_active, rules_queue, queue)
rule1->orderno = orderno++;
}
}
#endif /* WITH_RULES */
/*
* Set rule active or inactive in modules it uses.
*/
int
mod_set_rule_active(struct rule *rule, int active)
{
if (rule->is_active == active) {
logmsgx(IPA_LOG_ERR, "internal error: mod_set_rule_active(%s, %d): rule is already %s",
rule->rule_name, active, active_msg[active]);
return -1;
}
if (debug_worktime)
logmsgx(IPA_LOG_INFO, "rule %s: set rule %s",
rule->rule_name, active_msg[active]);
rule->is_active = active;
if (ac_set_rule_active(rule, active) < 0) {
logmsgx(IPA_LOG_ERR, "rule %s: mod_set_rule_active: ac_set_rule_active(%d) failed",
rule->rule_name, active);
return -1;
}
if (db_set_rule_active(rule, active) < 0) {
logmsgx(IPA_LOG_ERR, "rule %s: mod_set_rule_active: db_set_rule_active(%d) failed",
rule->rule_name, active);
return -1;
}
return 0;
}
static void
show_inactive_rules(void)
{
const struct rule *rule;
logmsgx(IPA_LOG_INFO, "inactive rules after sorting:");
TAILQ_FOREACH(rule, &rules_inactive, queue)
logmsgx(IPA_LOG_INFO, " active %s, rule %s",
sec_to_buf(rule->worktime->active_sec), rule->rule_name);
}
/*
* Move a rule from the active rules queue to the inactive rules queue.
*/
int
set_rule_inactive(struct rule *rule1)
{
struct rule *rule2;
/* Inform modules that rule becomes inactive. */
if (mod_set_rule_active(rule1, INACTIVE_FLAG) < 0)
return -1;
/* Remove the rule from the active rules queue. */
TAILQ_REMOVE(&rules_active, rule1, queue);
/* Add the rule to the inactive rules queue, keep that queue sorted. */
if (rule1->worktime->active_sec != SECONDS_IN_WEEK) {
/* Will be active in current day. */
TAILQ_FOREACH(rule2, &rules_inactive, queue)
if (rule1->worktime->active_sec < rule2->worktime->active_sec) {
TAILQ_INSERT_BEFORE(rule2, rule1, queue);
goto out;
}
}
TAILQ_INSERT_TAIL(&rules_inactive, rule1, queue);
out:
if (debug_worktime)
show_inactive_rules();
rules_inactive_check_sec = TAILQ_FIRST(&rules_inactive)->worktime->active_sec;
return 0;
}
/*
* Move a rule from the inactive rules queue to the active rules queue.
*/
int
set_rule_active(struct rule *rule)
{
/* Inform modules that rule becomes active. */
if (mod_set_rule_active(rule, ACTIVE_FLAG) < 0)
return -1;
/* Get new stat for the rule. */
rule->newstat = 1;
/* Remove the rule from the inactive rules queue. */
TAILQ_REMOVE(&rules_inactive, rule, queue);
/* Add the rule to the active rules queue. */
queue_active_rule(rule);
/* Get new value of rules_inactive_check_sec. */
rules_inactive_check_sec = TAILQ_EMPTY(&rules_inactive) ?
SECONDS_IN_WEEK : TAILQ_FIRST(&rules_inactive)->worktime->active_sec;
/* Append new record for the rule. */
if (db_append_rule(rule, &uint64_zero, 1) < 0) {
logmsgx(IPA_LOG_ERR, "rule %s: set_rule_active: db_append_rule failed",
rule->rule_name);
return -1;
}
rule->check_sec = cursec;
return 0;
}
/*
* Sort inactive rules queue;
*/
void
sort_inactive_rules(void)
{
struct rule *rule1, *rule1_next, *rule2;
if (TAILQ_EMPTY(&rules_inactive))
rules_inactive_check_sec = SECONDS_IN_WEEK;
else {
rule1 = TAILQ_FIRST(&rules_inactive);
TAILQ_INIT(&rules_inactive);
for (; rule1 != NULL; rule1 = rule1_next) {
rule1_next = TAILQ_NEXT(rule1, queue);
if (rule1->worktime->active_sec != SECONDS_IN_WEEK) {
/* Will be active in current day. */
TAILQ_FOREACH(rule2, &rules_inactive, queue)
if (rule1->worktime->active_sec <= rule2->worktime->active_sec) {
TAILQ_INSERT_BEFORE(rule2, rule1, queue);
goto next;
}
}
TAILQ_INSERT_TAIL(&rules_inactive, rule1, queue);
next:
;
}
rules_inactive_check_sec = TAILQ_FIRST(&rules_inactive)->worktime->active_sec;
}
if (debug_worktime)
show_inactive_rules();
}
/*
* Check if time to make some rule active come. This function
* is called when rules_inactive_check_sec <= cursec.
*/
int
check_inactive_rules(void)
{
struct rule *rule, *rule_next;
for (rule = TAILQ_FIRST(&rules_inactive); rule != NULL; rule = rule_next)
if (rule->worktime->active_sec <= cursec) {
/* It's time to make a rule active. */
rule_next = TAILQ_NEXT(rule, queue);
if (set_rule_active(rule) < 0) {
logmsgx(IPA_LOG_ERR, "rule %s: check_inactive_rules: set_rule_active failed",
rule->rule_name);
return -1;
}
} else {
/* Set next time of invocation of this function. */
rules_inactive_check_sec = rule->worktime->active_sec;
return 0;
}
/* No more inactive rules. */
rules_inactive_check_sec = SECONDS_IN_WEEK;
return 0;
}
/*
* Log messages about lost negative statistics.
*/
static void
check_cnt_neg(const struct rule *rule, int only_for_limits)
{
#ifdef WITH_LIMITS
const struct limit *limit;
#endif
#ifdef WITH_THRESHOLDS
const struct threshold *threshold;
#endif
if (!only_for_limits && rule->cnt_neg > UINT64_C(0))
logmsgx(IPA_LOG_WARNING, "rule %s: statistics counter is -%"PRIu64", this statistics is lost",
rule->rule_name, rule->cnt_neg);
#ifdef WITH_LIMITS
STAILQ_FOREACH(limit, &rule->limits, link)
if (limit->cnt_neg > UINT64_C(0))
logmsgx(IPA_LOG_WARNING, "rule %s, limit %s: statistics counter is -%s, this statistics is lost",
rule->rule_name, limit->limit_name, cnt_to_buf(&limit->cnt_neg, limit->cnt_type));
#endif
#ifdef WITH_THRESHOLDS
STAILQ_FOREACH(threshold, &rule->thresholds, link)
if (threshold->cnt_neg > UINT64_C(0))
logmsgx(IPA_LOG_WARNING, "rule %s, threshold %s: statistics counter is -%s, this statistics is lost",
rule->rule_name, threshold->threshold_name, cnt_to_buf(&threshold->cnt_neg, threshold->cnt_type));
#endif
}
#ifdef WITH_RULES
/*
* Init one static rule.
*/
static int
init_stat_rule(struct rule *rule)
{
rule->newstat = 1;
if (rule->append_tevent == NULL)
rule->append_sec = SECONDS_IN_WEEK;
if (ac_init_statrule(rule) < 0) {
logmsgx(IPA_LOG_ERR, "rule %s: init_stat_rule: ac_init_statrule failed",
rule->rule_name);
return -1;
}
if (db_init_statrule(rule) < 0) {
logmsgx(IPA_LOG_ERR, "rule %s: init_stat_rule: db_init_statrule failed",
rule->rule_name);
return -1;
}
return 0;
}
#endif /* WITH_RULES */
/*
* Run startup or shutdown commands for a rule, its limits,
* sublimits and thresholds.
*/
int
run_rule_cmds(struct rule *rule, int x)
{
const char *sect;
struct cmds_rule *rcmds;
#ifdef WITH_LIMITS
int if_any_reached, if_all_reached;
struct limit *limit;
struct cmds_limit *lcmds;
#endif
#ifdef WITH_SUBLIMITS
struct sublimit *sublimit;
#endif
#ifdef WITH_THRESHOLDS
struct cmd_list *cmdl;
struct threshold *threshold;
#endif
sect = x == RC_STARTUP ? "startup" : "shutdown";
rcmds = &rule->rc[x];
if (!STAILQ_EMPTY(&rcmds->cmdl.list)) {
if (rule->debug_exec)
logmsgx(IPA_LOG_INFO, "rule %s: run %s commands (%s)",
rule->rule_name, sect, sync_exec_msg[rcmds->cmdl.sync_exec]);
if (exec_cmd_list(rule, (struct wpid *)NULL, &rcmds->cmdl,
"rule %s { %s {}}", rule->rule_name, sect) < 0)
return -1;
}
if (x == RC_STARTUP)
free_cmds_rule(rcmds);
#ifdef WITH_LIMITS
if_any_reached = 0;
if_all_reached = 1;
STAILQ_FOREACH(limit, &rule->limits, link)
if (IS_REACHED(limit))
if_any_reached = 1;
else
if_all_reached = 0;
if (if_any_reached) {
if (!STAILQ_EMPTY(&rcmds->cmdl_if_any_reached.list)) {
if (rule->debug_exec)
logmsgx(IPA_LOG_INFO, "rule %s: run %s if_any_reached commands (%s)",
rule->rule_name, sect, sync_exec_msg[rcmds->cmdl_if_any_reached.sync_exec]);
if (exec_cmd_list(rule, (struct wpid *)NULL, &rcmds->cmdl_if_any_reached,
"rule %s { %s { if_any_reached {}}}", rule->rule_name, sect) < 0)
return -1;
}
} else {
if (!STAILQ_EMPTY(&rcmds->cmdl_if_any_not_reached.list)) {
if (rule->debug_exec)
logmsgx(IPA_LOG_INFO, "rule %s: run %s if_any_not_reached commands (%s)",
rule->rule_name, sect, sync_exec_msg[rcmds->cmdl_if_any_not_reached.sync_exec]);
if (exec_cmd_list(rule, (struct wpid *)NULL, &rcmds->cmdl_if_any_not_reached,
"rule %s { %s { if_any_not_reached {}}}", rule->rule_name, sect) < 0)
return -1;
}
}
if (if_all_reached) {
if (!STAILQ_EMPTY(&rcmds->cmdl_if_all_reached.list)) {
if (rule->debug_exec)
logmsgx(IPA_LOG_INFO, "rule %s: run %s if_all_reached commands (%s)",
rule->rule_name, sect, sync_exec_msg[rcmds->cmdl_if_all_reached.sync_exec]);
if (exec_cmd_list(rule, (struct wpid *)NULL, &rcmds->cmdl_if_all_reached,
"rule %s { %s { if_all_reached {}}}", rule->rule_name, sect) < 0)
return -1;
}
} else {
if (!STAILQ_EMPTY(&rcmds->cmdl_if_all_not_reached.list)) {
if (rule->debug_exec)
logmsgx(IPA_LOG_INFO, "rule %s: run %s if_all_not_reached commands (%s)",
rule->rule_name, sect, sync_exec_msg[rcmds->cmdl_if_all_not_reached.sync_exec]);
if (exec_cmd_list(rule, (struct wpid *)NULL, &rcmds->cmdl_if_all_not_reached,
"rule %s { %s { if_all_not_reached {}}}", rule->rule_name, sect) < 0)
return -1;
}
}
STAILQ_FOREACH(limit, &rule->limits, link) {
lcmds = &limit->rc[x];
if (!STAILQ_EMPTY(&lcmds->cmdl.list)) {
if (rule->debug_exec)
logmsgx(IPA_LOG_INFO, "rule %s, limit %s: run %s commands (%s)",
rule->rule_name, limit->limit_name, sect, sync_exec_msg[lcmds->cmdl.sync_exec]);
if (exec_cmd_list(rule, (struct wpid *)NULL, &lcmds->cmdl,
"rule %s { limit %s { %s {}}}", rule->rule_name, limit->limit_name, sect) < 0)
return -1;
}
if (IS_REACHED(limit)) {
if (!STAILQ_EMPTY(&lcmds->cmdl_if_reached.list)) {
if (rule->debug_exec)
logmsgx(IPA_LOG_INFO, "rule %s, limit %s: run %s if_reached commands (%s)",
rule->rule_name, limit->limit_name, sect, sync_exec_msg[lcmds->cmdl_if_reached.sync_exec]);
if (exec_cmd_list(rule, (struct wpid *)NULL, &lcmds->cmdl_if_reached,
"rule %s { limit %s { %s { if_reached{}}}}", rule->rule_name, limit->limit_name, sect) < 0)
return -1;
}
if (ac_limit_event(rule, limit, x == RC_STARTUP ? IPA_LIMIT_EVENT_STARTUP_IF_REACHED : IPA_LIMIT_EVENT_SHUTDOWN_IF_REACHED) < 0) {
logmsgx(IPA_LOG_ERR, "rule %s, limit %s: run_rule_cmds: ac_limit_event failed",
rule->rule_name, limit->limit_name);
return -1;
}
} else {
if (!STAILQ_EMPTY(&lcmds->cmdl_if_not_reached.list)) {
if (rule->debug_exec)
logmsgx(IPA_LOG_INFO, "rule %s, limit %s (%s, if_not_reached): run commands (%s)",
rule->rule_name, limit->limit_name, sect, sync_exec_msg[lcmds->cmdl_if_not_reached.sync_exec]);
if (exec_cmd_list(rule, (struct wpid *)NULL, &lcmds->cmdl_if_not_reached,
"rule %s { limit %s { %s { if_not_reached{}}}}", rule->rule_name, limit->limit_name, sect) < 0)
return -1;
}
if (ac_limit_event(rule, limit, x == RC_STARTUP ? IPA_LIMIT_EVENT_STARTUP_IF_NOT_REACHED : IPA_LIMIT_EVENT_SHUTDOWN_IF_NOT_REACHED) < 0) {
logmsgx(IPA_LOG_ERR, "rule %s, limit %s: run_rule_cmds: ac_limit_event failed",
rule->rule_name, limit->limit_name);
return -1;
}
}
if (x == RC_STARTUP)
free_cmds_limit(lcmds);
# ifdef WITH_SUBLIMITS
STAILQ_FOREACH(sublimit, &limit->sublimits, link) {
lcmds = &sublimit->rc[x];
if (!STAILQ_EMPTY(&lcmds->cmdl.list)) {
if (rule->debug_exec)
logmsgx(IPA_LOG_INFO, "rule %s, limit %s, sublimit %s: run %s commands (%s)",
rule->rule_name, limit->limit_name, sublimit->sublimit_name, sect, sync_exec_msg[lcmds->cmdl.sync_exec]);
if (exec_cmd_list(rule, (struct wpid *)NULL, &lcmds->cmdl,
"rule %s { limit %s { sublimit %s { %s {}}}}", rule->rule_name, limit->limit_name, sublimit->sublimit_name, sect) < 0)
return -1;
}
if (IS_REACHED(sublimit)) {
if (!STAILQ_EMPTY(&lcmds->cmdl_if_reached.list)) {
if (rule->debug_exec)
logmsgx(IPA_LOG_INFO, "rule %s, limit %s, sublimit %s: run %s if_reached commands (%s)",
rule->rule_name, limit->limit_name, sublimit->sublimit_name, sect, sync_exec_msg[lcmds->cmdl_if_reached.sync_exec]);
if (exec_cmd_list(rule, (struct wpid *)NULL, &lcmds->cmdl_if_reached,
"rule %s { limit %s { sublimit %s { %s { if_reached{}}}}}", rule->rule_name, limit->limit_name, sublimit->sublimit_name, sect) < 0)
return -1;
}
} else {
if (!STAILQ_EMPTY(&lcmds->cmdl_if_not_reached.list)) {
if (rule->debug_exec)
logmsgx(IPA_LOG_INFO, "rule %s, limit %s, sublimit %s (%s, if_not_reached): run commands (%s)",
rule->rule_name, limit->limit_name, sublimit->sublimit_name, sect, sync_exec_msg[lcmds->cmdl_if_not_reached.sync_exec]);
if (exec_cmd_list(rule, (struct wpid *)NULL, &lcmds->cmdl_if_not_reached,
"rule %s { limit %s { sublimit %s { %s { if_not_reached{}}}}}", rule->rule_name, limit->limit_name, sublimit->sublimit_name, sect) < 0)
return -1;
}
}
if (x == RC_STARTUP)
free_cmds_limit(lcmds);
}
# endif /* WITH_SUBLIMITS */
}
#endif /* WITH_LIMITS */
#ifdef WITH_THRESHOLDS
STAILQ_FOREACH(threshold, &rule->thresholds, link) {
cmdl = &threshold->rc[x];
if (!STAILQ_EMPTY(&cmdl->list)) {
if (rule->debug_exec)
logmsgx(IPA_LOG_INFO, "rule %s, threshold %s: run %s commands (%s)",
rule->rule_name, threshold->threshold_name, sect, sync_exec_msg[cmdl->sync_exec]);
if (exec_cmd_list(rule, (struct wpid *)NULL, cmdl,
"rule %s { threshold %s { %s {}}}", rule->rule_name, threshold->threshold_name, sect) < 0)
return -1;
if (x == RC_STARTUP)
free_cmd_list(cmdl);
}
}
#endif /* WITH_THRESHOLDS */
return 0;
}
/*
* Run startup or shutdown commands for rules, its limits,
* sublimits and thresholds.
*/
int
run_rules_cmds(int x)
{
struct rule *rule;
TAILQ_FOREACH(rule, &rules_list, list)
if (run_rule_cmds(rule, x) < 0) {
logmsgx(IPA_LOG_ERR, "rule %s: run_rules_cmds: run_rule_cmds failed",
rule->rule_name);
return -1;
}
return 0;
}
#ifdef WITH_RULES
/*
* Build reverse list for "ac_gather_*" parameters: if rule1 matches
* rule2's "ac_gather_*" parameters, then rule2 is added to rule1's
* ac_gather_rev list.
*/
int
init_ac_gather_rev(struct rule *rule1)
{
struct rule *rule2;
struct ac_gather_rev *ac_gather_rev;
SLIST_FOREACH(rule2, &ac_gather_list, ac_gather_link)
if (rule1 != rule2) {
if (rule2->ac_gather_add_pat != NULL) {
if (regexec(&rule2->ac_gather_add_reg, rule1->rule_name, 0, (regmatch_t *)NULL, 0) == 0) {
if ( (ac_gather_rev = mzone_alloc(ac_gather_rev_mzone)) == NULL) {
xlogmsgx(IPA_LOG_ERR, "init_ac_gather_rev: mzone_alloc failed");
return -1;
}
ac_gather_rev->addition = 1;
ac_gather_rev->rule = rule2;
SLIST_INSERT_HEAD(&rule1->ac_gather_rev, ac_gather_rev, link);
}
}
if (rule2->ac_gather_sub_pat != NULL) {
if (regexec(&rule2->ac_gather_sub_reg, rule1->rule_name, 0, (regmatch_t *)NULL, 0) == 0) {
if ( (ac_gather_rev = mzone_alloc(ac_gather_rev_mzone)) == NULL) {
xlogmsgx(IPA_LOG_ERR, "init_ac_gather_rev: mzone_alloc failed");
return -1;
}
ac_gather_rev->addition = 0;
ac_gather_rev->rule = rule2;
SLIST_INSERT_HEAD(&rule1->ac_gather_rev, ac_gather_rev, link);
}
}
}
return 0;
}
/*
* Release memory used by ac_gather_rev list for one rule.
*/
static void
deinit_ac_gather_rev(struct rule *rule)
{
struct ac_gather_rev *ac_gather_rev, *ac_gather_rev_next;
ac_gather_rev = SLIST_FIRST(&rule->ac_gather_rev);
while (ac_gather_rev != NULL) {
ac_gather_rev_next = SLIST_NEXT(ac_gather_rev, link);
mzone_free(ac_gather_rev_mzone, ac_gather_rev);
ac_gather_rev = ac_gather_rev_next;
}
}
#endif /* WITH_RULES */
/*
* Init all rules and their limits and thresholds. This function is
* called for initial initialization of static rules (first_call == 0),
* or when there are problems with time.
*/
int
init_rules(int first_call)
{
struct rule *rule;
TAILQ_FOREACH(rule, &rules_list, list) {
#ifdef WITH_RULES
if (first_call) {
if (init_stat_rule(rule) < 0) {
logmsgx(IPA_LOG_ERR, "init_rules: init_stat_rule failed");
return -1;
}
} else
#endif
check_cnt_neg(rule, 1);
#ifdef WITH_LIMITS
if (init_limits(rule) < 0) {
logmsgx(IPA_LOG_ERR, "init_rules: init_limits failed");
return -1;
}
#endif
#ifdef WITH_THRESHOLDS
if (init_thresholds(rule) < 0) {
logmsgx(IPA_LOG_ERR, "init_rules: init_thresholds failed");
return -1;
}
#endif
}
return 0;
}
/*
* Unlink rule from rules list and release memory used by one rule.
*/
void
free_rule(struct rule *rule)
{
int dyn_flag = RULE_IS_DYNAMIC(rule);
mem_free(rule->rule_name, m_anon);
mem_free(rule->rule_info, dyn_flag ? m_anon : m_parser);
free_cmds_rule(&rule->rc[RC_STARTUP]);
free_cmds_rule(&rule->rc[RC_SHUTDOWN]);
#ifdef WITH_LIMITS
free_limits(rule->free_mask, &rule->limits, dyn_flag);
#endif
#ifdef WITH_THRESHOLDS
free_thresholds(rule->free_mask, &rule->thresholds, dyn_flag);
#endif
#ifdef WITH_RULES
if (rule->ac_gather_add_pat != NULL) {
mem_free(rule->ac_gather_add_pat, m_parser);
regfree(&rule->ac_gather_add_reg);
}
if (rule->ac_gather_sub_pat != NULL) {
mem_free(rule->ac_gather_sub_pat, m_parser);
regfree(&rule->ac_gather_sub_reg);
}
deinit_ac_gather_rev(rule);
#endif
/* Remove rule from rules list. */
TAILQ_REMOVE(&rules_list, rule, list);
/* Remove rule from rules hash. */
rem_rule_from_hash(rule);
mzone_free(rule_mzone, rule);
}
/*
* Call free_rule() for each rule.
*/
void
free_rules(void)
{
struct rule *rule;
while ( (rule = TAILQ_FIRST(&rules_list)) != NULL)
free_rule(rule);
}
/*
* Deinit one rule.
*/
int
deinit_rule(struct rule *rule)
{
int error = 0;
#ifdef WITH_LIMITS
struct limit *limit;
#endif
#ifdef WITH_SUBLIMITS
struct sublimit *sublimit;
#endif
#ifdef WITH_THRESHOLDS
struct threshold *threshold;
#endif
check_cnt_neg(rule, 0);
/* Remove rule from active or inactive queue. */
if (IS_ACTIVE(rule))
TAILQ_REMOVE(&rules_active, rule, queue);
else if (IS_INACTIVE(rule)) {
TAILQ_REMOVE(&rules_inactive, rule, queue);
rules_inactive_check_sec = !TAILQ_EMPTY(&rules_inactive) ?
TAILQ_FIRST(&rules_inactive)->worktime->active_sec : SECONDS_IN_WEEK;
}
/* Deinit in modules. */
#ifdef WITH_LIMITS
STAILQ_FOREACH(limit, &rule->limits, link) {
rem_wpid_from_hash(&limit->wpid);
# ifdef WITH_SUBLIMITS
STAILQ_FOREACH(sublimit, &limit->sublimits, link)
rem_wpid_from_hash(&sublimit->wpid);
# endif
if (db_deinit_limit(rule, limit) < 0) {
logmsgx(IPA_LOG_ERR, "rule %s: db_deinit_rule: db_deinit_limit failed",
rule->rule_name);
error = -1;
}
}
#endif /* WITH_LIMITS */
#ifdef WITH_THRESHOLDS
STAILQ_FOREACH(threshold, &rule->thresholds, link) {
rem_wpid_from_hash(&threshold->wpid);
if (db_deinit_threshold(rule, threshold) < 0) {
logmsgx(IPA_LOG_ERR, "rule %s: db_deinit_rule: db_deinit_threshold failed",
rule->rule_name);
error = -1;
}
}
#endif
if (ac_deinit_rule(rule) < 0) {
logmsgx(IPA_LOG_ERR, "rule %s: deinit_rule: ac_deinit_rule failed",
rule->rule_name);
error = -1;
}
if (db_deinit_rule(rule) < 0) {
logmsgx(IPA_LOG_ERR, "rule %s: deinit_rule: db_deinit_rule failed",
rule->rule_name);
error = -1;
}
/* Decrement ref_count of accounting systems if rule is not inactive. */
if (!IS_INACTIVE(rule))
if (ac_dec_ref_count(rule->ac_list) < 0) {
logmsgx(IPA_LOG_ERR, "rule %s: deinit_rule: ac_dec_ref_count failed",
rule->rule_name);
error = -1;
}
return error;
}
static int
deinit_stat_rule(struct rule *rule)
{
#ifdef WITH_AUTORULES
if (rules_ptr_marray != NULL)
/* Mark rule as unused. */
marray_free(rules_ptr_marray, rule->ruleno);
#endif
if (deinit_rule(rule) < 0) {
logmsgx(IPA_LOG_ERR, "deinit_stat_rule: deinit_rule failed");
return -1;
}
return 0;
}
/*
* Deinit all static and dynamic rules.
*/
int
deinit_rules(void)
{
int error = 0;
struct rule *rule;
TAILQ_FOREACH(rule, &rules_list, list) {
#ifdef WITH_AUTORULES
if (RULE_IS_DYNAMIC(rule)) {
if (deinit_dyn_rule(rule) < 0) {
logmsgx(IPA_LOG_ERR, "deinit_rules: deinit_dyn_rule failed");
error = -1;
}
} else
#endif
{
if (deinit_stat_rule(rule) < 0) {
logmsgx(IPA_LOG_ERR, "deinit_rules: deinit_rule failed");
error = -1;
}
}
}
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);
LIST_INSERT_HEAD(&rules_hash[get_rules_bucket(rule->hash_value)], rule, hlink);
}
/*
* Remove rule from rules_hash.
*/
void
rem_rule_from_hash(struct rule *rule)
{
LIST_REMOVE(rule, hlink);
}
/*
* Init rules_hash.
*/
void
init_rules_hash(void)
{
struct rules_hash *hash;
for (hash = rules_hash; hash < rules_hash + RULES_HASH_BUCKETS; ++hash)
LIST_INIT(hash);
}
int
rules_hash_is_empty(void)
{
const struct rules_hash *hash;
for (hash = rules_hash; hash < rules_hash + RULES_HASH_BUCKETS; ++hash)
if (!LIST_EMPTY(hash))
return 0;
return 1;
}
/*
* 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)];
LIST_FOREACH(rule, hash, hlink)
if (rule->hash_value == hash_value &&
strcmp(rule->rule_name, rule_name) == 0)
return rule;
return NULL;
}
/*
* Set check_sec for all active rules to 0, so they
* will be checked immediately.
*/
void
set_rules_for_check(void)
{
struct rule *rule;
TAILQ_FOREACH(rule, &rules_active, queue)
rule->check_sec = 0;
}
/*
* 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);
free_cmds_rule(&rulepat->rc[RC_STARTUP]);
free_cmds_rule(&rulepat->rc[RC_SHUTDOWN]);
#ifdef WITH_LIMITS
free_limits(RULE_FREE_LIMITS, &rulepat->limits, 0);
#endif
#ifdef WITH_THRESHOLDS
free_thresholds(RULE_FREE_THRESHOLDS, &rulepat->thresholds, 0);
#endif
}
mzone_deinit(rulepat_mzone);
}
void
init_cmds_in_rule(struct rule *rule)
{
init_cmds_rule(&rule->rc[RC_STARTUP]);
init_cmds_rule(&rule->rc[RC_SHUTDOWN]);
}
void
set_sync_exec_in_rule(struct rule *rule)
{
set_sync_exec_cmds_rule(&rule->rc[RC_STARTUP]);
set_sync_exec_cmds_rule(&rule->rc[RC_SHUTDOWN]);
}
void
init_cmds_in_rulepat(struct rulepat *rulepat)
{
init_cmds_rule(&rulepat->rc[RC_STARTUP]);
init_cmds_rule(&rulepat->rc[RC_SHUTDOWN]);
}
void
set_sync_exec_in_rulepat(struct rulepat *rulepat)
{
set_sync_exec_cmds_rule(&rulepat->rc[RC_STARTUP]);
set_sync_exec_cmds_rule(&rulepat->rc[RC_SHUTDOWN]);
}
syntax highlighted by Code2HTML, v. 0.9.1