/*-
* 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 <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#include <sys/types.h>
#include "ipa_mod.h"
#include "dlapi.h"
#include "confcommon.h"
#include "memfunc.h"
#include "parser.h"
#include "ipa_ac.h"
#include "ipa_db.h"
#include "ipa_cmd.h"
#include "ipa_ctl.h"
#include "ipa_time.h"
#include "ipa_conf.h"
#include "ipa_log.h"
#include "ipa_main.h"
#include "ipa_rules.h"
#include "ipa_autorules.h"
u_int 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 */
syntax highlighted by Code2HTML, v. 0.9.1