diff --git a/include/AlertableEntity.h b/include/AlertableEntity.h index 3258857499..d4f32b4f58 100644 --- a/include/AlertableEntity.h +++ b/include/AlertableEntity.h @@ -25,46 +25,82 @@ #include "ntop_includes.h" class AlertableEntity { - protected: +protected: AlertEntity entity_type; std::string entity_val; - std::map alert_cache[MAX_NUM_PERIODIC_SCRIPTS]; - std::map triggered_alerts[MAX_NUM_PERIODIC_SCRIPTS]; + /* + Creating multiple maps guarantees that periodic scripts at different granularities + do not interfere each other and thus that they can run concurrently without locking. - public: + However while alert_cache is accessed only by the alert engine and thus it is "thread-safe" + as concurrent scripts (i.e. at different granularities) cannot call it, triggered_alerts + needs to be protected as + - it can be called by the Lua GUI + - it can be called by the alert engine + */ + std::map alert_cache[MAX_NUM_PERIODIC_SCRIPTS]; + std::map triggered_alerts[MAX_NUM_PERIODIC_SCRIPTS], + /* Read-only and shadow copy of the triggered alerts (that as usually empty/NULL) */ + *rx_triggered_alerts[MAX_NUM_PERIODIC_SCRIPTS], *shadow_rx_triggered_alerts[MAX_NUM_PERIODIC_SCRIPTS]; + u_int num_triggered_alerts; + + + void syncReadonlyTriggeredAlerts(); + void updateNumTriggeredAlerts(); + +public: AlertableEntity(AlertEntity entity) { - entity_type = entity; + entity_type = entity, num_triggered_alerts = 0; + + for(u_int i=0; i::iterator it = alert_cache[(u_int)p].find(key); return((it != alert_cache[(u_int)p].end()) ? it->second : std::string("")); } - inline void setAlertCacheValue(std::string key, std::string value, ScriptPeriodicity p) { + inline void setAlertCacheValue(std::string key, std::string value, + ScriptPeriodicity p) { alert_cache[(u_int)p][key] = value; } u_int getNumTriggeredAlerts(ScriptPeriodicity p); + inline u_int getNumTriggeredAlerts() { return(num_triggered_alerts); } + inline void setEntityValue(const char *ent_val) { entity_val = ent_val; } bool triggerAlert(lua_State* vm, std::string key, - NetworkInterface *iface, ScriptPeriodicity p, time_t now, - AlertLevel alert_severity, AlertType alert_type, - const char *alert_subtype, - const char *alert_json, - bool alert_disabled); - bool releaseAlert(lua_State* vm, NetworkInterface *iface, std::string key, ScriptPeriodicity p, time_t now); + NetworkInterface *iface, ScriptPeriodicity p, time_t now, + AlertLevel alert_severity, AlertType alert_type, + const char *alert_subtype, + const char *alert_json, + bool alert_disabled); + bool releaseAlert(lua_State* vm, NetworkInterface *iface, std::string key, + ScriptPeriodicity p, time_t now); void luaAlert(lua_State* vm, Alert *alert, ScriptPeriodicity p); void getExpiredAlerts(ScriptPeriodicity p, lua_State* vm, time_t now); - u_int getNumTriggeredAlerts(); void countAlerts(grouped_alerts_counters *counters); - void getAlerts(lua_State* vm, AlertType type_filter, AlertLevel severity_filter, u_int *idx); + void getAlerts(lua_State* vm, AlertType type_filter, + AlertLevel severity_filter, u_int *idx); }; #endif diff --git a/src/AlertableEntity.cpp b/src/AlertableEntity.cpp index ca33b9b85d..9acc43eb22 100644 --- a/src/AlertableEntity.cpp +++ b/src/AlertableEntity.cpp @@ -28,6 +28,7 @@ void AlertableEntity::getExpiredAlerts(ScriptPeriodicity p, lua_State* vm, time_ std::map::iterator it; int seconds = Utils::periodicityToSeconds(p); u_int idx = 0; + bool modified = false; for(it = triggered_alerts[(u_int)p].begin(); it != triggered_alerts[(u_int)p].end();) { Alert *alert = &it->second; @@ -36,9 +37,10 @@ void AlertableEntity::getExpiredAlerts(ScriptPeriodicity p, lua_State* vm, time_ if(alert->is_disabled) { /* The alert is disabled, remove it now. * NOTE: do not increment again iterator after this assignment. */ - triggered_alerts[(u_int)p].erase(it++); + triggered_alerts[(u_int)p].erase(it++), modified = true; } else { lua_newtable(vm); + luaAlert(vm, alert, p); lua_pushinteger(vm, ++idx); @@ -49,35 +51,38 @@ void AlertableEntity::getExpiredAlerts(ScriptPeriodicity p, lua_State* vm, time_ } else ++it; } + + if(modified) syncReadonlyTriggeredAlerts(); } /* ****************************************** */ void AlertableEntity::luaAlert(lua_State* vm, Alert *alert, ScriptPeriodicity p) { /* NOTE: must conform to the AlertsManager format */ - lua_push_int32_table_entry(vm, "alert_type", alert->alert_type); - lua_push_str_table_entry(vm, "alert_subtype", alert->alert_subtype.c_str()); - lua_push_int32_table_entry(vm, "alert_severity", alert->alert_severity); - lua_push_int32_table_entry(vm, "alert_entity", entity_type); - lua_push_str_table_entry(vm, "alert_entity_val", entity_val.c_str()); + lua_push_int32_table_entry(vm, "alert_type", alert->alert_type); + lua_push_str_table_entry(vm, "alert_subtype", alert->alert_subtype.c_str()); + lua_push_int32_table_entry(vm, "alert_severity", alert->alert_severity); + lua_push_int32_table_entry(vm, "alert_entity", entity_type); + lua_push_str_table_entry(vm, "alert_entity_val", entity_val.c_str()); lua_push_uint64_table_entry(vm, "alert_tstamp", alert->alert_tstamp_start); lua_push_uint64_table_entry(vm, "alert_tstamp_end", alert->last_update); - lua_push_int32_table_entry(vm, "alert_granularity", Utils::periodicityToSeconds((ScriptPeriodicity)p)); - lua_push_str_table_entry(vm, "alert_json", alert->alert_json.c_str()); + lua_push_int32_table_entry(vm, "alert_granularity", Utils::periodicityToSeconds((ScriptPeriodicity)p)); + lua_push_str_table_entry(vm, "alert_json", alert->alert_json.c_str()); } /* ****************************************** */ /* Return true if the element was inserted, false if already present */ bool AlertableEntity::triggerAlert(lua_State* vm, std::string key, - NetworkInterface *iface, ScriptPeriodicity p, time_t now, - AlertLevel alert_severity, AlertType alert_type, - const char *alert_subtype, - const char *alert_json, - bool alert_disabled) { + NetworkInterface *iface, ScriptPeriodicity p, time_t now, + AlertLevel alert_severity, AlertType alert_type, + const char *alert_subtype, + const char *alert_json, + bool alert_disabled) { bool rv = false; std::map::iterator it = triggered_alerts[(u_int)p].find(key); - + bool modified = false; + if(entity_val.empty()) { ntop->getTrace()->traceEvent(TRACE_ERROR, "setEntityValue() not called or empty entity_val"); } else if(it != triggered_alerts[(u_int)p].end()) { @@ -87,10 +92,12 @@ bool AlertableEntity::triggerAlert(lua_State* vm, std::string key, /* Alert was not accounted but now enabled, so increase count */ it->second.is_disabled = false; iface->incNumAlertsEngaged(p); + modified = true; } else if(!it->second.is_disabled && alert_disabled) { /* Alert was accounted but and now disabled, so decresase count */ it->second.is_disabled = true; iface->decNumAlertsEngaged(p); + modified = true; } /* already present */ @@ -111,8 +118,7 @@ bool AlertableEntity::triggerAlert(lua_State* vm, std::string key, iface->incNumAlertsEngaged(p); triggered_alerts[(u_int)p][key] = alert; - - rv = true; /* inserted */ + modified = true, rv = true; /* inserted */ lua_newtable(vm); luaAlert(vm, &alert, p); @@ -121,15 +127,18 @@ bool AlertableEntity::triggerAlert(lua_State* vm, std::string key, if(!rv) lua_pushnil(vm); + if(modified) syncReadonlyTriggeredAlerts(); + return(rv); } /* ****************************************** */ -bool AlertableEntity::releaseAlert(lua_State* vm, NetworkInterface *iface, std::string key, ScriptPeriodicity p, time_t now) { +bool AlertableEntity::releaseAlert(lua_State* vm, NetworkInterface *iface, + std::string key, ScriptPeriodicity p, time_t now) { std::map::iterator it = triggered_alerts[(u_int)p].find(key); bool rv = false; - + if(it == triggered_alerts[(u_int)p].end()) { lua_pushnil(vm); return(rv); @@ -149,19 +158,21 @@ bool AlertableEntity::releaseAlert(lua_State* vm, NetworkInterface *iface, std:: lua_pushnil(vm); triggered_alerts[(u_int)p].erase(it); + syncReadonlyTriggeredAlerts(); + return(rv); } /* ****************************************** */ -u_int AlertableEntity::getNumTriggeredAlerts() { +void AlertableEntity::updateNumTriggeredAlerts() { int i; u_int num_alerts = 0; for(i = 0; i::iterator it; for(p = 0; psecond; + std::map *rx_copy = rx_triggered_alerts[p]; + + /* NOTE + Use rx_copy and not rx_triggered_alerts[p] as it might change overtime + due to syncReadonlyTriggeredAlerts() + */ - if(!alert->is_disabled) { - if(((type_filter == alert_none) || (type_filter == alert->alert_type)) - && ((severity_filter == alert_level_none) || (severity_filter == alert->alert_severity))) { - lua_newtable(vm); - luaAlert(vm, alert, (ScriptPeriodicity)p); + if(rx_copy != NULL) { + for(it = rx_copy->begin(); it != rx_copy->end(); ++it) { + Alert *alert = &it->second; - lua_pushinteger(vm, ++(*idx)); - lua_insert(vm, -2); - lua_settable(vm, -3); - } + if(!alert->is_disabled) { + if(((type_filter == alert_none) + || (type_filter == alert->alert_type)) + && ((severity_filter == alert_level_none) + || (severity_filter == alert->alert_severity))) { + lua_newtable(vm); + luaAlert(vm, alert, (ScriptPeriodicity)p); + + lua_pushinteger(vm, ++(*idx)); + lua_insert(vm, -2); + lua_settable(vm, -3); + } + } } } } @@ -209,14 +237,53 @@ void AlertableEntity::getAlerts(lua_State* vm, AlertType type_filter, AlertLevel /* ****************************************** */ +/* + IMPORTANT + as this method is called by the GUI while triggered_alerts[] might be manipulated + by periodic scrits, it uses rx_triggered_alerts instead of triggered_alerts +*/ u_int AlertableEntity::getNumTriggeredAlerts(ScriptPeriodicity p) { std::map::iterator it; u_int ctr = 0; - - for(it = triggered_alerts[p].begin(); it != triggered_alerts[p].end(); ++it) { - if(!it->second.is_disabled) - ctr++; + std::map *rx_copy = rx_triggered_alerts[p]; + + /* NOTE + Use rx_copy and not rx_triggered_alerts[p] as it might change overtime + due to syncReadonlyTriggeredAlerts() + */ + if(rx_copy != NULL) { + for(it = rx_copy->begin(); it != rx_copy->end(); ++it) { + if(!it->second.is_disabled) + ctr++; + } } - + return(ctr); } + +/* ****************************************** */ + +void AlertableEntity::syncReadonlyTriggeredAlerts() { + for(u_int i=0; i *cpy; + std::map::iterator it; + + try { + cpy = new std::map(); + + if(shadow_rx_triggered_alerts[i] != NULL) + delete shadow_rx_triggered_alerts[i]; + + shadow_rx_triggered_alerts[i] = rx_triggered_alerts[i]; + + for(it = triggered_alerts[i].begin(); it != triggered_alerts[i].end(); ++it) + (*cpy)[it->first] = Alert(it->second); + + rx_triggered_alerts[i] = cpy; + } catch(std::bad_alloc& ba) { + ntop->getTrace()->traceEvent(TRACE_ERROR, "Memory allocation error"); + } + } + + updateNumTriggeredAlerts(); +} diff --git a/src/NetworkInterface.cpp b/src/NetworkInterface.cpp index 281d7cb240..8f90876cbb 100644 --- a/src/NetworkInterface.cpp +++ b/src/NetworkInterface.cpp @@ -7700,7 +7700,7 @@ static bool host_invoke_alertable_callback(GenericHashEntry *entity, void *user_ * parameter set to the provided user_data. */ void NetworkInterface::walkAlertables(int entity_type, const char *entity_value, - alertable_callback *callback, void *user_data) { + alertable_callback *callback, void *user_data) { /* Hosts */ if((entity_type == alert_entity_none) || (entity_type == alert_entity_host)) { if(entity_value == NULL) { @@ -7755,7 +7755,7 @@ static void count_alerts_callback(AlertableEntity *alertable, void *user_data) { } void NetworkInterface::getEngagedAlertsCount(lua_State *vm, int entity_type, - const char *entity_value) { + const char *entity_value) { grouped_alerts_counters counters; u_int32_t tot_alerts = 0; std::map::iterator it; @@ -7864,7 +7864,8 @@ static void get_engaged_alerts_callback(AlertableEntity *alertable, void *user_d } void NetworkInterface::getEngagedAlerts(lua_State *vm, int entity_type, - const char *entity_value, AlertType alert_type, AlertLevel alert_severity) { + const char *entity_value, AlertType alert_type, + AlertLevel alert_severity) { struct get_engaged_alerts_userdata data; data.vm = vm;