// Author: Ricardas Stoma // Company: Kolmisoft // Year: 2013 // About: Script checks various user call data and alerts if limits are reached #define SCRIPT_NAME "m2_alerts" #define SCRIPT_VERSION "1.36" #include "../m2_functions.c" #include "m2_alerts.h" #include // GLOBAL VARIABLES // dynamic list alerts_t *alerts = NULL; unsigned long int alerts_count = 0; unsigned long int last_alerts_count = 0; // dynamic calls list calls_index_t calls[DATA_PACKETS]; // last call ID unsigned long long int last_call_id = 0; // owner email data email_data_t *email; int email_count = 0; // time variables char date_str[100]; int current_hour; int current_min; int current_day; // get alerts on first start up int forced_update = 1; int debug = 0; // will be set to one on alert/clear // this variable is used by alert groups int global_alert_is_changed = 0; // FUNCTION DECLARATIONS int get_alerts(); int get_calls_data(); unsigned long long int mysql_last_call_id(); int update_data(int add); int check_alerts(); int report_alert(alerts_t *alert, data_info_t *data, int report); int get_email_data(int owner_id); int get_contacts(alerts_t *alert); int get_schedules(alerts_t *alert); int check_if_alerts_need_update(); int before_start(); double calculate_aggregated_data(int alert_type, double data_sum, long int data_count); int get_email_owner_index(int owner_id); int update_email_details(); void increment_clear_period(); void check_clear_date(); void show_alerts_status(); void *update_time(); void email_action_log(int user_id, char *email, int status, char *error); void sms_action_log(int user_id, char *number, int status, char *error); void alert_action_log(char *msg, int alert, int alert_id); int get_op_routing_group_id(int op_id); void alert_get_object_url(int check_type_integer, char *url, int id); // MAIN FUNCTION int main(int argc, char *argv[]) { if (argc > 1 && strcmp(argv[1], "-v") == 0) { printf("%s\n", SCRIPT_VERSION); return 0; } // error file FILE *tmp_errorfile = fopen(LOG_PATH, "a+"); if (tmp_errorfile == NULL) { fprintf(stderr, "Cannot open log file " LOG_PATH "\n"); return 1; } if (m2_check_process_lock()) exit(1); fclose(tmp_errorfile); run_in_background_check(argc, argv); if (run_in_background) { // Our process ID and Session ID pid_t pid, sid; // Fork off the parent process pid = fork(); if (pid < 0) { exit(1); } // If we got a good PID, then we can exit the parent process. if (pid > 0) { exit(0); } // Change the file mode mask umask(0); // Create a new SID for the child process sid = setsid(); if (sid < 0) { // Log the failure exit(EXIT_FAILURE); } // Change the current working directory if ((chdir("/")) < 0) { // Log the failure exit(EXIT_FAILURE); } // Close out the standard file descriptors close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); } // // create thread with detached state pthread_attr_t tattr; pthread_attr_init(&tattr); pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED); // timer that updates current time every second // we use separate thread to minimize the execution of time functions // lets say we call m2_log 1000 times, then we call time functions 1000 times // so updating time every second ir more efficient pthread_t timer; pthread_create(&timer, &tattr, update_time, NULL); sleep(1); system("mkdir -p /tmp/m2/alerts"); system("chmod 777 -R /tmp/m2/alerts"); system("cd /tmp/m2/alerts && find . -name '*' ! -path . | xargs rm -fr"); // just a counter that increments every second // it is used for timing unsigned int counter = 0; // starting sript m2_init("Starting M2 Alerts script\n"); // check if everything is ok with database if (before_start()) exit(1); // get email data if (get_email_data(0)) exit(1); // initialize calls index table memset(&calls[0], 0, DATA_PACKETS * sizeof(calls_index_t)); // get last call_id last_call_id = mysql_last_call_id(&mysql); // get web_dir and web_url if (m2_get_web_config(web_url, web_dir)) exit(1); while (1) { // get new alerts data if ((counter % ((CHECK_IF_UPDATE + 30) / 30)) == 0) { if (get_alerts()) exit(1); // reread last call id if (last_alerts_count == 0 && alerts_count > 0) { last_call_id = mysql_last_call_id(&mysql); } last_alerts_count = alerts_count; } if (alerts_count > 0) { // GET, UPDATE AND DELETE DATA if (update_data(0)) exit(1); // remove old data if (get_calls_data()) exit(1); // get latest calls if (update_data(1)) exit(1); // add new data // CHECK ALERTS if (check_alerts()) exit(1); increment_clear_period(); check_clear_date(); if ((counter % ((SHOW_ALERTS_STATUS_EVERY + 30) / 30)) == 0) { show_alerts_status(); } if ((counter % ((UPDATE_EMAILS_EVERY + 30) / 30)) == 0) { update_email_details(); } } else { sleep(DATA_TICK_TIME); // if we dont have alerts, then delete alert files system("cd /tmp/m2/alerts && find . -name '*' ! -path . | xargs rm -fr"); } counter++; } return 0; } /* ############ FUNCTIONS ####################################################### */ /* Function checks if alerts need to be updated Update is needed if alerts, schedules, contacts or groups are modified in any way */ int check_if_alerts_need_update() { MYSQL_RES *result; MYSQL_ROW row; // defaul state id 'alerts dont need an update' int need_update = 0; // lets check if alerts, contact, schedules or groups have been modified // gui sets 'alerts_need_update' value to 1 when any of those pages are created/updated/removed if (m2_mysql_query("SELECT value FROM conflines WHERE name = 'alerts_need_update'")) { return -1; } result = mysql_store_result(&mysql); if (result) { if (mysql_num_rows(result)) { row = mysql_fetch_row(result); // get integer value if (row[0]) need_update = atoi(row[0]); } } mysql_free_result(result); return need_update; } /* Convert alert type string value to integer */ int alert_get_alert_type(const char *alert_type) { int type = 0; if (strcmp(alert_type, "calls_total") == 0) { type = 1; } else if (strcmp(alert_type, "calls_answered") == 0) { type = 2; } else if (strcmp(alert_type, "calls_not_answered") == 0) { type = 3; } else if (strcmp(alert_type, "asr") == 0) { type = 4; } else if (strcmp(alert_type, "acd") == 0) { type = 5; } else if (strcmp(alert_type, "pdd") == 0) { type = 6; } else if (strcmp(alert_type, "ttc") == 0) { type = 7; } else if (strcmp(alert_type, "billsec_sum") == 0) { type = 8; } else if (strcmp(alert_type, "price_sum") == 0) { type = 9; } else if (strcmp(alert_type, "sim_calls") == 0) { type = 10; } else if (strcmp(alert_type, "hgc_absolute") == 0) { type = 11; } else if (strcmp(alert_type, "hgc_percent") == 0) { type = 12; } else if (strcmp(alert_type, "group") == 0) { type = 13; } if (type) { return type; } else { m2_log("Alert type can't be 0! (%s)\n", alert_type); exit(1); } } /* Convert alert count type string value to integer */ int alert_get_alert_count_type(const char *alert_count_type) { int type = 0; if (strcmp(alert_count_type, "ABS") == 0) type = 1; if (strcmp(alert_count_type, "DIFF") == 0) type = 2; if (type) { return type; } else { m2_log("Alert count type can't be 0! (%s)\n", alert_count_type); exit(1); } } /* Convert check type string value to integer */ int alert_get_check_type(const char *check_type, int secondary) { int type = 0; if (strcmp(check_type, "user") == 0) type = 1; if (strcmp(check_type, "termination_point") == 0) type = 2; if (strcmp(check_type, "origination_point") == 0) type = 3; if (strcmp(check_type, "destination") == 0) type = 4; if (strcmp(check_type, "destination_group") == 0) type = 5; if (!type && !secondary) { m2_log("Alert check type can't be 0! (%s)\n", check_type); exit(1); } return type; } /* Convert check type integer value to string */ void alert_get_check_type_string(int check_type_integer, char *check_type) { strcpy(check_type, "Unknown"); if (check_type_integer == 1) strcpy(check_type, "USER"); if (check_type_integer == 2) strcpy(check_type, "TERMINATION POINT"); if (check_type_integer == 3) strcpy(check_type, "ORIGINATION POINT"); if (check_type_integer == 4) strcpy(check_type, "DESTINATION"); if (check_type_integer == 5) strcpy(check_type, "DESTINATION GROUP"); } /* Get object URL supported object types: 1;2;3;5 */ void alert_get_object_url(int check_type_integer, char *url, int id) { // skip a non-existent object if (id <= 0) return; char object_edit_url[64] = ""; switch (check_type_integer) { case 1: strcpy(object_edit_url, "/users/edit/"); break; case 2: strcpy(object_edit_url, "/devices/device_edit/"); break; case 3: strcpy(object_edit_url, "/devices/device_edit/"); break; case 5: strcpy(object_edit_url, "/destination_groups/edit/"); break; } if (strlen(object_edit_url) > 0) { sprintf(url, "%s%s%s%d", web_url, web_dir, object_edit_url, id); } } /* Convert object id to object name string */ void alert_get_object_name_string(alerts_t *alert, int data_id, char *object_name, int secondary) { MYSQL_RES *result; MYSQL_ROW row; char sqlcmd[2048] = ""; int check_type = alert->check_type; char check_data[100] = ""; strcpy(check_data, alert->check_data); if (secondary) { check_type = alert->check_type_secondary; strcpy(check_data, alert->check_data_secondary); } if (check_type == 4 && strchr(check_data, '%')) { sprintf(object_name, "%s", check_data); return; } sprintf(object_name, "%d", data_id); if (check_type == 1) { sprintf(sqlcmd, "SELECT IF(LENGTH(TRIM(CONCAT(users.first_name, ' ', users.last_name))) > 0, TRIM(CONCAT(users.first_name, ' ', users.last_name)), users.username) AS 'nice_username' FROM users WHERE id = %d", data_id); } if (check_type == 2 || check_type == 3) { sprintf(sqlcmd, "SELECT IF(LENGTH(description), description, ipaddr) FROM devices WHERE id = %d", data_id); } if (check_type == 4) { sprintf(sqlcmd, "SELECT name FROM destinations WHERE prefix = %s", check_data); } if (check_type == 5) { sprintf(sqlcmd, "SELECT name FROM destinationgroups WHERE id = %d", data_id); } if (m2_mysql_query(sqlcmd)) return; result = mysql_store_result(&mysql); if (result) { if (mysql_num_rows(result)) { row = mysql_fetch_row(result); if (row[0]) sprintf(object_name, "%s", row[0]); } } mysql_free_result(result); } /* Log alerts */ void show_alerts() { int i = 0; if (alerts_count > 0) m2_log("Alerts:\n"); for (i = 0; i < alerts_count; i++) { m2_log("id: %d\n", alerts[i].id); m2_log("name: %s\n", alerts[i].name); m2_log("status: %d\n", alerts[i].status); m2_log("alert type: %d\n", alerts[i].alert_type); m2_log("alert count type: %d\n", alerts[i].alert_count_type); m2_log("check type: %d\n", alerts[i].check_type); m2_log("check data: %s\n", alerts[i].check_data); m2_log("value at alert: %.3f\n", alerts[i].value_at_alert); m2_log("alert if less: %.3f\n", alerts[i].alert_if_less); m2_log("alert if more: %.3f\n", alerts[i].alert_if_more); m2_log("value at clear: %.3f\n", alerts[i].value_at_clear); m2_log("clear if less: %.3f\n", alerts[i].clear_if_less); m2_log("clear if more: %.3f\n", alerts[i].clear_if_more); m2_log("ignore if calls less: %ld\n", alerts[i].ignore_if_calls_less); m2_log("ignore if calls more: %ld\n", alerts[i].ignore_if_calls_more); m2_log("action alert email: %d\n", alerts[i].action_alert_email); m2_log("action alert sms: %d\n", alerts[i].action_alert_sms); m2_log("action alert disable: %d\n", alerts[i].action_alert_disable_object); m2_log("action alert change rg id: %u\n", alerts[i].action_alert_change_rg_id); m2_log("action clear email: %d\n", alerts[i].action_clear_email); m2_log("action clear sms: %d\n", alerts[i].action_clear_sms); m2_log("action clear disable: %d\n", alerts[i].action_clear_enable_object); m2_log("action clear change rg id: %d\n", alerts[i].action_clear_change_rg_id); m2_log("before alert original rg id: %d\n", alerts[i].before_alert_original_rg_id); m2_log("alert groups id: %u\n", alerts[i].alert_groups_id); m2_log("disable clear: %u\n", alerts[i].disable_clear); m2_log("disable tp in dp: %u\n", alerts[i].action_alert_disable_object_in_dp); m2_log("enable tp in dp: %u\n", alerts[i].action_clear_enable_object_in_dp); m2_log("owner id: %d\n", alerts[i].owner_id); m2_log("clear_after: %" PRIu64 "\n", alerts[i].clear_period); m2_log("clear_date: %s\n", alerts[i].clear_date); m2_log("check_period: %d\n", alerts[i].raw_period); m2_log("notify to user: %d\n", alerts[i].notify_to_user); m2_log("hgc: %d\n", alerts[i].hgc); m2_log("disable dp in rg: %u\n", alerts[i].action_alert_disable_dp_in_rg); m2_log("enable dp in rg: %u\n", alerts[i].action_clear_enable_dp_in_rg); m2_log("dial_peer_id: %d\n", alerts[i].dial_peer_id); m2_log("SMS alert message: %s\n", alerts[i].sms_alert_message); m2_log("SMS clear message: %s\n", alerts[i].sms_clear_message); m2_log("check type secondary: %d\n", alerts[i].check_type_secondary); m2_log("check data secondary: %s\n", alerts[i].check_data_secondary); if (alerts[i].alert_type == 13) { m2_log("alert groups: %s\n", alerts[i].alert_group_id_list); m2_log("alert if more than: %d\n", alerts[i].alert_if_more_than); m2_log("alert if less than: %d\n", alerts[i].clear_if_less_than); } m2_log("comment: %s\n\n", alerts[i].comment); if (alerts[i].alert_groups_id > 0) { contact_t *current = alerts[i].group->contact; while (current) { m2_log("contact email = %s\n", current->email); m2_log("contact id = %d\n", current->id); schedule_t *current_s = alerts[i].group->schedule; while (current_s) { m2_log("daytype = %d\n", current_s->daytype); m2_log("start = %s\n", current_s->start); m2_log("end = %s\n", current_s->end); current_s = current_s->prev; } current = current->prev; m2_log("------------------------\n\n"); } } } } /* Calculates last index of calls array, based on period in minutes */ int calculate_period(long long int minutes) { // last index is needed for alerts to have different periods // lets say we have maximum period of 60 seconds and we get new calls every 15 seconds // so we have 60 / 15 = 4 packets in our calls array: [0 1 2 3] // if an alerts has a period of 30 seconds, then the last index is 2 // note: actually we have minutes not seconds return ((minutes * 60) / DATA_TICK_TIME) - 1; } /* Get all alerts from database */ int get_alerts() { MYSQL_RES *result; MYSQL_ROW row; // check if this is our initial daemon run // if so, we need to force alert update, because we dont have anu alerts at the moment if (!forced_update) { // if this is not initial daemon run, we need to check if alerts have been modified int res = check_if_alerts_need_update(); // res = -1 means error // res = 1 means alerts need update // res = 0 means alerts dont need update if (res == -1) { return 0; } else if (res != 1) { return 0; } } m2_log("Alerts updated. Rereading data...\n"); // for alert groups global_alert_is_changed = 1; // do not force again forced_update = 0; // get user alerts // if alert is disabled, then all the aggregated data is lost. Maybe we should keep that data in case the alert gets activated? if (m2_mysql_query("SELECT alerts.id, alert_type, alert_count_type, check_type, check_data, value_at_alert, " "alert_if_less, alert_if_more, value_at_clear, clear_if_less, clear_if_more, ignore_if_calls_less, " "ignore_if_calls_more, status, action_alert_email, action_alert_sms, " "action_alert_disable_object, action_alert_change_routing_group_id, action_clear_email, action_clear_sms, " "action_clear_enable_object, action_clear_change_routing_group_id, before_alert_original_routing_group_id, " "alert_groups_id, count_period, disable_clear, owner_id, comment, disable_tp_in_dial_peer, " "enable_tp_in_dial_peer, clear_after, clear_on_date, notify_to_user, hgc, " "GROUP_CONCAT(alert_dependencies.alert_id), alert_when_more_than, clear_when_less_than, name, action_alert_sms_text, action_clear_sms_text, " "action_alert_disable_dp_in_rg, action_clear_enable_dp_in_rg, dial_peer_id, check_type_secondary, check_data_secondary " "FROM alerts " "LEFT JOIN alert_dependencies ON alert_dependencies.owner_alert_id = alerts.id " "WHERE status = 1 " "GROUP BY alerts.id")) { return 0; } // allocate memory to save old data // we will save old alerts to preserve aggregated data (like total calls, average billsec, ...) // and later delete not existing/disabled alerts alerts_tmp_data_t *alerts_tmp = malloc(alerts_count * sizeof(alerts_tmp_data_t)); int i = 0, j = 0; int old_alerts_count = alerts_count; // save old alerts IDs and pointers to aggregated data for (i = 0; i < alerts_count; i++) { alerts_tmp[i].id = alerts[i].id; // old IDs alerts_tmp[i].addr = alerts[i].data_info; // old pointers to aggregated data alerts_tmp[i].found = 0; // this will be used to match old alerts with new alerts alerts_tmp[i].first_uniqueid = alerts[i].first_uniqueid; } // also, we will update all contacts and schedules // free old contacts for (i = 0; i < alerts_count; i++) { if (alerts[i].alert_groups_id > 0) { contact_t *current = alerts[i].group->contact; contact_t *prev = alerts[i].group->contact; while (current != NULL) { prev = current->prev; free(current); current = prev; } } } // free old schedules for (i = 0; i < alerts_count; i++) { if (alerts[i].alert_groups_id > 0) { schedule_t *current = alerts[i].group->schedule; schedule_t *prev = alerts[i].group->schedule; while (current != NULL) { prev = current->prev; free(current); current = prev; } } } // clear alerts memory memset(alerts, 0, alerts_count * sizeof(alerts_t)); alerts_count = 0; result = mysql_store_result(&mysql); // fill alerts list while ((row = mysql_fetch_row(result)) != NULL) { alerts = realloc(alerts, (alerts_count + 1) * sizeof(alerts_t)); memset(&alerts[alerts_count], 0, sizeof(alerts_t)); if (row[0]) alerts[alerts_count].id = atoi(row[0]); if (row[1]) alerts[alerts_count].alert_type = alert_get_alert_type(row[1]); if (row[2]) { alerts[alerts_count].alert_count_type = alert_get_alert_count_type(row[2]); } else { alerts[alerts_count].alert_count_type = 1; } if (row[3]) alerts[alerts_count].check_type = alert_get_check_type(row[3], 0); if (row[4]) strcpy(alerts[alerts_count].check_data, row[4]); if (row[5]) alerts[alerts_count].value_at_alert = atof(row[5]); if (row[6]) alerts[alerts_count].alert_if_less = atof(row[6]); if (row[7]) alerts[alerts_count].alert_if_more = atof(row[7]); if (row[8]) alerts[alerts_count].value_at_clear = atof(row[8]); if (row[9]) alerts[alerts_count].clear_if_less = atof(row[9]); if (row[10]) alerts[alerts_count].clear_if_more = atof(row[10]); if (row[11]) alerts[alerts_count].ignore_if_calls_less = atol(row[11]); if (row[12]) alerts[alerts_count].ignore_if_calls_more = atol(row[12]); if (row[13]) if (strcmp("enabled", row[13]) == 0) alerts[alerts_count].status = 1; if (row[14]) alerts[alerts_count].action_alert_email = atoi(row[14]); if (row[15]) alerts[alerts_count].action_alert_sms = atoi(row[15]); if (row[16]) alerts[alerts_count].action_alert_disable_object = atoi(row[16]); if (row[17]) alerts[alerts_count].action_alert_change_rg_id = atoi(row[17]); if (row[18]) alerts[alerts_count].action_clear_email = atoi(row[18]); if (row[19]) alerts[alerts_count].action_clear_sms = atoi(row[19]); if (row[20]) alerts[alerts_count].action_clear_enable_object = atoi(row[20]); if (row[21]) alerts[alerts_count].action_clear_change_rg_id = atoi(row[21]); if (row[22]) alerts[alerts_count].before_alert_original_rg_id = atoi(row[22]); if (row[23]) alerts[alerts_count].alert_groups_id = atoi(row[23]); if (row[24]) alerts[alerts_count].raw_period = atoi(row[24]); if (row[25]) alerts[alerts_count].disable_clear = atoi(row[25]); if (row[26]) alerts[alerts_count].owner_id = atoi(row[26]); if (row[27]) strcpy(alerts[alerts_count].comment, row[27]); if (row[28]) alerts[alerts_count].action_alert_disable_object_in_dp = atoi(row[28]); if (row[29]) alerts[alerts_count].action_clear_enable_object_in_dp = atoi(row[29]); if (row[30]) alerts[alerts_count].clear_period = atoll(row[30])*60; if (row[31]) strcpy(alerts[alerts_count].clear_date, row[31]); if (row[32]) alerts[alerts_count].notify_to_user = atoi(row[32]); if (row[33]) alerts[alerts_count].hgc = atoi(row[33]); if (row[34]) sprintf(alerts[alerts_count].alert_group_id_list, ",%s,", row[34]); if (row[35]) alerts[alerts_count].alert_if_more_than = atoi(row[35]); if (row[36]) alerts[alerts_count].clear_if_less_than = atoi(row[36]); if (row[37]) strcpy(alerts[alerts_count].name, row[37]); if (row[38]) strcpy(alerts[alerts_count].sms_alert_message, row[38]); if (row[39]) strcpy(alerts[alerts_count].sms_clear_message, row[39]); if (row[40]) alerts[alerts_count].action_alert_disable_dp_in_rg = atoi(row[40]); if (row[41]) alerts[alerts_count].action_clear_enable_dp_in_rg = atoi(row[41]); if (row[42]) alerts[alerts_count].dial_peer_id = atoi(row[42]); if (row[43]) alerts[alerts_count].check_type_secondary = alert_get_check_type(row[43], 1); if (row[44]) strcpy(alerts[alerts_count].check_data_secondary, row[44]); // get email data for alert owner int email_index = get_email_owner_index(alerts[alerts_count].owner_id); if (email_index == -1) get_email_data(alerts[alerts_count].owner_id); if (alerts[alerts_count].raw_period > 0) { alerts[alerts_count].period = calculate_period(alerts[alerts_count].raw_period); } else { alerts[alerts_count].period = DATA_PACKETS - 1; // default 1 hour } // initial aggregated data alerts[alerts_count].data_info = malloc(sizeof(data_info_t)); memset(alerts[alerts_count].data_info, 0, sizeof(data_info_t)); alerts[alerts_count].data_info->id = -2; // -2 = empty, -1 = prefix alerts[alerts_count].data_info->secondary_id = -2; // -2 = empty, -1 = prefix alerts[alerts_count].data_info->user_id = -2; // -2 = empty, we need to save user_id to prevent SQL JOINS when we have just device ID alerts[alerts_count].data_info->data_count = 0; // number of aggregated data alerts[alerts_count].data_info->data_sum = 0; // sum of aggregated data alerts[alerts_count].data_info->alert_is_set = 0; // indication of alert status (1 = alerts is set, 0 - alert is not set) alerts[alerts_count].data_info->clear_period_countdown = 0; // initialization alerts[alerts_count].data_info->clear_period_counter = 0; // initialization alerts[alerts_count].data_info->next = NULL; // pointer to next data node (only when we have data = all otherwise there is only one node) if (alerts[alerts_count].check_type == 4) { // is check type = prefix? if so, we dont have an ID alerts[alerts_count].data_info->id = -1; // dont have ID alerts[alerts_count].data_info->secondary_id = -1; // dont have ID } else if (strcmp("all", alerts[alerts_count].check_data)) { // is this all? if not, lets take the ID alerts[alerts_count].data_info->id = atoi(row[4]); alerts[alerts_count].data_info->secondary_id = atoi(alerts[alerts_count].check_data_secondary); } alerts_count++; } mysql_free_result(result); // get new contacts for (i = 0; i < alerts_count; i++) { if (alerts[i].alert_groups_id > 0) if (get_contacts(&alerts[i])) return 1; } // get new schedules for (i = 0; i < alerts_count; i++) { if (alerts[i].alert_groups_id > 0) if (get_schedules(&alerts[i])) return 1; } // re-assgin data nodes to correct alert for (i = 0; i < alerts_count; i++) { for (j = 0; j < old_alerts_count; j++) { if (alerts[i].id == alerts_tmp[j].id) { alerts[i].data_info = alerts_tmp[j].addr; alerts_tmp[j].found = 1; alerts[i].first_uniqueid = alerts_tmp[j].first_uniqueid; } } } // free data nodes that do not have matching alert for (i = 0; i < old_alerts_count; i++) { if (alerts_tmp[i].found == 0) { data_info_t *current = alerts_tmp[i].addr, *next = NULL; while (current != NULL) { next = current->next; free(current); current = next; } } } m2_mysql_query("UPDATE conflines SET value = '0' WHERE name = 'alerts_need_update'"); free(alerts_tmp); show_alerts(); return 0; } /* Check if call is answered by comparing disposition */ int check_if_call_answered(const char *disposition) { int answered = -1; if (strcmp(disposition, "ANSWERED") == 0) answered = 1; if (strcmp(disposition, "BUSY") == 0) answered = 0; if (strcmp(disposition, "FAILED") == 0) answered = 0; if (strcmp(disposition, "NO ANSWER") == 0) answered = 0; if (answered != -1) { return answered; } else { m2_log("Can't determine disposition: %s\n", disposition); exit(1); } } /* Get calls from database */ int get_calls_data() { MYSQL_RES *result; MYSQL_ROW row; char sqlcmd[2048] = ""; unsigned long int calls_data_count = 0; calls_data_t *calls_data = NULL; sprintf(sqlcmd, "SELECT calls.id, user_id, dst_device_id, disposition, duration, billsec, " "call_details.pdd, calls.prefix, calls.src_device_id, destinationgroup_id, user_price, provider_price, " "hangupcause, users.ignore_global_alerts, tp_user.ignore_global_alerts FROM calls " "LEFT JOIN destinations ON destinations.prefix = calls.prefix " "LEFT JOIN users ON users.id = calls.user_id " "LEFT JOIN users AS tp_user ON tp_user.id = calls.provider_id " "LEFT JOIN call_details ON call_details.call_id = calls.id WHERE calls.id > %llu", last_call_id); if (m2_mysql_query(sqlcmd)) { return 0; } // lets create new node for current calls batch calls_data = malloc(sizeof(calls_data_t)); memset(calls_data, 0, sizeof(calls_data_t)); result = mysql_store_result(&mysql); // fill this node while ((row = mysql_fetch_row(result)) != NULL) { calls_data = realloc(calls_data, (calls_data_count + 1) * sizeof(calls_data_t)); memset(&calls_data[calls_data_count], 0, sizeof(calls_data_t)); if (row[1]) calls_data[calls_data_count].user_id = atoi(row[1]); if (row[2]) calls_data[calls_data_count].tp_id = atoi(row[2]); if (row[3]) calls_data[calls_data_count].answered = check_if_call_answered(row[3]); if (row[4]) calls_data[calls_data_count].duration = atol(row[4]); if (row[5]) calls_data[calls_data_count].billsec = atol(row[5]); if (row[6]) calls_data[calls_data_count].pdd = atof(row[6]); else calls_data[calls_data_count].pdd = -1; if (row[7]) strcpy(calls_data[calls_data_count].prefix, row[7]); if (row[8]) calls_data[calls_data_count].op_id = atoi(row[8]); if (row[9]) calls_data[calls_data_count].destinationgroup_id = atoi(row[9]); if (row[10]) calls_data[calls_data_count].user_price = atof(row[10]); if (row[11]) calls_data[calls_data_count].tp_user_price = atof(row[11]); if (row[12]) calls_data[calls_data_count].hgc = atoi(row[12]); if (row[13]) calls_data[calls_data_count].ignore_alert = atoi(row[13]); if (row[14]) calls_data[calls_data_count].tp_ignore_alert = atoi(row[14]); calls_data[calls_data_count].all = 1; // m2_log("CALL: answered = %d, prefix = %s, op_id = %u, user_id: %d\n", calls_data[calls_data_count].answered, calls_data[calls_data_count].prefix, calls_data[calls_data_count].op_id, calls_data[calls_data_count].user_id); calls_data_count++; if (atoll(row[0]) > last_call_id) { last_call_id = atoll(row[0]); } } mysql_free_result(result); int last_user_id = 0; unsigned long int active_calls_data_count = calls_data_count; // get active calls sprintf(sqlcmd, "SELECT user_id, count(activecalls.id) as active_calls FROM activecalls " "WHERE activecalls.active = 1 " "GROUP BY user_id " "ORDER BY user_id, active_calls DESC"); if (m2_mysql_query(sqlcmd)) { free(calls_data); return 1; } result = mysql_store_result(&mysql); while ((row = mysql_fetch_row(result)) != NULL) { if (row[0]) { if (last_user_id != atoi(row[0])) { if (atoi(row[1]) > 1) { calls_data = realloc(calls_data, (active_calls_data_count + 1) * sizeof(calls_data_t)); memset(&calls_data[active_calls_data_count], 0, sizeof(calls_data_t)); if (row[0]) calls_data[active_calls_data_count].user_id = atoi(row[0]); if (row[1]) calls_data[active_calls_data_count].user_sim_calls = atoi(row[1]); calls_data[active_calls_data_count].all = 1; last_user_id = calls_data[active_calls_data_count].user_id; active_calls_data_count++; } } } } mysql_free_result(result); time_t uniqueid = time(NULL); char file_buffer[256] = ""; sprintf(file_buffer, "/tmp/m2/alerts/%lu", uniqueid); FILE *fp = fopen(file_buffer, "w"); if (fp == NULL) return 1; unsigned long long int i = 0; for (i = 0; i < calls_data_count; i++) { char line_buffer[512] = ""; sprintf(line_buffer, "%i,%i,%i,%li,%li,%.3f,%s,%i,%i,%f,%f,%i,%i,%i\n", calls_data[i].user_id, calls_data[i].tp_id, calls_data[i].answered, calls_data[i].duration, calls_data[i].billsec, calls_data[i].pdd, strlen(calls_data[i].prefix) > 0 ? calls_data[i].prefix : "x", calls_data[i].op_id, calls_data[i].destinationgroup_id, calls_data[i].user_price, calls_data[i].tp_user_price, calls_data[i].hgc, calls_data[i].ignore_alert, calls_data[i].tp_ignore_alert); fprintf(fp, line_buffer); } fprintf(fp, "-- ACTIVE CALLS --\n"); for (i = calls_data_count; i < active_calls_data_count; i++) { char line_buffer[512] = ""; sprintf(line_buffer, "%i,%i\n", calls_data[i].user_id, calls_data[i].user_sim_calls); fprintf(fp, line_buffer); } fclose(fp); free(calls_data); // now remove file, containing the very last period if (&calls[DATA_PACKETS - 1]) { if (calls[DATA_PACKETS - 1].uniqueid) { char tmp_buffer[128] = ""; sprintf(tmp_buffer, "rm -fr /tmp/m2/alerts/%u", calls[DATA_PACKETS - 1].uniqueid); system(tmp_buffer); } } // shift memory because we need to hold data that fits a particular period // lets say we have this set: [1, 2, 3, 4, 5] (period is 5 nodes) // now we have: [1, 2, 3, 4, empty] // move memory memmove(&calls[1], &calls[0], ((DATA_PACKETS) - 1) * sizeof(calls_index_t)); // now we have: [1, 1, 2, 3, 4] // assign new node calls[0].uniqueid = uniqueid; // now we have: [new, 1, 2, 3, 4] // memory is shifted! this was very fast return 0; } /* Get last call id from calls table */ unsigned long long int mysql_last_call_id() { MYSQL_RES *result; MYSQL_ROW row; unsigned long long int last_call_id = 0; // we need last ID to get only new calls at every call update // lets say last call id is 1542 then next time we will get calls that have id > 1542 if (m2_mysql_query("SELECT id FROM calls ORDER BY calldate DESC LIMIT 1")) { return 0; } result = mysql_store_result(&mysql); if (result) { if (mysql_num_rows(result)) { row = mysql_fetch_row(result); if (row[0]) last_call_id = atoll(row[0]); } } mysql_free_result(result); m2_log("Last call ID: %llu\n", last_call_id); return last_call_id; } /* Function that gets target id based on check type (target id = user id, op id, tp id, destinationgroup id from ALERTS) */ long int get_target_id(alerts_t *alert, int secondary) { char check_data[60] = ""; int check_type = alert->check_type; int data_id = alert->data_info->id; strcpy(check_data, alert->check_data); if (secondary) { strcpy(check_data, alert->check_data_secondary); check_type = alert->check_type_secondary; data_id = alert->data_info->secondary_id; } if (check_type == 1) { if (strcmp("all", check_data) == 0) { return 1; // return 1, this indicates all users } else { return data_id; // return real ID } } if (check_type == 2) { if (strcmp("all", check_data) == 0) { return 1; // return 1, this indicates all tp } else { return data_id; // return real ID } } if (check_type == 3) { if (strcmp("all", check_data) == 0) { return 1; // return 1, this indicates all op } else { return data_id; // return real ID } } if (check_type == 4) return 0; // this is prefix, so we dont care abount ID, checking will be different if (check_type == 5) { if (strcmp("all", check_data) == 0) { return 1; // return 1, this indicates all dg } else { return data_id; // return real ID } } return -1; } /* Function that gets offset based on check type (offset is calculated for op_user id, op id, tp id, destinationgroup id, tp_user id from CALLS) */ int get_check_id_offset(alerts_t *alert, int secondary) { int check_type = alert->check_type; char check_data[100] = ""; if (secondary) { check_type = alert->check_type_secondary; strcpy(check_data, alert->check_data_secondary); } else { strcpy(check_data, alert->check_data); } // calls data structure is as follows: // int user_id; // int tp_id; // int op_id; // int all; // int destinationgroup_id; // offset starts at user_id (offset = 0) // if offset is 1 then we check tp_id and so on if (check_type == 1) { if (strcmp(check_data, "all") == 0) { return 3; // mark that this record will be used for all users } else { return 0; // just user ID } } if (check_type == 2) { if (strcmp(check_data, "all") == 0) return 3; else return 1; // just tp ID } if (check_type == 3) { if (strcmp(check_data, "all") == 0) return 3; return 2; // op ID } if (check_type == 4) return 0; // user ID (but we dont care, since this is prefix and the checking will be different) if (check_type == 5) { if (strcmp(check_data, "all") == 0) return 3; return 4; // destinationgroup ID } return 0; } /* Function is used to find a particular data node by traversing the aggregated nodes and comparing IDs */ data_info_t *get_data_address(int id, int user_id, alerts_t *alert, int ignore_alert, int update, int secondary, long int secondary_id) { // !!!!!!!!!! // taget = user/op/tp/destination_group // !!!!!!!!!! data_info_t *current = alert->data_info; data_info_t *last = NULL; while (current) { int matches = 0; if (secondary) { matches = (id == current->id) && (secondary_id == current->secondary_id) ? 1 : 0; } else { matches = id == current->id ? 1 : 0; } if (debug) { m2_log("Checking primary id %ld and secondary id %ld\n", current->id, current->secondary_id); } if (matches) { // does current node have the same ID as target from calls record? (id == target_id) if (alert->check_type == 1 || alert->check_type == 2 || alert->check_type == 3) { current->user_id = user_id; // set user_id } else { current->user_id = 0; } if ((alert->check_type == 1 || alert->check_type == 2 || alert->check_type == 3) && update) { current->ignore_alert = ignore_alert; } else { current->ignore_alert = 0; } if (debug) { m2_log("Object found! Object id: %d\n", id); } return current; // return pointer tho this node } last = current; current = current->next; } if (debug) { m2_log("New object! Adding new object to alerts data. Object id: %d, secondary_id: %ld\n", id, secondary_id); } // we did not find target so we assume this is a new target so we should track him // create new node for this target data_info_t *new_data = NULL; new_data = malloc(sizeof(data_info_t)); // fill data new_data->id = id; if (secondary) { new_data->secondary_id = secondary_id; } if (alert->check_type == 1 || alert->check_type == 2 || alert->check_type == 3) { new_data->user_id = user_id; } else { new_data->user_id = 0; } new_data->data_sum = 0; new_data->data_count = 0; new_data->alert_is_set = 0; new_data->clear_period_countdown = 0; new_data->clear_period_counter = 0; new_data->user_rg_id = 0; if ((alert->check_type == 1 || alert->check_type == 2 || alert->check_type == 3) && update) { new_data->ignore_alert = ignore_alert; } else { new_data->ignore_alert = 0; } new_data->next = NULL; last->next = new_data; // return pointer to this node return new_data; } /* Function checks if user searching is needed */ int get_search(alerts_t *alert, int secondary) { char check_data[60] = ""; if (secondary) { strcpy(check_data, alert->check_data_secondary); } else { strcpy(check_data, alert->check_data); } if (strcmp("all", check_data) == 0) { return 1; // search } else { return 0; // do not search, use first node } } /* Function that updates every node by aggregating data with current calls data */ int update_alert_data(alerts_t *alert, int update, int index) { // if update = 1 nodes will be updated with current data // if update = 0 old data will removed from nodes if (!&calls[index]) { if (!calls[index].uniqueid) { return 0; } } uint i = 0; int target_id = 0; // check data id from alerts int target_id_secondary = 0; // check data id from alerts double value = 0; // this value will be added to aggregated data unsigned int count = 0; // if count = 1, node will updated int offset = 0; // offset to particular ID in calls packet int offset_secondary = 0; // offset to particular ID in calls packet int can_pass = 0; // call record can pass int search = 0; // indicated that we need to search for users int search_secondary = 0; // indicated that we need to search for users int alert_type = alert->alert_type; int check_type = alert->check_type; int check_type_secondary = alert->check_type_secondary; calls_data_t *calls_data = NULL; unsigned long long int calls_data_count = 0; unsigned long long int active_calls_data_count = 0; // gets offset, id and search indicator target_id = get_target_id(alert, 0); offset = get_check_id_offset(alert, 0); search = get_search(alert, 0); // secondary object if (check_type_secondary) { target_id_secondary = get_target_id(alert, 1); offset_secondary = get_check_id_offset(alert, 1); search_secondary = get_search(alert, 1); } // current calls packet doesn't have any calls, return // if (calls[index].count == 0 && calls[index].a_count == 0) goto sim_check; // lets create new node for current calls batch calls_data = malloc(sizeof(calls_data_t)); memset(calls_data, 0, sizeof(calls_data_t)); char file_buffer[256] = ""; sprintf(file_buffer, "/tmp/m2/alerts/%u", calls[index].uniqueid); char *line = NULL; size_t len = 0; ssize_t read; int continue_reading = 1; FILE *fp = fopen(file_buffer, "r"); if (fp == NULL) return 0; while ((read = getline(&line, &len, fp)) != -1) { if (strcmp(line, "-- ACTIVE CALLS --\n") != 0 && continue_reading == 1) { calls_data = realloc(calls_data, (calls_data_count + 1) * sizeof(calls_data_t)); memset(&calls_data[calls_data_count], 0, sizeof(calls_data_t)); calls_data[calls_data_count].all = 1; char * pch; pch = strtok(line, ","); int tok_count = 1; while (pch != NULL) { if (tok_count == 1) calls_data[calls_data_count].user_id = atoi(pch); if (tok_count == 2) calls_data[calls_data_count].tp_id = atoi(pch); if (tok_count == 3) calls_data[calls_data_count].answered = atoi(pch); if (tok_count == 4) calls_data[calls_data_count].duration = atol(pch); if (tok_count == 5) calls_data[calls_data_count].billsec = atol(pch); if (tok_count == 6) calls_data[calls_data_count].pdd = atof(pch); else calls_data[calls_data_count].pdd = -1; if (tok_count == 7) strcpy(calls_data[calls_data_count].prefix, pch); if (tok_count == 8) calls_data[calls_data_count].op_id = atoi(pch); if (tok_count == 9) calls_data[calls_data_count].destinationgroup_id = atoi(pch); if (tok_count == 10) calls_data[calls_data_count].user_price = atof(pch); if (tok_count == 11) calls_data[calls_data_count].tp_user_price = atof(pch); if (tok_count == 12) calls_data[calls_data_count].hgc = atoi(pch); if (tok_count == 13) calls_data[calls_data_count].ignore_alert = atoi(pch); if (tok_count == 14) calls_data[calls_data_count].tp_ignore_alert = atoi(pch); tok_count++; pch = strtok(NULL, ","); } // printf("answered = %d, prefix = %s op_id = %u\n", calls_data[calls_data_count].answered, calls_data[calls_data_count].prefix, calls_data[calls_data_count].op_id); calls_data_count++; } else { // skip first line if (continue_reading) { active_calls_data_count = calls_data_count; continue_reading = 0; continue; } calls_data = realloc(calls_data, (active_calls_data_count + 1) * sizeof(calls_data_t)); memset(&calls_data[active_calls_data_count], 0, sizeof(calls_data_t)); calls_data[active_calls_data_count].all = 1; calls_data[active_calls_data_count].all = 1; char * pch; pch = strtok(line, ","); int tok_count = 1; while (pch != NULL) { if (tok_count == 1) calls_data[active_calls_data_count].user_id = atoi(pch); if (tok_count == 2) calls_data[active_calls_data_count].user_sim_calls = atoi(pch); tok_count++; pch = strtok(NULL, ","); } // printf("user_id = %d, sim_calls = %u\n", calls_data[active_calls_data_count].user_id, calls_data[active_calls_data_count].user_sim_calls); active_calls_data_count++; } } if (line) free(line); fclose(fp); if (alert_type == 10) goto sim_check; // check ever call record for current calls packet for (i = 0; i < calls_data_count; i++) { // default values value = 0; count = 0; can_pass = 0; // if this is not prefix if (check_type != 4) { // use offset and target id to check if current call record can pass for this alert if (*(&calls_data[i].user_id + offset) == target_id) { can_pass = 1; } // skip admin calls if (check_type == 1 && calls_data[i].user_id == 0) { can_pass = 0; } } else { // this is prefix so we do checking by comparing prefix string if (alert->check_data[strlen(alert->check_data) - 1] == '%') { if (strncmp(alert->check_data, calls_data[i].prefix, strlen(alert->check_data) - 1) == 0) can_pass = 1; } else { if (strcmp(alert->check_data, calls_data[i].prefix) == 0) can_pass = 1; } } // secondary object check if (check_type_secondary) { if (check_type_secondary != 4) { // use offset and target id to check if current call record can pass for this alert if (*(&calls_data[i].user_id + offset_secondary) != target_id_secondary) { can_pass = 0; } } else { // this is prefix so we do checking by comparing prefix string if (alert->check_data_secondary[strlen(alert->check_data_secondary) - 1] == '%') { if (strncmp(alert->check_data_secondary, calls_data[i].prefix, strlen(alert->check_data_secondary) - 1) != 0) can_pass = 0; } else { if (strcmp(alert->check_data_secondary, calls_data[i].prefix) != 0) can_pass = 0; } } } if (can_pass) { if (debug) { m2_log("PASS! Alert[%d]: OFFSET: %d, CHECK: %d, TARGET: %d, OFFSET_SECONDARY: %d, CHECK SECONDARY: %d, TARGET SECONDARY: %d, user_id: %d, tp_id: %d, answered: %d, duration: %ld, billsec: %ld, pdd: %.3f, prefix: %s, " "op_id: %d, destinationgroup_id: %d, user_price: %.3f, tp_user_price: %.3f, hgc: %d, alert_if_more: %.3f, alert_if_less: %.3f, " "clear_if_more: %.3f, clear_if_less: %.3f\n", alert->id, offset, *(&calls_data[i].user_id + offset), target_id, offset_secondary, check_type_secondary == 0 ? 0 : *(&calls_data[i].user_id + offset_secondary), target_id_secondary, calls_data[i].user_id, calls_data[i].tp_id, calls_data[i].answered, calls_data[i].duration, calls_data[i].billsec, calls_data[i].pdd, calls_data[i].prefix, calls_data[i].op_id, calls_data[i].destinationgroup_id, calls_data[i].user_price, calls_data[i].tp_user_price, calls_data[i].hgc, alert->alert_if_more, alert->alert_if_less, alert->clear_if_more, alert->clear_if_less); } // check only parameter specified by alert type if (alert_type == 1) { value = 1; count = 1; } else if (alert_type == 2) { if (calls_data[i].answered) value = 1; count = 1; } else if (alert_type == 3) { if (!calls_data[i].answered) value = 1; count = 1; } else if (alert_type == 4) { if (calls_data[i].answered) value = 1; count = 1; } else if (alert_type == 5) { if (calls_data[i].answered) { value = calls_data[i].billsec; count = 1; } } else if (alert_type == 6) { if (calls_data[i].answered) { if (calls_data[i].pdd > -1) { // if pdd = -1, sipchaninfo is disabled value = calls_data[i].pdd; count = 1; } } } else if (alert_type == 7) { if (calls_data[i].answered) { value = calls_data[i].duration - calls_data[i].billsec; count = 1; } } else if (alert_type == 8) { if (calls_data[i].answered) { value = calls_data[i].billsec; count = 1; } } else if (alert_type == 9) { if (calls_data[i].answered) { if (check_type == 1 || check_type == 3) { value = calls_data[i].user_price; } else if (check_type == 2) { value = calls_data[i].tp_user_price; } count = 1; } } else if (alert_type == 11) { if (alert->hgc >= 0) { if (calls_data[i].hgc == alert->hgc) { value = 1; count = 1; } } } else if (alert_type == 12) { if (alert->hgc >= 0) { if (calls_data[i].hgc == alert->hgc) { value = 1; } count = 1; } } } else { if (debug) { m2_log("FAIL! Alert[%d]: OFFSET: %d, CHECK: %d, TARGET: %d, OFFSET_SECONDARY: %d, CHECK SECONDARY: %d, TARGET SECONDARY: %d, user_id: %d, tp_id: %d, answered: %d, duration: %ld, billsec: %ld, pdd: %.3f, prefix: %s, " "op_id: %d, destinationgroup_id: %d, user_price: %.3f, tp_user_price: %.3f, hgc: %d, alert_if_more: %.3f, alert_if_less: %.3f, " "clear_if_more: %.3f, clear_if_less: %.3f\n", alert->id, offset, *(&calls_data[i].user_id + offset), target_id, offset_secondary, check_type_secondary == 0 ? 0 : *(&calls_data[i].user_id + offset_secondary), target_id_secondary, calls_data[i].user_id, calls_data[i].tp_id, calls_data[i].answered, calls_data[i].duration, calls_data[i].billsec, calls_data[i].pdd, calls_data[i].prefix, calls_data[i].op_id, calls_data[i].destinationgroup_id, calls_data[i].user_price, calls_data[i].tp_user_price, calls_data[i].hgc, alert->alert_if_more, alert->alert_if_less, alert->clear_if_more, alert->clear_if_less); } } // count is not 0 so we need to add this value to aggregated data and increment current count if (count) { data_info_t *data_node = NULL; // search for particular node if (search || search_secondary) { long int primary_id = 0; long int secondary_id = 0; int user_id = 0; int ignore_alert = 0; if (alert->check_type == 1 && calls_data[i].user_id > 0) { primary_id = calls_data[i].user_id; user_id = calls_data[i].user_id; ignore_alert = calls_data[i].ignore_alert; } else if (alert->check_type == 2 && calls_data[i].tp_id > 0) { primary_id = calls_data[i].tp_id; user_id = calls_data[i].tp_user_id; ignore_alert = calls_data[i].tp_ignore_alert; } else if (alert->check_type == 3 && calls_data[i].op_id > 0) { primary_id = calls_data[i].op_id; user_id = calls_data[i].user_id; ignore_alert = calls_data[i].ignore_alert; } else if (alert->check_type == 5 && calls_data[i].destinationgroup_id > 0) { primary_id = calls_data[i].destinationgroup_id; } if (search_secondary) { if (alert->check_type_secondary == 1 && calls_data[i].user_id > 0) { secondary_id = calls_data[i].user_id; } else if (alert->check_type_secondary == 2 && calls_data[i].tp_id > 0) { secondary_id = calls_data[i].tp_id; } else if (alert->check_type_secondary == 3 && calls_data[i].op_id > 0) { secondary_id = calls_data[i].op_id; } else if (alert->check_type_secondary == 5 && calls_data[i].destinationgroup_id > 0) { secondary_id = calls_data[i].destinationgroup_id; } } data_node = get_data_address(primary_id, user_id, alert, ignore_alert, update, search_secondary, secondary_id); } else { alert->data_info->user_id = calls_data[i].user_id; data_node = alert->data_info; } if (data_node != NULL) { // update node if (update) { data_node->data_sum += value; data_node->data_count += count; } else { if (!(data_node->clear_period_countdown > 0)) { data_node->data_sum -= value; data_node->data_count -= count; } } } } } sim_check: if (alert_type == 10) { // if call ends, record will not be in activecalls table // so we set all sim call record state to default - 0 sim calls // later, we will assign a real value of current sim calls data_info_t *data = alert->data_info; while (data) { data->data_sum = 0; data->data_count = 0; data = data->next; } // we set sim calls only when updating values // if we are deleting old data, then we just set default values and return if (!update) { free(calls_data); return 0; } for (i = calls_data_count; i < active_calls_data_count; i++) { // check if call record users matches alert target (by id or by type) if (*(&calls_data[i].user_id + offset) == target_id) { data_info_t *data_node = NULL; // search for particular target if (search) { if (alert->check_type == 1 && calls_data[i].user_id > 0) { data_node = get_data_address(calls_data[i].user_id, 0, alert, calls_data[i].ignore_alert, update, search_secondary, *(&calls_data[i].user_id + offset_secondary)); } else if (alert->check_type == 2 && calls_data[i].tp_id > 0) { data_node = get_data_address(calls_data[i].tp_id, calls_data[i].tp_user_id, alert, calls_data[i].tp_ignore_alert, update, search_secondary, *(&calls_data[i].user_id + offset_secondary)); } else if (alert->check_type == 3 && calls_data[i].op_id > 0) { data_node = get_data_address(calls_data[i].op_id, calls_data[i].user_id, alert, calls_data[i].ignore_alert, update, search_secondary, *(&calls_data[i].user_id + offset_secondary)); } else if (alert->check_type == 4 && calls_data[i].destinationgroup_id > 0) { data_node = get_data_address(calls_data[i].destinationgroup_id, 0, alert, 0, update, search_secondary, *(&calls_data[i].user_id + offset_secondary)); } } else { alert->data_info->user_id = calls_data[i].user_id; data_node = alert->data_info; } if (data_node != NULL) { // updating to current sim calls if (update) { data_node->data_sum = (float)calls_data[i].user_sim_calls; data_node->data_count = calls_data[i].user_sim_calls; } } } } } free(calls_data); return 0; } /* Update alerts with current calls packet */ int update_data(int add) { // alerts will be updated in chunks every second // if we have 5 chunks, than alerts data will be updated in 5 seconds int chunk_num = DATA_AGGREGATE_PERIODS; // adjust chunk count for small number of alerts if (alerts_count < chunk_num) chunk_num = alerts_count; // claculate chunk size (alerts are divided equaly into x number of chunks) int chunk_size = (int)ceil((double)alerts_count / (double)chunk_num); int chunk_start = 0, chunk_end; chunk_end = chunk_size; uint i = 0; for (i = 0; i < chunk_num; i++) { // update every chunk uint j = 0; // printf("update %d %d %ld %d\n", chunk_start, chunk_end, alerts_count, add); // update every alert in that chunk for (j = chunk_start; j < chunk_end; j++) { if (add == 0 && alerts[j].first_uniqueid == 0) { continue; } if (add == 1 && alerts[j].first_uniqueid == 0) { alerts[j].first_uniqueid = calls[0].uniqueid; } // index to calls array int index = 0; if (add == 0) { index = alerts[j].period; } if (add == 0) { if (&calls[index]) { if (calls[index].uniqueid) { if (alerts[j].first_uniqueid > calls[index].uniqueid) { continue; } } } } if (alerts[j].alert_type != 13) { if (update_alert_data(&alerts[j], add, index)) return 1; } } chunk_start += chunk_size; chunk_end += chunk_size; if (chunk_end >= alerts_count) chunk_end = alerts_count; // little pause to free cpu from work and let do other things sleep(1); } if (chunk_num < DATA_AGGREGATE_PERIODS) { for (i = 0; i < DATA_AGGREGATE_PERIODS - chunk_num; i++) { sleep(1); } } return 0; } /* Function that aggregates data based on alert type */ double calculate_aggregated_data(int alert_type, double data_sum, long int data_count) { double new_data_sum = data_sum; if (data_count != 0) { if (alert_type == 4 || alert_type == 12) new_data_sum = (data_sum / data_count) * 100; // asr if (alert_type == 5) new_data_sum = data_sum / data_count; // acd if (alert_type == 6) new_data_sum = data_sum / data_count; // average pdd if (alert_type == 7) new_data_sum = data_sum / data_count; // average ttc } else { new_data_sum = 0; } return new_data_sum; } /* Get hour and minutes from time string like '22:18' */ void get_schedule_time(char *strtime, int *hour, int *min) { char buff[3] = ""; strncpy(buff, strtime, 2); *hour = atoi(buff); strncpy(buff, strtime + 3, 2); *min = atoi(buff); } /* Action sends emails by schedule */ int action_alert_email(alerts_t *alert, data_info_t *data, int report, char *cause, double data_sum, long int data_count) { alert_action_log("Send email to alert group", report, alert->id); // we don't have group? return if (alert->alert_groups_id < 1) { m2_log("Group id is < 1. Alert will not be reported\n"); email_action_log(0, "", 0, "Group id is < 1. Alert will not be reported"); return 0; } // get owner email data int email_index = get_email_owner_index(alert->owner_id); // we don't have properly configured email? return if (email[email_index].enabled == 0 || email_index == -1) { char email_error[512] = ""; sprintf(email_error, "Emails are disabled for owner with id: %d. Email will not be sent", alert->owner_id); m2_log("%s\n", email_error); email_action_log(0, "", 0, email_error); if (email_index >= 0) m2_log("Email data: owner_id: %d, server: %s, login: %s, from: %s, port: %d\n", alert->owner_id, email[email_index].server, email[email_index].login, email[email_index].from, email[email_index].port); return 0; } char alertstr[32] = "CLEAR"; char subject[1024] = ""; char body[10650] = ""; char email_to[256] = ""; char object_type[128] = ""; char object_name[256] = ""; char secondary_object_type[128] = ""; char secondary_object_name[128] = ""; char secondary_object_string[500] = ""; char object_url[600] = ""; char object_url_body[650] = ""; alert_get_check_type_string(alert->check_type, object_type); alert_get_object_name_string(alert, data->id, object_name, 0); if (alert->check_type_secondary) { alert_get_check_type_string(alert->check_type_secondary, secondary_object_type); alert_get_object_name_string(alert, data->secondary_id, secondary_object_name, 1); sprintf(secondary_object_string, "Secondary object Type: %s\nSecondary object: %s\nSecondary object name: %s\n", secondary_object_type, alert->check_data_secondary, secondary_object_name); } if (report) strcpy(alertstr, "ALERT"); alert_get_object_url(alert->check_type, object_url, data->id); if (strlen(object_url) > 0) { sprintf(object_url_body, "Object URL: %s\n", object_url); } sprintf(subject, "[Alert] %s for %s: %s", alert->name, object_type, object_name); m2_log("Action: Email, Alert: %d, Check type: %d, Alert ID: %d, Data ID: %ld, Group ID: %d, Owner ID: %d\n", report, alert->check_type, alert->id, data->id, alert->alert_groups_id, email[email_index].owner_id); // format email body sprintf(body, "Alert: %s\n\nReporting: %s\nParameter: %s\nGroup to notify: %s\nObject Type: %s\nObject: %s\nObject name: %s\n%s%sAlert ID: %u\n\n" "Date: %s\n\n" "Calls count: %ld\n" "Current value: %0.0f\n" "Alert if more: %0.0f\n" "Alert if less: %0.0f\n" "Clear if more: %0.0f\n" "Clear if less: %0.0f\n" "Ignore if calls more: %ld\n" "Ignore if calls less: %ld\n\nComment:\n\n%s\n", alert->name, alertstr, cause, alert->group->name == NULL ? "" : alert->group->name, object_type, alert->check_data, object_name, secondary_object_string, object_url_body, alert->id, date_str, data_count, data_sum, alert->alert_if_more, alert->alert_if_less, alert->clear_if_more, alert->clear_if_less, alert->ignore_if_calls_more, alert->ignore_if_calls_less, alert->comment); contact_t *contact = alert->group->contact; // get current minute int min = current_min; // check ever contact while (contact) { // shift time by timezone time_t shifted_linux_time = time(NULL) + (contact->timezone * 3600); struct tm shifted_current_time; gmtime_r(&shifted_linux_time, &shifted_current_time); int send_email = 0; int hour = shifted_current_time.tm_hour; int day = shifted_current_time.tm_wday; schedule_t *schedule = alert->group->schedule; // check if current time is in scheduled period while (schedule) { // printf("current_day = %d, day = %d\n", day, schedule->daytype); if (day == schedule->daytype || schedule->daytype == -1) { int start_hour = 0, start_min = 0; int end_hour = 0, end_min = 0; get_schedule_time(schedule->start, &start_hour, &start_min); get_schedule_time(schedule->end, &end_hour, &end_min); // printf("current_hour = %d, current_min = %d, current_day = %d\n", current_hour, current_min, current_day); // printf("shifted_hour = %d, shifted_min = %d, shifted_day = %d\n", hour, min, day); // printf("start_hour = %d, start_min = %d\n", start_hour, start_min); // printf("end_hour = %d, end_min = %d\n", end_hour, end_min); int calculated_time = hour * 3600 + min; int calculated_start_time = start_hour * 3600 + start_min; int calculated_end_time = end_hour * 3600 + end_min; if (calculated_time >= calculated_start_time && calculated_time < calculated_end_time) send_email = 1; // this period is OK, send email! } schedule = schedule->prev; } // should we send email? if (send_email) { char response[256] = ""; strcpy(email_to, contact->email); m2_log("Email to: %s\n", contact->email); if (m2_send_email(NULL, contact->email, subject, body, NULL, 0, 0, "alerts", email[email_index].owner_id, response)) { m2_log("Failed to send email: %s\n", response); email_action_log(0, contact->email, 0, response); m2_log("Emails will be temporary disabled for owner: %d\n", email[email_index].owner_id); email[email_index].enabled = 0; } else { m2_log("Email was sent successfully\n"); email_action_log(0, contact->email, 1, ""); } } else { email_action_log(0, contact->email, 0, "Email will no be sent. Check schedule settings"); m2_log("Email will no be sent. Check schedule settings\n"); } contact = contact->prev; } return 0; } /* Action sends sms by schedule */ int action_alert_sms(alerts_t *alert, data_info_t *data, int report, char *cause, double data_sum, long int data_count) { alert_action_log("Send SMS to alert group", report, alert->id); // we don't have group? return if (alert->alert_groups_id < 1) { m2_log("Group id is < 1. Alert will not be reported\n"); sms_action_log(0, "", 0, "Group id is < 1. Alert will not be reported"); return 0; } char sms_message[3000] = ""; if (report) { strcpy(sms_message, alert->sms_alert_message); } else { strcpy(sms_message, alert->sms_clear_message); } if (!strlen(sms_message)) { m2_log("SMS text is empty\n"); sms_action_log(0, "", 0, "SMS text is empty"); return 0; } CURL *curl = curl_easy_init(); if (curl) { char *encoded_sms_message = curl_easy_escape(curl, sms_message, strlen(sms_message)); if (encoded_sms_message) { m2_log("Encoded SMS message: %s\n", encoded_sms_message); strcpy(sms_message, encoded_sms_message); curl_free(encoded_sms_message); } else { m2_log("Encoded SMS text is empty\n"); sms_action_log(0, "", 0, "Encoded SMS text is empty"); return 0; } } // get current minute int min = current_min; contact_t *contact = alert->group->contact; // check ever contact while (contact) { // shift time by timezone time_t shifted_linux_time = time(NULL) + (contact->timezone * 3600); struct tm shifted_current_time; gmtime_r(&shifted_linux_time, &shifted_current_time); int send_sms = 0; int hour = shifted_current_time.tm_hour; int day = shifted_current_time.tm_wday; schedule_t *schedule = alert->group->schedule; // check if current time is in scheduled period while (schedule) { if (day == schedule->daytype || schedule->daytype == -1) { int start_hour = 0, start_min = 0; int end_hour = 0, end_min = 0; get_schedule_time(schedule->start, &start_hour, &start_min); get_schedule_time(schedule->end, &end_hour, &end_min); int calculated_time = hour * 3600 + min; int calculated_start_time = start_hour * 3600 + start_min; int calculated_end_time = end_hour * 3600 + end_min; if (calculated_time >= calculated_start_time && calculated_time < calculated_end_time) send_sms = 1; // this period is OK, send sms! } schedule = schedule->prev; } // should we send sms? if (send_sms) { char sms_to[50] = ""; char response[256] = ""; strcpy(sms_to, contact->number); char sms_cmd[5000] = ""; if (!strlen(sms_to)) { m2_log("Number is empty, SMS will not be sent\n"); sms_action_log(0, "", 0, "Number is empty, SMS will not be sent"); } m2_log("SMS to: %s\n", sms_to); sprintf(sms_cmd, "/usr/bin/curl -XGET \"%s?from=%s&to=%s&msg=%s\"", SMS_API_URL, SMS_FROM, sms_to, sms_message); m2_log("%s\n", sms_cmd); FILE *pipe = popen(sms_cmd, "r"); if (pipe != NULL) { fgets(response, 255, pipe); pclose(pipe); if (strstr(response, "\"status\":\"200\"")) { m2_log("SMS was sent successfully\n"); sms_action_log(0, contact->number, 1, ""); } else { m2_log("Failed to send SMS: %s\n", response); sms_action_log(0, contact->number, 0, response); } } else { m2_log("Failed to run send SMS command\n"); sms_action_log(0, contact->number, 0, "Failed to run send sms command"); } } else { sms_action_log(0, contact->number, 0, "SMS will no be sent. Check schedule settings"); m2_log("SMS will no be sent. Check schedule settings\n"); } contact = contact->prev; } return 0; } /* Send email to user */ int action_alert_email_to_user(alerts_t *alert, data_info_t *data, int report, char *cause, double data_sum, long int data_count) { MYSQL_RES *result; MYSQL_ROW row; alert_action_log("Send email to user", report, alert->id); // applies only to users/tp/op if (alert->check_type != 1 && alert->check_type != 2 && alert->check_type != 3) return 0; // get owner email data int email_index = get_email_owner_index(alert->owner_id); // we don't have properly configured email? return if (email[email_index].enabled == 0 || email_index == -1) { char email_error[512] = ""; sprintf(email_error, "Emails are disabled for owner with id: %d. Email will not be sent", alert->owner_id); m2_log("%s\n", email_error); email_action_log(data->id, "", 0, email_error); if (email_index >= 0) m2_log("Email data: owner_id: %d, server: %s, login: %s, from: %s, port: %d\n", alert->owner_id, email[email_index].server, email[email_index].login, email[email_index].from, email[email_index].port); return 0; } char alertstr[32] = "CLEAR"; char subject[1024] = "Alert notification"; char body[10000] = ""; char email_to[256] = ""; char object_type[128] = ""; char object_name[256] = ""; char secondary_object_type[256] = ""; char secondary_object_name[256] = ""; char secondary_object_string[1000] = ""; int user_id = data->id; alert_get_check_type_string(alert->check_type, object_type); alert_get_object_name_string(alert, data->id, object_name, 0); if (alert->check_type_secondary) { alert_get_check_type_string(alert->check_type_secondary, secondary_object_type); alert_get_object_name_string(alert, data->secondary_id, secondary_object_name, 1); sprintf(secondary_object_string, "Secondary object Type: %s\nSecondary object: %s\nSecondary object name: %s\n", secondary_object_type, alert->check_data_secondary, secondary_object_name); } sprintf(subject, "[Alert] %s for %s: %s", alert->name, object_type, object_name); if (report) strcpy(alertstr, "ALERT"); m2_log("Action: Email to user, Alert: %d, Check type: %d, Alert ID: %d, Data ID: %ld, Owner ID: %d\n", report, alert->check_type, alert->id, data->id, email[email_index].owner_id); // format email body sprintf(body, "Alert: %s\n\nReporting: %s\nParameter: %s\nObject Type: %s\nObject: %s\nObject name: %s\n%sAlert ID: %u\n\n" "Date: %s\n\n" "Current value: %0.0f\n" "Data count: %ld\n" "Alert if more: %0.0f\n" "Alert if less: %0.0f\n" "Clear if more: %0.0f\n" "Clear if less: %0.0f\n" "Ignore if calls more: %ld\n" "Ignore if calls less: %ld\n\nComment:\n\n%s\n", alert->name, alertstr, cause, object_type, alert->check_data, object_name, secondary_object_string, alert->id, date_str, data_sum, data_count, alert->alert_if_more, alert->alert_if_less, alert->clear_if_more, alert->clear_if_less, alert->ignore_if_calls_more, alert->ignore_if_calls_less, alert->comment); char email_query[1024] = ""; char user_email[256] = ""; if (alert->check_type == 1) { sprintf(email_query, "SELECT noc_email, main_email, billing_email, rates_email, NULL, users.id FROM users JOIN addresses ON addresses.id = users.address_id WHERE users.id = %li LIMIT 1", data->id); } else { sprintf(email_query, "SELECT noc_email, main_email, billing_email, rates_email, NULL, users.id FROM users JOIN addresses ON addresses.id = users.address_id JOIN devices ON devices.user_id = users.id WHERE devices.id = %li LIMIT 1", data->id); } if (m2_mysql_query(email_query)) { return 1; } result = mysql_store_result(&mysql); // fill alerts list while ((row = mysql_fetch_row(result)) != NULL) { if (row[5]) user_id = atoi(row[5]); if (row[4]) if (strlen(row[4]) > 0) strcpy(user_email, row[4]); if (row[3]) if (strlen(row[3]) > 0) strcpy(user_email, row[3]); if (row[2]) if (strlen(row[2]) > 0) strcpy(user_email, row[2]); if (row[1]) if (strlen(row[1]) > 0) strcpy(user_email, row[1]); if (row[0]) if (strlen(row[0]) > 0) strcpy(user_email, row[0]); } if (strlen(user_email) > 0) { char response[2048] = ""; strcpy(email_to, user_email); m2_log("Email to: %s\n", email_to); if (m2_send_email(NULL, email_to, subject, body, NULL, 0, 0, "alerts", email[email_index].owner_id, response)) { m2_log("Failed to send email: %s\n", response); m2_log("Emails will be temporary disabled for owner: %d\n", email[email_index].owner_id); email_action_log(data->id, user_email, 0, response); email[email_index].enabled = 0; } else { m2_log("Email was sent successfully\n"); email_action_log(data->id, user_email, 1, ""); } } else { m2_log("Option 'notify to user' is enabled, but user's email is empty\n"); email_action_log(user_id, "", 0, "Option 'notify to user' is enabled, but user's email is empty"); } return 0; } /* Action disables user/op/tp */ int action_alert_disable_object(alerts_t *alert, data_info_t *data, int report) { // only applies to users, op, tp if (!(alert->check_type == 1 || alert->check_type == 2 || alert->check_type == 3)) return 0; // only if we have id if (data->id < 0) return 0; char target[256] = ""; char buffer[1024] = ""; int digit = 0; if (alert->check_type == 1) { strcpy(target, "user"); } if (alert->check_type == 2) { strcpy(target, "terminator"); } if (alert->check_type == 3) { strcpy(target, "originator"); } char alert_msg[512] = ""; sprintf(alert_msg, "%s %s (id %ld)", report == 1 ? "Disable" : "Enable", target, data->id); alert_action_log(alert_msg, report, alert->id); m2_log("Action: %s %s, alert_id: %d, %s_id: %ld\n", report == 1 ? "disable" : "enable", target, alert->id, target, data->id); // when reporting alert if (report) { digit = 1; } // for user if (alert->check_type == 1) { sprintf(buffer, "UPDATE users SET blocked = %d, changes_present = 1 WHERE id = %ld", digit, data->id); } // for op if (alert->check_type == 2) { sprintf(buffer, "UPDATE devices SET tp_active = %d, changes_present = 1 WHERE id = %ld", digit ^ 1, data->id); } // for tp if (alert->check_type == 3) { sprintf(buffer, "UPDATE devices SET op_active = %d, changes_present = 1 WHERE id = %ld", digit ^ 1, data->id); } if (m2_mysql_query(buffer)) { return 1; } return 0; } /* Action changes RG for originator */ int action_alert_change_rg(alerts_t *alert, data_info_t *data, int report) { MYSQL_RES *result; MYSQL_ROW row; // only for originators if (alert->check_type != 3) return 0; char alert_msg[512] = ""; sprintf(alert_msg, "Change routing group for originator (id %ld), rg id: %d, restore rg id: %d", data->id, alert->action_alert_change_rg_id, alert->action_clear_change_rg_id); alert_action_log(alert_msg, report, alert->id); m2_log("Action: change routing group, alert_id: %d, originator_id: %ld, user_id: %ld, change_rg_id: %d, restore_rg_id: %d\n", alert->id, data->id, data->user_id, alert->action_alert_change_rg_id, alert->action_clear_change_rg_id); char buffer[1024] = ""; int target_rg_id = 0; if (report) { // save current user rg so we can later restore user's rg to this rg (restore original user rg) char sqlcmd[2048] = ""; sprintf(sqlcmd, "SELECT op_routing_group_id FROM devices WHERE id = %ld LIMIT 1", data->id); if (m2_mysql_query(sqlcmd)) return 1; result = mysql_store_result(&mysql); if (result) { if (mysql_num_rows(result)) { row = mysql_fetch_row(result); if (row[0]) data->user_rg_id = atoi(row[0]); } } mysql_free_result(result); if (data->user_rg_id < 1 && alert->action_clear_change_rg_id == -1) { m2_log("Restore routing group option is enabled, but can't determine current user routing group, which will be restored. routing group will not be changed\n"); return 0; } target_rg_id = alert->action_alert_change_rg_id; } else { target_rg_id = alert->action_clear_change_rg_id; if (target_rg_id == -1) { if (data->user_rg_id > 0) { target_rg_id = data->user_rg_id; } else { m2_log("Restore routing group option is enabled, but original routing group id is 0. Something went wrong\n"); alert_action_log("Restore routing group option is enabled, but original routing group id is 0. Something went wrong", report, alert->id); return 0; } } } if (target_rg_id) { sprintf(buffer, "UPDATE devices SET op_routing_group_id = %d, changes_present = 1 WHERE id = %ld", target_rg_id, data->id); if (m2_mysql_query(buffer)) { return 1; } } else { m2_log("Routing group id = 0. Originator's rg_id will not be changed\n"); alert_action_log("Routing group id = 0. Originator's rg_id will not be changed", report, alert->id); } return 0; } /* Action disable object in dial peer */ int action_alert_disable_object_in_dp(alerts_t *alert, data_info_t *data, int report) { // only for terminators if (alert->check_type != 2) return 0; char alert_msg[512] = ""; sprintf(alert_msg, "%s terminator (id %ld) in dial peer (id %d)", report == 1 ? "Disable" : "Enable", data->id, alert->action_alert_disable_object_in_dp); alert_action_log(alert_msg, report, alert->id); m2_log("Action: %s terminator in dial peer, alert_id: %d, terminator_id: %ld, dialpeer_id: %d\n", report == 1 ? "disable" : "enable", alert->id, data->id, alert->action_alert_disable_object_in_dp); char mysql_buffer[1024] = ""; char mysql_buffer2[1024] = ""; if (report) { sprintf(mysql_buffer, "UPDATE dpeer_tpoints SET active = 0 WHERE dial_peer_id = %d AND device_id = %ld", alert->action_alert_disable_object_in_dp, data->id); } else { sprintf(mysql_buffer, "UPDATE dpeer_tpoints SET active = 1 WHERE dial_peer_id = %d AND device_id = %ld", alert->action_alert_disable_object_in_dp, data->id); } sprintf(mysql_buffer2, "UPDATE dial_peers SET changes_present = 1 WHERE id = %d", alert->action_alert_disable_object_in_dp); if (m2_mysql_query(mysql_buffer)) { return 1; } if (mysql_affected_rows(&mysql) == 0) { return 2; } if (m2_mysql_query(mysql_buffer2)) { return 1; } return 0; } /* Action disable dial peer in routing group */ int action_alert_disable_dp_in_rg(alerts_t *alert, data_info_t *data, int report) { char alert_msg[512] = ""; int op_routing_group_id = 0; // only for originators if (alert->check_type != 3) return 0; op_routing_group_id = get_op_routing_group_id(data->id); if (op_routing_group_id == 0) { m2_log("OP routing group id is 0!\n"); return 1; } sprintf(alert_msg, "%s dial peer (id %d) in routing group (id %d)", report == 1 ? "Disable" : "Enable", alert->dial_peer_id, op_routing_group_id); alert_action_log(alert_msg, report, alert->id); m2_log("Action: %s dial peer in routing group, alert_id: %d, routing group id: %d, dialpeer_id: %d\n", report == 1 ? "disable" : "enable", alert->id, op_routing_group_id, alert->dial_peer_id); char mysql_buffer[1024] = ""; if (report) { sprintf(mysql_buffer, "UPDATE rgroup_dpeers SET active = 0 WHERE dial_peer_id = %d AND routing_group_id = %d", alert->dial_peer_id, op_routing_group_id); } else { sprintf(mysql_buffer, "UPDATE rgroup_dpeers SET active = 1 WHERE dial_peer_id = %d AND routing_group_id = %d", alert->dial_peer_id, op_routing_group_id); } if (m2_mysql_query(mysql_buffer)) { return 1; } sprintf(mysql_buffer, "UPDATE routing_groups SET changes_present = 1 WHERE id = %d", op_routing_group_id); if (m2_mysql_query(mysql_buffer)) { return 1; } return 0; } /* Report about alert and take defined action */ int report_alert(alerts_t *alert, data_info_t *data, int report) { // we do not report alert if it is not cleared in the first place if (report == 1 && data->alert_is_set == 1) return 0; // we do not clear alert if if is not reported in the first place if (report == 0 && data->alert_is_set == 0) return 0; char cause[128] = "UNKNOWN"; // default value char alertstr[32] = "CLEAR"; // default value double data_sum = calculate_aggregated_data(alert->alert_type, data->data_sum, data->data_count); long int data_count = data->data_count; int status = 0; if (report) strcpy(alertstr, "ALERT"); // get alert type string from integer value if (alert->alert_type == 1) { strcpy(cause, "CALLS TOTAL"); } else if (alert->alert_type == 2) { strcpy(cause, "CALLS ANSWERED"); } else if (alert->alert_type == 3) { strcpy(cause, "CALLS NOT ASWERED"); } else if (alert->alert_type == 4) { strcpy(cause, "ASR"); } else if (alert->alert_type == 5) { strcpy(cause, "ACD"); } else if (alert->alert_type == 6) { strcpy(cause, "PDD"); } else if (alert->alert_type == 7) { strcpy(cause, "TCC"); } else if (alert->alert_type == 8) { strcpy(cause, "BILLSEC SUM"); } else if (alert->alert_type == 9) { strcpy(cause, "PRICE SUM"); } else if (alert->alert_type == 10) { strcpy(cause, "SIMULTANEOUS CALLS"); } else if (alert->alert_type == 11) { strcpy(cause, "HANGUPCAUSE ABSOLUTE"); } else if (alert->alert_type == 12) { strcpy(cause, "HANGUPCAUSE PERCENT"); } else if (alert->alert_type == 13) { strcpy(cause, "GROUP"); } if (!((alert->check_type == 1 || alert->check_type == 2 || alert->check_type == 3) && data->ignore_alert)) { // take action if (report && alert->action_alert_email) if (action_alert_email(alert, data, report, cause, data_sum, data_count)) return 1; if (!report && alert->action_clear_email) if (action_alert_email(alert, data, report, cause, data_sum, data_count)) return 1; if (report && alert->action_alert_sms) if (action_alert_sms(alert, data, report, cause, data_sum, data_count)) return 1; if (!report && alert->action_clear_sms) if (action_alert_sms(alert, data, report, cause, data_sum, data_count)) return 1; if (report && alert->notify_to_user) if (action_alert_email_to_user(alert, data, report, cause, data_sum, data_count)) return 1; if (!report && alert->notify_to_user) if (action_alert_email_to_user(alert, data, report, cause, data_sum, data_count)) return 1; if (report && alert->action_alert_disable_object) if (action_alert_disable_object(alert, data, report)) return 1; if (!report && alert->action_clear_enable_object) if (action_alert_disable_object(alert, data, report)) return 1; if (report && alert->action_alert_change_rg_id) if (action_alert_change_rg(alert, data, report)) return 1; if (!report && alert->action_clear_change_rg_id) if (action_alert_change_rg(alert, data, report)) return 1; if (report && alert->action_alert_disable_object_in_dp) status = action_alert_disable_object_in_dp(alert, data, report); if (!report && alert->action_clear_enable_object_in_dp) status = action_alert_disable_object_in_dp(alert, data, report); if (report && alert->action_alert_disable_dp_in_rg) action_alert_disable_dp_in_rg(alert, data, report); if (!report && alert->action_clear_enable_dp_in_rg) action_alert_disable_dp_in_rg(alert, data, report); } // this is only used with tp disabling in rg // if we can not find tp in specified rg, action should be created to inform user if (status == 1) { return 1; } else if (status == 2) { // just to disable spaming data->alert_is_set = 1; char mysql_buffer[1024] = ""; sprintf(mysql_buffer, "INSERT INTO actions(action, data, data2, date) VALUES('alert_warning', 'Alert id: %d', 'Terminator (id: %ld) not found in Dial peer (id: %d)', NOW())", alert->id, data->id, alert->action_alert_disable_object_in_dp); if (m2_mysql_query(mysql_buffer)) { return 1; } m2_log("Terminator was not found in selected Dial peer\n"); return 0; } if (report) { data->alert_is_set = 1;\ if (!((alert->check_type == 1 || alert->check_type == 2 || alert->check_type == 3) && data->ignore_alert)) { char mysql_buffer[1024] = ""; sprintf(mysql_buffer, "UPDATE alerts set value_at_alert = %.5f, value_at_clear = 0, alert_raised_at = NOW(), alert_cleared_at = 0 WHERE id = %d", data_sum, alert->id); if (m2_mysql_query(mysql_buffer)) { return 1; } } } else { data->alert_is_set = 0; if (!((alert->check_type == 1 || alert->check_type == 2 || alert->check_type == 3) && data->ignore_alert)) { char mysql_buffer[1024] = ""; sprintf(mysql_buffer, "UPDATE alerts set value_at_alert = 0, value_at_clear = %.5f, alert_raised_at = 0, alert_cleared_at = NOW() WHERE id = %d", data_sum, alert->id); if (m2_mysql_query(mysql_buffer)) { return 1; } } } data->clear_period_counter = 0; global_alert_is_changed = 1; if (!((alert->check_type == 1 || alert->check_type == 2 || alert->check_type == 3) && data->ignore_alert)) { // log this alert m2_log("Reporting [%s] for [%s] to group id [%u], data ID [%ld], alert id [%u], " "current value: %0.0f, " "data count: %ld, " "alert if more: %0.0f, " "alert if less: %0.0f, " "clear if more: %0.0f, " "clear if less: %0.0f, " "ignore if calls more: %ld, " "ignore if calls less: %ld, " "group alert if more: %d, " "group clear if less: %d\n", alertstr, cause, alert->alert_groups_id, data->id == -1 ? atol(alert->check_data) : data->id, alert->id, data_sum, data_count, alert->alert_if_more, alert->alert_if_less, alert->clear_if_more, alert->clear_if_less, alert->ignore_if_calls_more, alert->ignore_if_calls_less, alert->alert_if_more_than, alert->clear_if_less_than); } else { char target[128] = "user"; if (alert->check_type == 2) strcpy(target, "terminator"); if (alert->check_type == 3) strcpy(target, "originator"); if (report) { m2_log("Alert is triggered, but will be ignored. Alerts are disabled for %s_id = %ld\n", target, data->id); } else { m2_log("Clear is triggered, but will be ignored. Alerts are disabled for %s_id = %ld\n", target, data->id); } } return 0; } int check_current_alert(alerts_t *alert) { data_info_t *current = alert->data_info; while (current) { // skip empty objects (id = -2) if (current->id != -2) { if (debug) { m2_log("Alert check #1: checking target: %ld, data sum = %.0f, data count = %llu, alert_is_set: %d\n", current->id, current->data_sum, current->data_count, current->alert_is_set); } long int data_count = current->data_count; double data_sum = calculate_aggregated_data(alert->alert_type, current->data_sum, current->data_count); if (debug) { m2_log("alert_id = %d\n", alert->id); m2_log("current_value = %.2f\n", data_sum); m2_log("current_count = %ld\n", data_count); m2_log("alert if more = %.2f\n", alert->alert_if_more); m2_log("alert if less = %.2f\n", alert->alert_if_less); m2_log("clear if more = %.2f\n", alert->clear_if_more); m2_log("clear if less = %.2f\n", alert->clear_if_less); } // check if we need to clear alert if (current->alert_is_set && !alert->disable_clear && alert->clear_period == 0) { if (alert->clear_if_more > alert->clear_if_less) { if (data_sum >= alert->clear_if_more) if (report_alert(alert, current, 0)) return 1; } else { if (data_sum <= alert->clear_if_less) if (report_alert(alert, current, 0)) return 1; } } // check if we should ignore this alert if ((data_count >= alert->ignore_if_calls_more) && (alert->ignore_if_calls_more != 0)) goto skip_this; if (data_count <= alert->ignore_if_calls_less && alert->ignore_if_calls_less != 0) goto skip_this; // check if we need to set alert if (!current->alert_is_set) { if (alert->alert_if_more > alert->alert_if_less) { if (data_sum >= alert->alert_if_more) if (report_alert(alert, current, 1)) return 1; } else { if (data_sum <= alert->alert_if_less) if (report_alert(alert, current, 1)) return 1; } } } skip_this: current = current->next; } return 0; } int check_current_alert_group(alerts_t *alert) { data_info_t *data_node_1 = NULL; data_info_t *data_node_2 = NULL; int checked_data[500] = { 0 }; int checked_data_count = 0; int data_sum = 0; int data_count = 0; int i = 0, j = 0, k = 0; char alert_id_buffer[64] = ""; m2_log("Alert group check for alert_id: %u\n", alert->id); for (i = 0; i < alerts_count; i++) { if (alerts[i].id != alert->id) { sprintf(alert_id_buffer, ",%d,", alerts[i].id); if (strstr(alert->alert_group_id_list, alert_id_buffer)) { if (debug) { m2_log("Checking #1 alert %d in this alert group\n", alerts[i].id); } data_node_1 = alerts[i].data_info; data_sum = 0; data_count = 0; while (data_node_1) { if (data_node_1->id < -1) goto skip_data; if (checked_data_count < 500) { for (k = 0; k < checked_data_count; k++) { if (checked_data[k] == data_node_1->id) goto skip_data; } checked_data[checked_data_count] = data_node_1->id; checked_data_count++; } data_count++; if (data_node_1->alert_is_set) data_sum++; for (j = 0; j < alerts_count; j++) { if (alerts[j].id != alert->id && alerts[j].id != alerts[i].id) { sprintf(alert_id_buffer, ",%d,", alerts[j].id); if (strstr(alert->alert_group_id_list, alert_id_buffer)) { data_node_2 = alerts[j].data_info; while (data_node_2) { if (alerts->check_type == 4) { int min_len1 = strlen(alerts[j].check_data); int min_len2 = strlen(alerts[i].check_data); int min_len3 = strlen(alert->check_data); int min_len = min_len1; if (alerts[j].check_data[strlen(alerts[j].check_data) - 1] == '%') min_len1--; if (alerts[i].check_data[strlen(alerts[i].check_data) - 1] == '%') min_len2--; if (alerts->check_data[strlen(alerts->check_data) - 1] == '%') min_len3--; if (min_len2 < min_len) { min_len = min_len2; } if (min_len3 < min_len) { min_len = min_len3; } if (strncmp(alerts[j].check_data, alerts->check_data, min_len) == 0) { if (strncmp(alerts[j].check_data, alerts->check_data, min_len) == 0) { data_count++; if (data_node_2->alert_is_set) data_sum++; } } } else { if (data_node_1->id == data_node_2->id) { data_count++; if (data_node_2->alert_is_set) data_sum++; } } // m2_log("data_node_1_id: %ld, data_node_2_id: %ld, is_set_1: %d, is_set_2: %d\n", data_node_1->id, data_node_2->id, data_node_1->alert_is_set, data_node_2->alert_is_set); data_node_2 = data_node_2->next; } } } } if (debug) { if (alerts->check_type != 4) { m2_log("data_id: %ld, data_count: %d, data_sum: %d\n", data_node_1->id, data_count, data_sum); } else { m2_log("data: %s, data_count: %d, data_sum: %d\n", alert->check_data, data_count, data_sum); } } // update data data_info_t *data_node = NULL; // search for particular node data_node = get_data_address(data_node_1->id, 0, alert, 0, 0, 0, 0); if (data_node != NULL) { data_node->data_sum = data_sum; data_node->data_count = data_count; } skip_data: data_node_1 = data_node_1->next; } } } } // check if alerts need to be reported data_info_t *current = alert->data_info; while (current) { data_sum = current->data_sum; // check if we need to clear alert if (current->alert_is_set && !alert->disable_clear && alert->clear_period == 0) { if (data_sum < alert->clear_if_less_than) if (report_alert(alert, current, 0)) return 1; } // check if we need to set alert if (!current->alert_is_set) { if (data_sum > alert->alert_if_more_than) if (report_alert(alert, current, 1)) return 1; } current = current->next; } return 0; } int check_current_alert_diff(alerts_t *alert) { alert->diff_counter++; if (alert->diff_counter >= (alert->period + 1)) { } return 0; } int check_alerts() { int chunk_num = DATA_AGGREGATE_PERIODS; if (alerts_count < chunk_num) chunk_num = alerts_count; int chunk_size = (int)ceil((double)alerts_count / (double)chunk_num); int chunk_start = 0, chunk_end; int chunk_group_start = 0, chunk_group_end; chunk_end = chunk_size; chunk_group_end = chunk_size; uint i = 0; int alert_group_used = 0; // update every record for (i = 0; i < chunk_num; i++) { uint j = 0; // update each record // printf("check %d %d %ld\n", chunk_start, chunk_end, alerts_count); for (j = chunk_start; j < chunk_end; j++) { if (alerts[j].status == 1) { if (alerts[j].alert_count_type == 1) { if (alerts[j].alert_type != 13) { if (check_current_alert(&alerts[j])) { m2_log("Error while checking alerts [%d]\n", j); return 1; } } } else if (alerts[j].alert_count_type == 2) { if (check_current_alert_diff(&alerts[j])) { m2_log("Error while checking alerts diff [%d]\n", j); return 1; } } } } chunk_start += chunk_size; chunk_end += chunk_size; if (chunk_end >= alerts_count) chunk_end = alerts_count; sleep(1); } // once again for alert groups // because first we need to check normal alerts and see if any of then have been alerted/cleared // if we have atleast one, then check alert groups // update every record if (global_alert_is_changed) { for (i = 0; i < chunk_num; i++) { uint j = 0; // update each record // printf("check %d %d %ld\n", chunk_group_start, chunk_end, alerts_count); for (j = chunk_group_start; j < chunk_group_end; j++) { if (alerts[j].status == 1) { if (alerts[j].alert_count_type == 1) { if (alerts[j].alert_type == 13) { m2_log("Checking alert groups\n"); alert_group_used = 1; if (check_current_alert_group(&alerts[j])) { m2_log("Error while checking alerts [%d]\n", j); return 1; } } } } } chunk_group_start += chunk_size; chunk_group_end += chunk_size; if (chunk_group_end >= alerts_count) chunk_group_end = alerts_count; } } if (chunk_num < DATA_AGGREGATE_PERIODS) { for (i = 0; i < DATA_AGGREGATE_PERIODS - chunk_num; i++) { sleep(1); } } // for alert groups, reset variable that show if alert status changed if (alert_group_used) { global_alert_is_changed = 0; } return 0; } int update_email_details() { MYSQL_RES *result; MYSQL_ROW row; char sqlcmd[2048] = ""; int i = 0; for (i = 0; i < email_count; i++) { sprintf(sqlcmd, "SELECT (SELECT value FROM conflines WHERE owner_id = %d AND name = 'Email_Smtp_Server'), " "(SELECT value FROM conflines WHERE owner_id = %d AND name = 'Email_Login'), " "(SELECT value FROM conflines WHERE owner_id = %d AND name = 'Email_Password'), " "(SELECT value FROM conflines WHERE owner_id = %d AND name = 'Email_from'), " "(SELECT value FROM conflines WHERE owner_id = %d AND name = 'Email_port')", email[i].owner_id, email[i].owner_id, email[i].owner_id, email[i].owner_id, email[i].owner_id); if (m2_mysql_query(sqlcmd)) { return 0; } result = mysql_store_result(&mysql); if (result == NULL) { m2_log("MySQL error: %s\n", mysql_error(&mysql)); m2_log("MySQL query: %s\n", sqlcmd); return 1; } if ((row = mysql_fetch_row(result)) == NULL) { m2_log("MySQL returned an empty result set\n"); m2_log("MySQL query: %s\n", sqlcmd); return 1; } if (row[0] && row[1] && row[2] && row[3] && row[4]) { email = realloc(email, (i + 1) * sizeof(email_data_t)); strcpy(email[i].server, row[0]); strcpy(email[i].login, row[1]); strcpy(email[i].password, row[2]); strcpy(email[i].from, row[3]); email[i].port = atoi(row[4]); email[i].enabled = 1; if (!strlen(email[i].server)) { m2_log("Email server is empty\n"); email[i].enabled = 0; } if (!strlen(email[i].from)) { m2_log("Email from is empty\n"); email[i].enabled = 0; } if (!email[i].port) { m2_log("Email port is empty\n"); email[i].enabled = 0; } if (email[i].enabled == 0) m2_log("Emails are disabled for owner with id: %d\n", email[i].owner_id); m2_log("Email data updated: owner_id: %d, server: %s, login: %s, from: %s, port: %d\n", email[i].owner_id, email[i].server, email[i].login, email[i].from, email[i].port); } else { m2_log("Emails are disabled for owner with id: %d. No email data found\n", email[i].owner_id); email[i].enabled = 0; } mysql_free_result(result); } return 0; } int get_email_data(int owner_id) { MYSQL_RES *result; MYSQL_ROW row; char sqlcmd[2048] = ""; sprintf(sqlcmd, "SELECT (SELECT value FROM conflines WHERE owner_id = %d AND name = 'Email_Smtp_Server'), " "(SELECT value FROM conflines WHERE owner_id = %d AND name = 'Email_Login'), " "(SELECT value FROM conflines WHERE owner_id = %d AND name = 'Email_Password'), " "(SELECT value FROM conflines WHERE owner_id = %d AND name = 'Email_from'), " "(SELECT value FROM conflines WHERE owner_id = %d AND name = 'Email_port')", owner_id, owner_id, owner_id, owner_id, owner_id); if (m2_mysql_query(sqlcmd)) { return 1; } result = mysql_store_result(&mysql); if (result == NULL) { m2_log("MySQL error: %s\n", mysql_error(&mysql)); m2_log("MySQL query: %s\n", sqlcmd); return 1; } if ((row = mysql_fetch_row(result)) == NULL) { m2_log("MySQL returned an empty result set\n"); m2_log("MySQL query: %s\n", sqlcmd); return 1; } if (row[0] && row[1] && row[2] && row[3] && row[4]) { email = realloc(email, (email_count + 1) * sizeof(email_data_t)); strcpy(email[email_count].server, row[0]); strcpy(email[email_count].login, row[1]); strcpy(email[email_count].password, row[2]); strcpy(email[email_count].from, row[3]); email[email_count].port = atoi(row[4]); email[email_count].enabled = 1; email[email_count].owner_id = owner_id; if (!strlen(email[email_count].server)) { m2_log("Email server is empty\n"); email[email_count].enabled = 0; } if (!strlen(email[email_count].from)) { m2_log("Email from is empty\n"); email[email_count].enabled = 0; } if (!email[email_count].port) { m2_log("Email port is empty\n"); email[email_count].enabled = 0; } if (email[email_count].enabled == 0) m2_log("Emails are disabled for owner with id: %d\n", owner_id); m2_log("Email data: owner_id: %d, server: %s, login: %s, from: %s, port: %d\n", owner_id, email[email_count].server, email[email_count].login, email[email_count].from, email[email_count].port); email_count++; } mysql_free_result(result); return 0; } int get_contacts(alerts_t *alert) { MYSQL_RES *result; MYSQL_ROW row; if (alert->alert_groups_id == 0) return 0; char sqlcmd[2048] = ""; sprintf(sqlcmd, "SELECT A.id, A.email, A.timezone, A.phone_number, C.email_schedule_id, C.sms_schedule_id, C.name FROM alert_contacts AS A " "INNER JOIN alert_contact_groups AS B ON A.id = B.alert_contact_id " "INNER JOIN alert_groups AS C ON C.id = B.alert_group_id " "WHERE B.alert_group_id = %d AND A.status = 1 AND C.status = 1", alert->alert_groups_id); if (m2_mysql_query(sqlcmd)) { return 1; } group_t *group = malloc(sizeof(group_t)); alert->group = group; group->alert_id = alert->id; contact_t *prev_contact = NULL; contact_t *current_contact = NULL; result = mysql_store_result(&mysql); // fill alerts list while ((row = mysql_fetch_row(result)) != NULL) { current_contact = malloc(sizeof(contact_t)); memset(current_contact, 0, sizeof(contact_t)); current_contact->prev = prev_contact; if (row[0]) current_contact->id = atoi(row[0]); if (row[1]) strcpy(current_contact->email, row[1]); if (row[2]) current_contact->timezone = atoi(row[2]); if (row[3]) strcpy(current_contact->number, row[3]); if (row[4]) group->email_schedule_id = atoi(row[4]); if (row[5]) group->sms_schedule_id = atoi(row[5]); if (row[6]) strcpy(group->name, row[6]); prev_contact = current_contact; } group->contact = current_contact; mysql_free_result(result); return 0; } int get_daytype(char *daytype) { if (strcmp("all days", daytype) == 0) return -1; if (strcmp("monday", daytype) == 0) return 1; if (strcmp("tuesday", daytype) == 0) return 2; if (strcmp("wednesday", daytype) == 0) return 3; if (strcmp("thursday", daytype) == 0) return 4; if (strcmp("friday", daytype) == 0) return 5; if (strcmp("saturday", daytype) == 0) return 6; if (strcmp("sunday", daytype) == 0) return 0; return -2; } int get_schedules(alerts_t *alert) { MYSQL_RES *result; MYSQL_ROW row; if (alert->alert_groups_id == 0) return 0; char sqlcmd[2048] = ""; sprintf(sqlcmd, "SELECT A.day_type, A.start, A.end FROM alert_schedule_periods AS A " "INNER JOIN alert_schedules AS B ON A.alert_schedule_id = B.id " "WHERE B.id = %d AND B.status = 1", alert->group->email_schedule_id); if (m2_mysql_query(sqlcmd)) { return 1; } schedule_t *prev_schedule = NULL; schedule_t *current_schedule = NULL; result = mysql_store_result(&mysql); // fill alerts list while ((row = mysql_fetch_row(result)) != NULL) { current_schedule = malloc(sizeof(schedule_t)); memset(current_schedule, 0, sizeof(schedule_t)); current_schedule->prev = prev_schedule; if (row[0]) current_schedule->daytype = get_daytype(row[0]); if (row[1]) strcpy(current_schedule->start, row[1]); if (row[2]) strcpy(current_schedule->end, row[2]); prev_schedule = current_schedule; } alert->group->schedule = current_schedule; mysql_free_result(result); return 0; } int before_start() { MYSQL_RES *result; MYSQL_ROW row; int count = 0; if (m2_mysql_query("SELECT COUNT(*) FROM conflines WHERE name = 'alerts_need_update'")) { return 1; } result = mysql_store_result(&mysql); if (result) { if (mysql_num_rows(result)) { row = mysql_fetch_row(result); if (row[0]) count = atoi(row[0]); } } mysql_free_result(result); if (count < 1) { if (m2_mysql_query("INSERT INTO conflines(name, value) VALUES('alerts_need_update', '1')")) { return 1; } } // reset alert status on start up if (m2_mysql_query("UPDATE alerts SET " "alert_raised_at = NULL, " "alert_cleared_at = NULL, " "value_at_clear = 0, " "value_at_alert = 0")) { return 1; } return 0; } int get_email_owner_index(int owner_id) { int index = -1; int i; for (i = 0; i < email_count; i++) { if (email[i].owner_id == owner_id) return i; } return index; } void _increment_clear_period(alerts_t *alert) { data_info_t *current = alert->data_info; while (current) { if (current->clear_period_countdown > 0) current->clear_period_countdown -= DATA_TICK_TIME; if (current->alert_is_set == 1) { // is alert set for current data? if (current->clear_period_counter >= alert->clear_period) { current->clear_period_counter = 0; report_alert(alert, current, 0); } current->clear_period_counter += DATA_TICK_TIME; // increment clear timer } current = current->next; } } void increment_clear_period() { int i = 0; for (i = 0; i < alerts_count; i++) { if (alerts[i].clear_period > 0 && !alerts[i].disable_clear) { _increment_clear_period(&alerts[i]); } } } /* Set clear on specific date */ void check_clear_date() { int i = 0; for (i = 0; i < alerts_count; i++) { if (strlen(alerts[i].clear_date) && !alerts[i].disable_clear) { int reported = 0; if (m2_compare_dates(date_str, alerts[i].clear_date)) { reported = 1; data_info_t *current = alerts[i].data_info; while (current) { if (current->alert_is_set == 1) { // is alert set for current data? if (m2_compare_dates(date_str, alerts[i].clear_date)) { current->clear_period_counter = 0; current->clear_period_countdown = (alerts[i].raw_period * 60) + 60; current->data_count = 0; current->data_sum = 0; report_alert(&alerts[i], current, 0); } } current = current->next; } } if (reported) { strcpy(alerts[i].clear_date, ""); char sql_buffer[1024] = ""; sprintf(sql_buffer, "UPDATE alerts SET clear_on_date = NULL WHERE id = %u", alerts[i].id); m2_mysql_query(sql_buffer); } } } } /* Show alert status for current time period */ void show_alerts_status() { int i = 0; int show_title = 1; for (i = 0; i < alerts_count; i++) { data_info_t *current = alerts[i].data_info; char target[128] = "-------"; char target2[128] = ""; char sum_type[128] = "-----------"; char sum_type2[128] = ""; char count_type[128] = "-----------"; if (alerts[i].check_type == 1) strcpy(target, "User id"); if (alerts[i].check_type == 2) strcpy(target, "TP id "); if (alerts[i].check_type == 3) strcpy(target, "OP id "); if (alerts[i].check_type == 4) strcpy(target, "DST id "); if (alerts[i].check_type == 5) strcpy(target, "DG id "); if (alerts[i].check_type == 6) strcpy(target, "System "); if (alerts[i].alert_type == 1) { strcpy(sum_type, "Total calls"); strcpy(count_type, "Total calls "); strcpy(sum_type2, "Total calls"); } if (alerts[i].alert_type == 2) { strcpy(sum_type, "Answered "); strcpy(count_type, "Answered calls"); strcpy(sum_type2, "Total answered calls"); } if (alerts[i].alert_type == 3) { strcpy(sum_type, "Failed "); strcpy(count_type, "Failed calls "); strcpy(sum_type2, "Total failed calls"); } if (alerts[i].alert_type == 4) { strcpy(sum_type, "ASR "); strcpy(count_type, "Total calls "); strcpy(sum_type2, "ASR"); } if (alerts[i].alert_type == 5) { strcpy(sum_type, "ACD "); strcpy(count_type, "Answered calls"); strcpy(sum_type2, "ACD"); } if (alerts[i].alert_type == 6) { strcpy(sum_type, "PDD "); strcpy(count_type, "Answered calls"); strcpy(sum_type2, "PDD"); } if (alerts[i].alert_type == 7) { strcpy(sum_type, "TTC "); strcpy(count_type, "Answered calls"); strcpy(sum_type2, "TTC"); } if (alerts[i].alert_type == 8) { strcpy(sum_type, "BILLSEC "); strcpy(count_type, "Answered calls"); strcpy(sum_type2, "Total billsec"); } if (alerts[i].alert_type == 9) { strcpy(sum_type, "PRICE "); strcpy(count_type, "Answered calls"); strcpy(sum_type2, "Total price"); } if (alerts[i].alert_type == 10) { strcpy(sum_type, "SIM CALLS "); strcpy(count_type, "Total calls "); strcpy(sum_type2, "Simultaneous calls"); } if (alerts[i].alert_type == 11) { strcpy(sum_type, "HGC ABS "); strcpy(count_type, "Total HGCs "); strcpy(sum_type2, "HGC absolute"); } if (alerts[i].alert_type == 12) { strcpy(sum_type, "HGC PERCENT"); strcpy(count_type, "Total HGCs "); strcpy(sum_type2, "HGC percent"); } if (alerts[i].alert_type == 13) { strcpy(sum_type, "GROUP "); strcpy(count_type, "Active alerts "); strcpy(sum_type2, "Group"); } alert_get_check_type_string(alerts[i].check_type, target2); int show_header = 1; while (current) { if ((current->id > 0 || alerts[i].check_type == 6) && current->data_count > 0) { if (show_title) { m2_log("\n\n########### CURRENT ALERT STATUS ###########\n\n"); show_title = 0; } if (show_header) { m2_log("Alert id: %d\n", alerts[i].id); m2_log("Alert type: %s\n", sum_type2); m2_log("Data check type: %s\n", target2); m2_log("Period: %d\n", alerts[i].raw_period); m2_log("+---------+----------------+-------------+---------------+---------------+----------------+----------------+\n"); m2_log("| %s | %s | %s | Alert if more | Alert if less | Ignore if more | Ignore if less |\n", target, count_type, sum_type); m2_log("+---------+----------------+-------------+---------------+---------------+----------------+----------------+\n"); show_header = 0; } double data_sum = calculate_aggregated_data(alerts[i].alert_type, current->data_sum, current->data_count); m2_log("| %-7ld | %-14llu | %-11.2f | %-13.2f | %-13.2f | %-14ld | %-14ld |\n", current->id, current->data_count, data_sum, alerts[i].alert_if_more, alerts[i].alert_if_less, alerts[i].ignore_if_calls_more, alerts[i].ignore_if_calls_less); m2_log("+---------+----------------+-------------+---------------+---------------+----------------+----------------+\n"); } current = current->next; } if (show_title == 0) { m2_log("\n"); m2_log("\n"); } } } // update time every sec void *update_time() { while (1) { time_t t; struct tm *tmp; t = time(NULL); tmp = localtime(&t); strftime(date_str, sizeof(date_str), DATE_FORMAT, tmp); time_t linux_time; struct tm *current_time; time(&linux_time); current_time = gmtime(&linux_time); current_hour = current_time->tm_hour; current_min = current_time->tm_min; current_day = current_time->tm_wday; sleep(1); } pthread_exit(NULL); } /* Create action log for email sending (failed and successful attempts) */ void email_action_log(int user_id, char *email, int status, char *error) { char sqlcmd[1024] = ""; char email_to_db[128] = "no email"; char error_to_db[256] = "unknown reason"; if (strlen(email)) { strcpy(email_to_db, email); } if (strlen(error)) { strcpy(error_to_db, error); error_to_db[strlen(error_to_db) - 1] = '\0'; } if (status == 1) { sprintf(sqlcmd, "INSERT INTO actions(action, user_id, target_id, data, date, target_type) VALUES('email_send', 0, '%d', \"%s\", NOW(), 'user')", user_id, email_to_db); } else { sprintf(sqlcmd, "INSERT INTO actions(action, user_id, target_id, data, data2, data3, date, target_type) VALUES('error', '0', '%d', \"%s\", \"Can't send email\", \"%s\", NOW(), 'user')", user_id, error_to_db, email_to_db); } m2_mysql_query(sqlcmd); } /* Create action log for sms sending (failed and successful attempts) */ void sms_action_log(int user_id, char *number, int status, char *error) { char sqlcmd[1024] = ""; char sms_to_db[128] = "no number"; char error_to_db[256] = "unknown reason"; int i = 0; if (strlen(number)) { strcpy(sms_to_db, number); } if (strlen(error)) { strcpy(error_to_db, error); error_to_db[strlen(error_to_db) - 1] = '\0'; } for (i = 0; i < strlen(error_to_db); i++) { if (error_to_db[i] == '\'') { error_to_db[i] = ' '; } } if (status == 1) { sprintf(sqlcmd, "INSERT INTO actions(action, user_id, target_id, data, date, target_type) VALUES('sms_send', 0, '%d', \"%s\", NOW(), 'user')", user_id, sms_to_db); } else { sprintf(sqlcmd, "INSERT INTO actions(action, user_id, target_id, data, data2, data3, date, target_type) VALUES('error', '0', '%d', '%s', \"Can't send SMS\", \"%s\", NOW(), 'user')", user_id, error_to_db, sms_to_db); } m2_mysql_query(sqlcmd); } /* Create action log for alert action */ void alert_action_log(char *msg, int alert, int alert_id) { char sqlcmd[1024] = ""; sprintf(sqlcmd, "INSERT INTO actions(action, user_id, target_id, data, data2, date, target_type) VALUES('alerts', 0, '%d', '%s', '%s', NOW(), 'alert')", alert_id, alert == 1 ? "alert" : "clear", msg); m2_mysql_query(sqlcmd); } /* Create action log for alert action */ int get_op_routing_group_id(int op_id) { MYSQL_RES *result; MYSQL_ROW row; char query[1024] = ""; int rg_id = 0; sprintf(query, "SELECT op_routing_group_id FROM devices WHERE id = %d", op_id); if (m2_mysql_query(query)) { return 1; } result = mysql_store_result(&mysql); if (result) { if (mysql_num_rows(result)) { row = mysql_fetch_row(result); if (row[0]) rg_id = atoi(row[0]); } mysql_free_result(result); } return rg_id; }