/*- * 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 #include #include #include #include #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]); }