/*- * 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_thresholds.c,v 1.1.4.3 2007/05/11 16:29:59 simon Exp $"; #endif /* !lint */ #include #include #include #include #include #include #include #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 nstatthresholds; /* Number of static thresholds. */ u_int ndynthresholds; /* Number of dynamic thresholds. */ #ifdef WITH_THRESHOLDS int global_debug_threshold; /* global { debug_threshold } */ int global_debug_threshold_init; /* global { debug_threshold_init } */ int global_threshold_type; /* global { threshold_type } */ int global_load_threshold; /* global { load_threshold } */ u_int global_threshold_time_width; /* global { threshold_time_width } */ const struct tevent *global_threshold_time_slice; /* global { threshold_time_slice } */ u_int global_threshold_below_lim; /* X:-:- global { threshold_balance } */ u_int global_threshold_equal_lim; /* -:X:- global { threshold_balance } */ u_int global_threshold_above_lim; /* -:-:X global { threshold_balance } */ ipa_mzone *threshold_mzone; /* Mzone for all struct threshold{}. */ const char *const threshold_event_msg[] = { "BELOW", /* 0 */ "EQUAL", /* 1 */ "ABOVE" /* 2 */ }; void set_thr_min_max(struct threshold *threshold) { uint64_t thr, dev; thr = threshold->thr; dev = threshold->thr_dev_per_cent != 0 ? uint64_per_cent(&thr, threshold->thr_dev_per_cent) : threshold->thr_dev; threshold->thr_max = (thr > UINT64_MAX - dev) ? UINT64_MAX : thr + dev; threshold->thr_min = thr < dev ? UINT64_C(0) : thr - dev; } /* * Remove statistics from the latest slice, if positive counter or * negative counter overflows, then return -1, since this means * incorrect configuration. */ static int threshold_flush_slice(const struct rule *rule, struct threshold *threshold) { u_int i; BITMAP_TYPE *bitword, bitmask; uint64_t chunk; i = threshold->cnt_slice_i; chunk = threshold->cnt_slice[i]; threshold->cnt_slice[i] = UINT64_C(0); bitword = BIT_WORD(threshold->cnt_slice_sign, i); if (*bitword != 0) { /* Have to check bit. */ bitmask = BIT_MASK(i); if (BIT_TEST(bitword, bitmask)) { BIT_CLEAR(bitword, bitmask); goto add_chunk; } } /* Subtract positive slice counter. */ if (rule->debug_threshold) logmsgx(IPA_LOG_INFO, "rule %s, threshold %s: threshold_flush_slice: remove chunk %s from slice #%u", rule->rule_name, threshold->threshold_name, cnt_to_buf(&chunk, threshold->cnt_type), i); if (threshold->cnt >= chunk) threshold->cnt -= chunk; else { chunk -= threshold->cnt; if (threshold->cnt_neg > UINT64_MAX - chunk) { logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: threshold_flush_slice: negative counter overflowed", rule->rule_name, threshold->threshold_name); logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: threshold_flush_slice: this means that something is wrong in configuration or in modules", rule->rule_name, threshold->threshold_name); return -1; } threshold->cnt_neg += chunk; threshold->cnt = UINT64_C(0); } return 0; add_chunk: /* Add absolute value of negative slice counter. */ if (rule->debug_threshold) logmsgx(IPA_LOG_INFO, "rule %s, threshold %s: threshold_flush_slice: remove chunk -%s from slice #%u", rule->rule_name, threshold->threshold_name, cnt_to_buf(&chunk, threshold->cnt_type), i); if (threshold->cnt_neg >= chunk) threshold->cnt_neg -= chunk; else { chunk -= threshold->cnt_neg; if (threshold->cnt > UINT64_MAX - chunk) { logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: threshold_flush_slice: positive counter overflowed", rule->rule_name, threshold->threshold_name); logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: threshold_flush_slice: this means that something is wrong in configuration or in modules", rule->rule_name, threshold->threshold_name); return -1; } threshold->cnt += chunk; threshold->cnt_neg = UINT64_C(0); } return 0; } /* * Set threshold active or inactive in modules it uses. */ int mod_set_threshold_active(const struct rule *rule, struct threshold *threshold, int active) { if (threshold->is_active == active) { logmsgx(IPA_LOG_ERR, "internal error: mod_set_threshold_active(%s, %s, %d): threshold is already %s", rule->rule_name, threshold->threshold_name, active, active_msg[active]); return -1; } if (debug_worktime) logmsgx(IPA_LOG_INFO, "rule %s, threshold %s: set threshold %s", rule->rule_name, threshold->threshold_name, active_msg[active]); threshold->is_active = active; if (ac_set_threshold_active(rule, threshold, active) < 0) { logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: mod_set_threshold_active: ac_set_threshold_active(%d) failed", rule->rule_name, threshold->threshold_name, active); return -1; } if (db_set_threshold_active(rule, threshold, active) < 0) { logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: mod_set_threshold_active: db_set_threshold_active(%d) failed", rule->rule_name, threshold->threshold_name, active); return -1; } return 0; } static int set_threshold_active(const struct rule *rule, struct threshold *threshold) { u_int i, n; if (threshold->threshold_type & THRESHOLD_JUMP_OVER_INACTIVE) { i = ipa_tm_diff(&threshold->tm_updated, &threshold->tm_from); threshold->tm_from = curdate; ipa_tm_subtime(&threshold->tm_from, i); if (rule->debug_threshold) logmsgx(IPA_LOG_INFO, "rule %s, threshold %s: set_threshold_active: jumping over inactive time interval", rule->rule_name, threshold->threshold_name); } else { n = ipa_tm_diff(&curdate, &threshold->tm_updated) / threshold->time_slice->event_step; if (n >= threshold->cnt_slice_n) n = threshold->cnt_slice_n; if (rule->debug_threshold) logmsgx(IPA_LOG_INFO, "rule %s, threshold %s: set_threshold_active: flushing %u slice%s, starting from slice #%u", rule->rule_name, threshold->threshold_name, n, n == 1 ? "" : "s", threshold->cnt_slice_i); for (i = 0; i < n; ++i) { threshold->cnt_slice_i++; if (threshold->cnt_slice_i == threshold->cnt_slice_n) threshold->cnt_slice_i = 0; if (threshold_flush_slice(rule, threshold) < 0) { logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: set_threshold_active: threshold_flush_slice failed", rule->rule_name, threshold->threshold_name); return -1; } } } /* Set new time when to check this threshold again. */ threshold->check_sec = threshold->time_slice->event_sec; return mod_set_threshold_active(rule, threshold, ACTIVE_FLAG); } /* * Add chunk to one threshold, if positive counter or slice counter * overflows, then return -1, since this means incorrect configuration. */ int add_chunk_to_threshold(const struct rule *rule, struct threshold *threshold, const uint64_t *chunk_ptr) { u_int i; BITMAP_TYPE *bitword, bitmask; uint64_t chunk; chunk = *chunk_ptr; /* Add chunk to counter. */ if (threshold->cnt_neg >= chunk) threshold->cnt_neg -= chunk; else { chunk -= threshold->cnt_neg; if (threshold->cnt > UINT64_MAX - chunk) { logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: add_chunk_to_threshold: positive counter overflowed", rule->rule_name, threshold->threshold_name); logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: add_chunk_to_threshold: this means that something is wrong in configuration or in modules", rule->rule_name, threshold->threshold_name); return -1; } threshold->cnt += chunk; threshold->cnt_neg = UINT64_C(0); /* Restore chunk. */ chunk = *chunk_ptr; } /* Add chunk to slice counter. */ i = threshold->cnt_slice_i; if (rule->debug_threshold) logmsgx(IPA_LOG_INFO, "rule %s, threshold %s: add_chunk_to_threshold: add chunk %s to slice #%u", rule->rule_name, threshold->threshold_name, cnt_to_buf(chunk_ptr, threshold->cnt_type), i); /* * Usually statistics is positive, so let's make some * optimization for usual situation. */ bitword = BIT_WORD(threshold->cnt_slice_sign, i); if (*bitword != 0) { /* Have to check bit. */ bitmask = BIT_MASK(i); if (BIT_TEST(bitword, bitmask)) { /* Slice has negative statistics. */ if (threshold->cnt_slice[i] > chunk) threshold->cnt_slice[i] -= chunk; else { threshold->cnt_slice[i] = chunk - threshold->cnt_slice[i]; BIT_CLEAR(bitword, bitmask); } return 0; } } /* Slice has positive statistics. */ if (threshold->cnt_slice[i] > UINT64_MAX - chunk) { logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: add_chunk_to_threshold: positive slice counter #%u overflowed", rule->rule_name, threshold->threshold_name, i); logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: add_chunk_to_threshold: this means that something is wrong in configuration or in modules", rule->rule_name, threshold->threshold_name); return -1; } threshold->cnt_slice[i] += chunk; return 0; } /* * Add chunk to every rule's threshold. */ int add_chunk_to_thresholds(const struct rule *rule, const uint64_t *chunk_ptr) { struct threshold *threshold; STAILQ_FOREACH(threshold, &rule->thresholds, link) if (IS_ACTIVE(threshold)) if (add_chunk_to_threshold(rule, threshold, chunk_ptr) < 0) { logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: add_chunk_to_thresholds: add_chunk_to_threshold failed", rule->rule_name, threshold->threshold_name); return -1; } return 0; } /* * Subtract chunk from one threshold. */ int sub_chunk_from_threshold(const struct rule *rule, struct threshold *threshold, const uint64_t *chunk_ptr) { u_int i; BITMAP_TYPE *bitword, bitmask; uint64_t chunk; chunk = *chunk_ptr; /* Subtract chunk from counter. */ if (threshold->cnt >= chunk) threshold->cnt -= chunk; else { chunk -= threshold->cnt; if (threshold->cnt_neg > UINT64_MAX - chunk) { logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: sub_chunk_from_threshold: negative counter overflowed", rule->rule_name, threshold->threshold_name); logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: sub_chunk_from_threshold: this means that something is wrong in configuration or in modules", rule->rule_name, threshold->threshold_name); return -1; } threshold->cnt_neg += chunk; threshold->cnt = UINT64_C(0); /* Restore chunk. */ chunk = *chunk_ptr; } /* Subtract chunk from slice counter. */ i = threshold->cnt_slice_i; if (rule->debug_threshold) logmsgx(IPA_LOG_INFO, "rule %s, threshold %s: sub_chunk_from_threshold: subtract chunk %s from slice #%u", rule->rule_name, threshold->threshold_name, cnt_to_buf(chunk_ptr, threshold->cnt_type), i); bitword = BIT_WORD(threshold->cnt_slice_sign, i); bitmask = BIT_MASK(i); if (BIT_TEST(bitword, bitmask)) { /* Slice has negative statistics. */ if (threshold->cnt_slice[i] > UINT64_MAX - chunk) { logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: sub_chunk_from_threshold: negative slice counter %u overflowed", rule->rule_name, threshold->threshold_name, i); logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: sub_chunk_from_threshold: this means that something is wrong in configuration or in modules", rule->rule_name, threshold->threshold_name); return -1; } threshold->cnt_slice[i] += chunk; } else { /* Slice has positive statistics. */ if (threshold->cnt_slice[i] >= chunk) threshold->cnt_slice[i] -= chunk; else { threshold->cnt_slice[i] = chunk - threshold->cnt_slice[i]; BIT_SET(bitword, bitmask); } } return 0; } /* * Subtract chunk from every rule's threshold. */ int sub_chunk_from_thresholds(const struct rule *rule, const uint64_t *chunk_ptr) { struct threshold *threshold; STAILQ_FOREACH(threshold, &rule->thresholds, link) if (IS_ACTIVE(threshold)) if (sub_chunk_from_threshold(rule, threshold, chunk_ptr) < 0) { logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: sub_chunk_from_thresholds: sub_chunk_from_threshold failed", rule->rule_name, threshold->threshold_name); return -1; } return 0; } /* * This routine implementes "below_threshold", "equal_threshold", * "above_threshold" and part of "worktime" in "threshold" section. */ int check_thresholds_events(const struct rule *rule, u_int *check_secp) { u_int i, check_sec; ipa_tm tm_from; struct wpid *wpid; struct threshold *threshold; const struct worktime *wt; check_sec = SECONDS_IN_WEEK; /* Hint. */ STAILQ_FOREACH(threshold, &rule->thresholds, link) { wt = threshold->worktime; if (IS_INACTIVE(threshold)) { /* Threshold is inactive. */ if (wt->active_sec <= cursec) { /* It's time to make threshold active. */ if (set_threshold_active(rule, threshold) < 0) { logmsgx(IPA_LOG_ERR, "check_thresholds_events: set_threshold_active failed"); return -1; } } else { if (check_sec > wt->active_sec) check_sec = wt->active_sec; continue; /* and don't check any time events for threshold. */ } } /* Here threshold is active. */ if (threshold->check_sec <= cursec) { /* It's time to check threshold. */ if (threshold->cnt > threshold->thr_max) { /* [ ] x */ if (rule->debug_threshold) logmsgx(IPA_LOG_INFO, "rule %s, threshold %s: above threshold, cnt %s, rest %u invocations", rule->rule_name, threshold->threshold_name, cnt_to_buf(&threshold->cnt, threshold->cnt_type), threshold->above_cnt); if (threshold->above_cnt > 0) { /* Adjust number of invocations. */ threshold->above_cnt--; threshold->equal_cnt = threshold->equal_lim; threshold->below_cnt = threshold->below_lim; /* Register event in modules. */ if (ac_threshold_event(rule, threshold, IPA_THRESHOLD_EVENT_ABOVE) < 0) { logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: check_thresholds_events: ac_threshold_event failed", rule->rule_name, threshold->threshold_name); return -1; } /* Run commands. */ if (!STAILQ_EMPTY(&threshold->above_threshold.list)) { if (rule->debug_exec) logmsgx(IPA_LOG_INFO, "rule %s, threshold %s: above threshold: run commands (%s)", rule->rule_name, threshold->threshold_name, sync_exec_msg[threshold->above_threshold.sync_exec]); wpid = &threshold->wpid; if (wpid->pid != 0) logmsgx(IPA_LOG_WARNING, "rule %s, threshold %s (above_threshold): previously run process with PID %ld has not exited yet", rule->rule_name, threshold->threshold_name, (long)wpid->pid); if (exec_cmd_list(rule, wpid, &threshold->above_threshold, "rule %s, threshold %s: above_threshold", rule->rule_name, threshold->threshold_name) < 0) { logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: check_thresholds_events: exec_cmd_list failed", rule->rule_name, threshold->threshold_name); return -1; } } } } else if (threshold->cnt < threshold->thr_min) { /* x [ ] */ if (rule->debug_threshold) { if (threshold->cnt_neg == UINT64_C(0)) logmsgx(IPA_LOG_INFO, "rule %s, threshold %s: below threshold, cnt %s, rest %u invocations", rule->rule_name, threshold->threshold_name, cnt_to_buf(&threshold->cnt, threshold->cnt_type), threshold->below_cnt); else logmsgx(IPA_LOG_INFO, "rule %s, threshold %s: below threshold, cnt -%s, rest %u invocations", rule->rule_name, threshold->threshold_name, cnt_to_buf(&threshold->cnt_neg, threshold->cnt_type), threshold->below_cnt); } if (threshold->below_cnt > 0) { /* Adjust number of invocations. */ threshold->below_cnt--; threshold->equal_cnt = threshold->equal_lim; threshold->above_cnt = threshold->above_lim; /* Register event in modules. */ if (ac_threshold_event(rule, threshold, IPA_THRESHOLD_EVENT_BELOW) < 0) { logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: check_thresholds_events: ac_threshold_event failed", rule->rule_name, threshold->threshold_name); return -1; } /* Run commands. */ if (!STAILQ_EMPTY(&threshold->below_threshold.list)) { if (rule->debug_exec) logmsgx(IPA_LOG_INFO, "rule %s, threshold %s: below threshold: run commands (%s)", rule->rule_name, threshold->threshold_name, sync_exec_msg[threshold->below_threshold.sync_exec]); wpid = &threshold->wpid; if (wpid->pid != 0) logmsgx(IPA_LOG_WARNING, "rule %s, threshold %s (below_threshold): previously run process with PID %ld has not exited yet", rule->rule_name, threshold->threshold_name, (long)wpid->pid); if (exec_cmd_list(rule, wpid, &threshold->below_threshold, "rule %s, threshold %s: below_threshold", rule->rule_name, threshold->threshold_name) < 0) { logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: check_thresholds_events: exec_cmd_list failed", rule->rule_name, threshold->threshold_name); return -1; } } } } else { /* [ x ] */ if (rule->debug_threshold) logmsgx(IPA_LOG_INFO, "rule %s, threshold %s: equal threshold, cnt %s, rest %u invocations", rule->rule_name, threshold->threshold_name, cnt_to_buf(&threshold->cnt, threshold->cnt_type), threshold->equal_cnt); if (threshold->equal_cnt) { /* Adjust number of invocations. */ threshold->equal_cnt--; threshold->below_cnt = threshold->below_lim; threshold->above_cnt = threshold->above_lim; /* Register event in modules. */ if (ac_threshold_event(rule, threshold, IPA_THRESHOLD_EVENT_EQUAL) < 0) { logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: check_thresholds_events: ac_threshold_event failed", rule->rule_name, threshold->threshold_name); return -1; } /* Run commands. */ if (!STAILQ_EMPTY(&threshold->equal_threshold.list)) { if (rule->debug_exec) logmsgx(IPA_LOG_INFO, "rule %s, threshold %s: equal threshold: run commands (%s)", rule->rule_name, threshold->threshold_name, sync_exec_msg[threshold->equal_threshold.sync_exec]); wpid = &threshold->wpid; if (wpid->pid != 0) logmsgx(IPA_LOG_WARNING, "rule %s, threshold %s (equal_threshold): previously run process with PID %ld has not exited yet", rule->rule_name, threshold->threshold_name, (long)wpid->pid); if (exec_cmd_list(rule, wpid, &threshold->equal_threshold, "rule %s, threshold %s: equal_threshold", rule->rule_name, threshold->threshold_name) < 0) { logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: check_thresholds_events: exec_cmd_list failed", rule->rule_name, threshold->threshold_name); return -1; } } } } /* Calculate next slice number, it is also the latest slice. */ i = threshold->cnt_slice_i + 1; if (i == threshold->cnt_slice_n) i = 0; threshold->cnt_slice_i = i; /* Remove statistics from the latest slice. */ if (threshold_flush_slice(rule, threshold) < 0) { logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: check_thresholds_events: threshold_flush_slice failed", rule->rule_name, threshold->threshold_name); return -1; } /* Set new time when to check this threshold again. */ threshold->check_sec = threshold->time_slice->event_sec; /* Update threshold in the database. */ tm_from = curdate; /* Threshold can be checked at 24:00:00. */ if (newday_flag) fix_240000(&tm_from); ipa_tm_subtime(&tm_from, threshold->time_width); if (threshold->update_tm_from) threshold->tm_from = tm_from; else if (cmp_ipa_tm(&tm_from, &threshold->tm_from) > 0) { threshold->update_tm_from = 1; threshold->tm_from = tm_from; } threshold->tm_updated = curdate; if (db_update_threshold(rule, threshold) < 0) { logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: check_thresholds_events: db_update_threshold failed", rule->rule_name, threshold->threshold_name); return -1; } } else if (need_check_flag) { if (db_update_threshold(rule, threshold) < 0) { logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: check_thresholds_events: db_update_threshold failed", rule->rule_name, threshold->threshold_name); return -1; } } if (IS_INACTIVE(wt)) { /* Threshold became inactive. */ if (!newday_flag) { if (set_threshold_inactive(rule, threshold) < 0) { logmsgx(IPA_LOG_ERR, "check_thresholds_events: set_threshold_inactive failed"); return -1; } if (check_sec > wt->active_sec) check_sec = wt->active_sec; } } else { /* Threshold is still active. */ if (check_sec > wt->inactive_sec) check_sec = wt->inactive_sec; if (check_sec > threshold->check_sec) check_sec = threshold->check_sec; } } *check_secp = check_sec; return 0; } /* * Init one threshold. */ static int init_threshold(const struct rule *rule, struct threshold *threshold) { int error; u_int i, n, old_slice_n, old_time_width; size_t size; uint64_t chunk, cnt; const char *db_name; ipa_tm tm_from; struct ipa_threshold_state oldstate, newstate; /* * If init_threshold() is called second, third... time, * then threshold can be inactive. */ if (IS_INACTIVE(threshold)) if (set_threshold_active(rule, threshold) < 0) { logmsgx(IPA_LOG_ERR, "init_threshold: set_threshold_active failed"); return -1; } threshold->cnt_neg = UINT64_C(0); threshold->update_tm_from = 0; /* Get number of statistics slices. */ n = threshold->cnt_slice_n = threshold->time_width / threshold->time_slice->event_step; /* Init statistics slices. */ size = n * sizeof *threshold->cnt_slice; if (threshold->cnt_slice == NULL) if ( (threshold->cnt_slice = mem_malloc(size, m_anon)) == NULL) { logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: init_threshold: mem_malloc for statistics slices failed", rule->rule_name, threshold->threshold_name); return -1; } memset(threshold->cnt_slice, 0, size); /* Init statistics slices signs. */ size = BITMAP_SIZE(n); if (threshold->cnt_slice_sign == NULL) if ( (threshold->cnt_slice_sign = mem_malloc(size, m_anon)) == NULL) { logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: init_threshold: mem_malloc for statistics slices signs failed", rule->rule_name, threshold->threshold_name); return -1; } memset(threshold->cnt_slice_sign, 0, size); error = 0; if (rule->debug_threshold_init) logmsgx(IPA_LOG_INFO, "rule %s, threshold %s: init_threshold: allocated %u statistics slices", rule->rule_name, threshold->threshold_name, n); /* * If we can't get current threshold state from the database, * then let's use current "threshold" value. */ newstate.thr = threshold->thr; if (STAILQ_EMPTY(threshold->db_list)) { /* "null" database is used. */ if (rule->debug_threshold_init) logmsgx(IPA_LOG_INFO, "rule %s, threshold %s: init_threshold: create threshold with no database", rule->rule_name, threshold->threshold_name); goto new_threshold_state; } /* Get threshold state from databases. */ if (db_get_threshold_state(rule, threshold, &oldstate, &db_name) < 0) { logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: init_threshold: db_get_threshold_state failed", rule->rule_name, threshold->threshold_name); return -1; } if (db_name == NULL) { logmsgx(IPA_LOG_WARNING, "rule %s, threshold %s: init_threshold: any database used for this threshold does not support db_get_threshold_state function", rule->rule_name, threshold->threshold_name); goto new_threshold_state; } if (rule->debug_threshold_init) logmsgx(IPA_LOG_INFO, "rule %s, threshold %s: init_threshold: got threshold state from %s database", rule->rule_name, threshold->threshold_name, db_name); /* Check if threshold has state in database. */ if (oldstate.thr == UINT64_C(0)) { /* There isn't the old state of the threshold. */ if (rule->debug_threshold_init) logmsgx(IPA_LOG_INFO, "rule %s, threshold %s: init_threshold: database does not has threshold state, probably this is a new threshold", rule->rule_name, threshold->threshold_name); goto new_threshold_state; } /* Check and init "threshold" parameter. */ if (rule->debug_threshold_init && threshold->thr != oldstate.thr) { char buf[BUF_CNT_SIZE]; strcpy(buf, cnt_to_buf(&threshold->thr, threshold->cnt_type)); logmsgx(IPA_LOG_INFO, "rule %s, threshold %s: init_threshold: config threshold %s, database threshold %s (will use %s value)", rule->rule_name, threshold->threshold_name, buf, cnt_to_buf(&oldstate.thr, threshold->cnt_type), threshold->load_threshold ? "database" : "config"); } if (threshold->load_threshold) newstate.thr = threshold->thr = oldstate.thr; /* Check tm_from and tm_updated. */ if (check_ipa_tm(&oldstate.tm_from) < 0) { logmsgx(IPA_LOG_WARNING, "rule %s, threshold %s: init_threshold: wrong value of tm_from date: %s", rule->rule_name, threshold->threshold_name, ipa_tm_to_buf(&oldstate.tm_from)); goto new_threshold_state_error; } /* A threshold could be updated at 24:00:00, fix this date. */ fix_240000(&oldstate.tm_updated); if (check_ipa_tm(&oldstate.tm_updated) < 0) { logmsgx(IPA_LOG_WARNING, "rule %s, threshold %s: init_threshold: wrong value of tm_updated date: %s", rule->rule_name, threshold->threshold_name, ipa_tm_to_buf(&oldstate.tm_updated)); goto new_threshold_state_error; } /* tm_from should be <= tm_updated. */ if (cmp_ipa_tm(&oldstate.tm_updated, &oldstate.tm_from) < 0) { char *buf; buf = mem_strdup(ipa_tm_to_buf(&oldstate.tm_updated), m_tmp); logmsgx(IPA_LOG_WARNING, "rule %s, threshold %s: init_threshold: tm_updated date %s is less than tm_from date %s", rule->rule_name, threshold->threshold_name, buf != NULL ? buf : "(no memory)", ipa_tm_to_buf(&oldstate.tm_from)); mem_free(buf, m_tmp); goto new_threshold_state_error; } if (cmp_ipa_tm(&oldstate.tm_updated, &curdate) > 0) { logmsgx(IPA_LOG_WARNING, "rule %s, threshold %s: init_threshold: tm_updated %s is greater than current time", rule->rule_name, threshold->threshold_name, ipa_tm_to_buf(&oldstate.tm_updated)); goto new_threshold_state_error; } old_time_width = ipa_tm_diff(&oldstate.tm_updated, &oldstate.tm_from); if (old_time_width != threshold->time_width) { char buf[BUF_TIME_SIZE]; strcpy(buf, time_to_buf(old_time_width)); logmsgx(IPA_LOG_INFO, "rule %s, threshold %s: init_threshold: it seems that threshold_time_width is changed: %s -> %s", rule->rule_name, threshold->threshold_name, buf, time_to_buf(threshold->time_width)); } /* Old number of slices between tm_from and tm_updated. */ old_slice_n = old_time_width / threshold->time_slice->event_step; /* * Let it be one slice between tm_from and tm_updated, * it is better, than simply forget about that statistics. */ if (old_slice_n == 0) old_slice_n = 1; /* * One chunk between tm_from and tm_updated. */ chunk = oldstate.cnt / old_slice_n; if (threshold->threshold_type & THRESHOLD_JUMP_OVER_STOPPED) { /* Jump over time interval when ipa was stopped. */ if (threshold->cnt_slice_n <= old_slice_n) { threshold->cnt_slice_i = 0; n = threshold->cnt_slice_n; } else { threshold->cnt_slice_i = old_slice_n; n = old_slice_n; } /* Calculate tm_from. */ tm_from = curdate; if (old_time_width < threshold->time_width) ipa_tm_subtime(&tm_from, old_time_width); else ipa_tm_subtime(&tm_from, threshold->time_width); newstate.tm_from = tm_from; } else { /* Slide threshold's statistics over time interval. */ if (ipa_tm_diff(&curdate, &oldstate.tm_updated) < threshold->time_width) { /* tm_from tm_updated curdate */ /* * Get number of slices between curdate and tm_updated. * n is always less than cnt_slice_n, since curdate - * oldstate.tm_updated is less than time_width. */ n = ipa_tm_diff(&curdate, &oldstate.tm_updated) / threshold->time_slice->event_step; /* Remember number of slices between curdate and tm_updated. */ threshold->cnt_slice_i = n; /* Max possible number of slices between tm_from and tm_updated. */ n = threshold->cnt_slice_n - n; /* n = min(n, old_slice_n). */ if (n > old_slice_n) n = old_slice_n; /* Get number of next slice to use. */ threshold->cnt_slice_i += n; if (threshold->cnt_slice_i == threshold->cnt_slice_n) threshold->cnt_slice_i = 0; /* Calculate tm_from. */ tm_from = curdate; ipa_tm_subtime(&tm_from, threshold->time_width); newstate.tm_from = cmp_ipa_tm(&tm_from, &oldstate.tm_from) > 0 ? tm_from : oldstate.tm_from; } else { /* Don't use previous threshold state, because it is stale. */ newstate.tm_from = curdate; threshold->cnt_slice_i = n = 0; cnt = UINT64_C(0); goto skip_chunk_fill; } } /* Fill slices. */ if (chunk != UINT64_C(0)) { /* Fill equal chunks. */ for (i = 0; i < n; ++i) threshold->cnt_slice[i] = chunk; cnt = n * chunk; chunk = oldstate.cnt - cnt; } else { /* Not enough statistics for equal chunks. */ cnt = UINT64_C(0); chunk = oldstate.cnt; } if (chunk != UINT64_C(0)) { /* * Fill the rest of statistics, this is very approximately * as all threshold's initialization. */ for (i = 0; i < n; ++i) { threshold->cnt_slice[i] += UINT64_C(1); cnt++; if (--chunk == UINT64_C(0)) break; } } skip_chunk_fill: set_thr_min_max(threshold); if (rule->debug_threshold_init) { logmsgx(IPA_LOG_INFO, "rule %s, threshold %s: init_threshold: %u slice%s inited from database, counter is %s, current slice #%u", rule->rule_name, threshold->threshold_name, n, n == 1 ? "" : "s", cnt_to_buf(&cnt, threshold->cnt_type), threshold->cnt_slice_i); logmsgx(IPA_LOG_INFO, "rule %s, threshold %s: thr %"PRIu64", thr_min %"PRIu64", thr_max %"PRIu64, rule->rule_name, threshold->threshold_name, threshold->thr, threshold->thr_min, threshold->thr_max); } threshold->cnt = newstate.cnt = cnt; threshold->tm_from = newstate.tm_from; threshold->tm_updated = newstate.tm_updated = curdate; /* Remove statistics from the latest slice. */ threshold_flush_slice(rule, threshold); if (rule->debug_threshold_init) logmsgx(IPA_LOG_INFO, "rule %s, threshold %s: init_threshold: set threshold state", rule->rule_name, threshold->threshold_name); if (db_set_threshold_state(rule, threshold, &newstate) < 0) { logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: init_threshold: db_set_threshold_state failed", rule->rule_name, threshold->threshold_name); return -1; } return 0; new_threshold_state_error: error = 1; new_threshold_state: set_thr_min_max(threshold); threshold->tm_from = threshold->tm_updated = newstate.tm_from = newstate.tm_updated = curdate; threshold->cnt = newstate.cnt = UINT64_C(0); threshold->cnt_slice_i = 0; if (rule->debug_threshold_init) { logmsgx(IPA_LOG_INFO, "rule %s, threshold %s: init_threshold: set new threshold state %s", rule->rule_name, threshold->threshold_name, error ? "(fixing errors found in database)" : ""); logmsgx(IPA_LOG_INFO, "rule %s, threshold %s: thr %"PRIu64", thr_min %"PRIu64", thr_max %"PRIu64, rule->rule_name, threshold->threshold_name, threshold->thr, threshold->thr_min, threshold->thr_max); } if (db_set_threshold_state(rule, threshold, &newstate) < 0) { logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: init_threshold: db_set_threshold_state failed", rule->rule_name, threshold->threshold_name); return -1; } return 0; } /* * Init all thresholds in one rule. */ int init_thresholds(const struct rule *rule) { struct threshold *threshold; STAILQ_FOREACH(threshold, &rule->thresholds, link) if (init_threshold(rule, threshold) < 0) { logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: init_thresholds: init_threshold failed", rule->rule_name, threshold->threshold_name); return -1; } return 0; } /* * Return pointer to threshold with the give name. */ struct threshold * threshold_by_name(const struct rule *rule, const char *name) { struct threshold *threshold; STAILQ_FOREACH(threshold, &rule->thresholds, link) if (strcmp(name, threshold->threshold_name) == 0) return threshold; return NULL; } /* * This function is called from newday(). */ int thresholds_newday(struct rule *rule) { u_int check_sec; struct threshold *threshold; check_sec = SECONDS_IN_WEEK; /* Hint. */ STAILQ_FOREACH(threshold, &rule->thresholds, link) { if (IS_INACTIVE(threshold->worktime)) { if (IS_ACTIVE(threshold)) if (set_threshold_inactive(rule, threshold) < 0) { logmsgx(IPA_LOG_ERR, "thresholds_newday: set_threshold_inactive failed"); return -1; } if (check_sec > threshold->worktime->active_sec) check_sec = threshold->worktime->active_sec; } else { if (IS_INACTIVE(threshold)) { if (set_threshold_active(rule, threshold) < 0) { logmsgx(IPA_LOG_ERR, "thresholds_newday: set_threshold_active failed"); return -1; } /* threshold->check_sec was updated in set_threshold_active(). */ } else threshold->check_sec = threshold->time_slice->event_sec; if (check_sec > threshold->worktime->inactive_sec) check_sec = threshold->worktime->inactive_sec; if (check_sec > threshold->check_sec) check_sec = threshold->check_sec; } if (rule->check_sec > check_sec) rule->check_sec = check_sec; } return 0; } /* * Copy all thresholds from list_src to rule. */ int copy_thresholds(struct rule *rule, const struct thresholds_list *list_src, int dyn_flag) { struct threshold *threshold_dst; const struct threshold *threshold_src; STAILQ_FOREACH(threshold_src, list_src, link) { if ( (threshold_dst = mzone_alloc(threshold_mzone)) == NULL) { xlogmsgx(IPA_LOG_ERR, "copy_thresholds: mzone_alloc failed"); return -1; } /* Copy settings from source threshold. */ *threshold_dst = *threshold_src; /* Init fields which are pointers to memory, which can't be shared. */ init_cmds_in_threshold(threshold_dst); threshold_dst->rule = rule; /* Link just allocated threshold to rule. */ STAILQ_INSERT_TAIL(&rule->thresholds, threshold_dst, link); if (dyn_flag) ++ndynthresholds; else ++nstatthresholds; /* * If something goes wrong with memory allocation, * previous settings will allow to deinit current rule. */ if (copy_cmd_list(rule, &threshold_dst->below_threshold, &threshold_src->below_threshold) < 0) return -1; if (copy_cmd_list(rule, &threshold_dst->equal_threshold, &threshold_src->equal_threshold) < 0) return -1; if (copy_cmd_list(rule, &threshold_dst->above_threshold, &threshold_src->above_threshold) < 0) return -1; if (copy_cmd_list(rule, &threshold_dst->rc[RC_STARTUP], &threshold_src->rc[RC_STARTUP]) < 0) return -1; if (copy_cmd_list(rule, &threshold_dst->rc[RC_SHUTDOWN], &threshold_src->rc[RC_SHUTDOWN]) < 0) return -1; threshold_dst->wpid.u.threshold = threshold_dst; } return 0; } /* * Release memory used by a list of thresholds, free_mask determines * which part of threshold{} structure to free. */ void free_thresholds(u_int free_mask, struct thresholds_list *thresholds, int dyn_flag) { struct threshold *threshold, *threshold_next; for (threshold = STAILQ_FIRST(thresholds); threshold != NULL; threshold = threshold_next) { threshold_next = STAILQ_NEXT(threshold, link); if (dyn_flag) --ndynthresholds; if (free_mask & RULE_FREE_THRESHOLDS) { mem_free(threshold->threshold_name, m_anon); mem_free(threshold->threshold_info, m_parser); } mem_free(threshold->cnt_slice, m_anon); mem_free(threshold->cnt_slice_sign, m_anon); free_cmd_list(&threshold->below_threshold); free_cmd_list(&threshold->equal_threshold); free_cmd_list(&threshold->above_threshold); free_cmd_list(&threshold->rc[RC_STARTUP]); free_cmd_list(&threshold->rc[RC_SHUTDOWN]); mzone_free(threshold_mzone, threshold); } } void init_cmds_in_threshold(struct threshold *threshold) { init_cmd_list(&threshold->below_threshold); init_cmd_list(&threshold->equal_threshold); init_cmd_list(&threshold->above_threshold); init_cmd_list(&threshold->rc[RC_STARTUP]); init_cmd_list(&threshold->rc[RC_SHUTDOWN]); } void set_sync_exec_in_threshold(struct threshold *threshold) { if (threshold->below_threshold.sync_exec < 0) threshold->below_threshold.sync_exec = 0; if (threshold->equal_threshold.sync_exec < 0) threshold->equal_threshold.sync_exec = 0; if (threshold->above_threshold.sync_exec < 0) threshold->above_threshold.sync_exec = 0; if (threshold->rc[RC_STARTUP].sync_exec < 0) threshold->rc[RC_STARTUP].sync_exec = 1; if (threshold->rc[RC_SHUTDOWN].sync_exec < 0) threshold->rc[RC_SHUTDOWN].sync_exec = 1; } #endif /* WITH_THRESHOLDS */