#define _XOPEN_SOURCE 700 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define CONFPATH "/etc/m2/system.conf" #define DATE_FORMAT "%Y-%m-%d %H:%M:%S" #define SCRIPT_PATH "/usr/local/m2/"SCRIPT_NAME #define LOG_PATH "/var/log/m2/"SCRIPT_NAME".log" #define M2_GUICONFPATH "/home/m2/config/environment.rb" #define M2_GUICONFPATH_ALT "/home/m4/config/environment.rb" #define m2_init(M) { _m2_init(M, argc, argv); } #define m2_log(M, ...) { char m2_log_buffer[10000]; snprintf(m2_log_buffer, 9999, M, ##__VA_ARGS__); _m2_log(m2_log_buffer, 0); m2_log_buffer[0] = 0; } #define m2_log_header(M, ...) { char m2_log_buffer[10000]; snprintf(m2_log_buffer, 9999, M, ##__VA_ARGS__); _m2_log(m2_log_buffer, 1); m2_log_buffer[0] = 0; } // GLOBAL VARIABLES // MySQL variables MYSQL mysql; MYSQL *mysql_multi; MYSQL cloud_mysql; int mysql_connections[8] = { 0 }; pthread_mutex_t m2_mysql_mutex = PTHREAD_MUTEX_INITIALIZER; int mysql_multi_initialized = 0; // Database variables char dbuser[64] = ""; char dbpass[64] = ""; char dbhost[64] = ""; char dbname[64] = ""; int dbport = 0; // FTP variables char ftp_user[256] = ""; char ftp_pass[256] = ""; char ftp_host[256] = ""; char ftp_port[256] = ""; char ftp_archived_calls_path[512] = ""; char ftp_backups_path[512] = ""; char ftp_backups_automatic_cdr_export_path[512] = ""; // SFTP variables char sftp_user[256] = ""; char sftp_pass[256] = ""; char sftp_host[256] = ""; char sftp_port[256] = ""; char sftp_use_key[256] = ""; char sftp_key_path[512] = "/var/www/.ssh/id_rsa"; char sftp_backups_automatic_cdr_export_path[512] = ""; // Cloud database variables char cloud_mysql_host[256] = ""; char cloud_mysql_user[256] = ""; char cloud_mysql_pass[256] = ""; char cloud_mysql_database[256] = ""; int cloud_mysql_port = 0; int cloud_mysql_enabled = 0; // Background tasks int task_id = 0; // Should we run script in background mode? int run_in_background = 1; // Elasticsearch int elasticsearch_is_running = 0; char elasticsearch_host[256] = "localhost"; // Debugging int global_debug = 0; // Types typedef struct m2_system_cmd_response_struct { int return_code; char data[1024]; char error[1024]; } m2_system_cmd_response_t; // FUNCTION DECLARATIONS // General functions int m2_read_config(); void _m2_log(char *msg, int new_line); int m2_check_process_lock(); int m2_check_process_lock_by_name(char *name); void _m2_init(char *header, int argc, char *argv[]); int m2_get_variable(char *variable, char *value); int m2_get_hdd_usage(); void m2_apply_taxes(double *amount, int compound, int tax1, int tax2, int tax3, int tax4, double tax1_value, double tax2_value, double tax3_value, double tax4_value); void get_elasticsearch_host(); void run_in_background_check(int argc, char *argv[]); // FTP/SFTP void m2_get_ftp_settings(); int m2_upload_file_to_ftp(char *ftp_filename, char *to_ftp_path); void m2_get_sftp_settings(); int m2_upload_file_to_sftp(char *sftp_filename, char *to_sftp_path); // Database int m2_mysql_connect(); int m2_mysql_reconnect(); int m2_mysql_close(); int m2_mysql_query(const char *query); int m2_mysql_connect_multi(int index); int m2_mysql_reconnect_multi(int index); int m2_mysql_close_multi(int index); int m2_mysql_get_connection(); int m2_mysql_query_multi(const char *query, int *connection); void m2_cloud_connect(); int m2_cloud_mysql_connect(); int m2_cloud_mysql_query(const char *query); int m2_cloud_mysql_close(); void m2_mysql_cleanup(); // Background tasks int m2_task_lock(); int m2_task_unlock(int status); int m2_task_get(int task, int *owner_id, int *user_id, char *data1, char *data2, char *data3, char *data4, char *data5, char *data6, char *data7, char *data8, char *data9, char *data10); int m2_task_finish(); // Emails int m2_get_email_template_settings(int template_id, char *format, char *name, char *from, char *subject); int m2_get_owner_email_settings(int owner_id, char *login, char *password, char *from, char *server, int *port, int *enabled); int m2_send_email(char *from, char *to, char *subject, char *message, char *attachement, int user_id, int template_id, char *email_name, int owner_id, char *response); // Strings void m2_escape_string(char *string, char c); void m2_escape_xml_string(char *string); void m2_str_replace(char *original, char *replace, char *with); // Dates int m2_get_current_date(char *date); int m2_compare_dates(char *date1, char *date2); int m2_get_last_day_of_month(); void m2_subtract_hours(char *output_date, char *input_date, int hours); void m2_subtract_days(char *output_date, char *input_date, int days); void m2_format_date(char *in_date, char *format, char *out_date); // FUNCTIONS /* Get email template settings */ int m2_get_email_template_settings(int template_id, char *format, char *name, char *from, char *subject) { MYSQL_RES *result; MYSQL_ROW row; char sqlcmd[2048] = ""; sprintf(sqlcmd, "SELECT format, name, from_email, subject FROM emails WHERE id = %d", template_id); if (m2_mysql_query(sqlcmd)) { return 1; } result = mysql_store_result(&mysql); if (result) { if (mysql_num_rows(result)) { while (( row = mysql_fetch_row(result) )) { if (row[0]) strcpy(format, row[0]); else strcpy(format, ""); if (row[1]) strcpy(name, row[1]); else strcpy(name, ""); if (row[2]) strcpy(from, row[2]); else strcpy(from, ""); if (row[3]) strcpy(subject, row[3]); else strcpy(subject, ""); } } mysql_free_result(result); } return 0; } /* Get email settings */ int m2_get_owner_email_settings(int owner_id, char *login, char *password, char *from, char *server, int *port, int *enabled) { 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'), " "(SELECT value FROM conflines WHERE owner_id = 0 AND name = 'Email_Sending_Enabled')", 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]) strcpy(server, row[0]); else strcpy(server, ""); if (row[1]) strcpy(login, row[1]); else strcpy(login, ""); if (row[2]) strcpy(password, row[2]); else strcpy(password, ""); if (row[3]) strcpy(from, row[3]); else strcpy(from, ""); if (row[4]) *port = atoi(row[4]); else *port = 0; if (row[5]) *enabled = atoi(row[5]); else *enabled = 0; mysql_free_result(result); return 0; } /* Send email */ int m2_send_email(char *from, char *to, char *subject, char *message, char *attachement, int user_id, int template_id, char *email_name, int owner_id, char *response) { FILE *uuid_pipe = NULL; char email_reason[500] = ""; char email_response[500] = ""; char email_status[256] = ""; char email_format[256] = ""; char email_template_name[256] = ""; char uuid[80] = ""; int email_record_created = 0; int return_status = 0; char global_from[256] = ""; char login[256] = ""; char password[256] = ""; char email_server_ip[256] = ""; int email_server_port = 0; int emails_enabled = 0; char debug_login[300] = ""; char debug_password[300] = ""; char template_from[256] = ""; char email_name_to_db[256] = ""; char login_cmd[310] = ""; char password_cmd[310] = ""; char attachement_cmd[500] = ""; char template_subject[256] = ""; char subject_cmd[1000] = ""; char format_cmd[256] = ""; char from_cmd[256] = ""; char subject_escaped[1000] = ""; char from_escaped[500] = ""; char to_escaped[500] = ""; if (!to || !strlen(to)) { strcpy(email_response, "Email 'to' is empty"); return_status = 1; goto handle_error; } if (template_id == 0 && (!email_name || !strlen(email_name))) { strcpy(email_response, "Email name is empty"); return_status = 1; goto handle_error; } uuid_pipe = popen("uuidgen", "r"); if (!uuid_pipe) { strcpy(email_response, "Failed to execute uuidgen command"); return_status = 1; goto handle_error; } fgets(uuid, sizeof(uuid), uuid_pipe); pclose(uuid_pipe); if (strlen(uuid)) { uuid[strlen(uuid) - 1] = '\0'; } else { strcpy(email_response, "Failed to generate uuid"); return_status = 1; goto handle_error; } // Get email server settings if (m2_get_owner_email_settings(owner_id, login, password, global_from, email_server_ip, &email_server_port, &emails_enabled)) { char buffer[300] = ""; sprintf(buffer, "Failed to retrieve email settings for owner: %d", owner_id); strcpy(email_response, buffer); return_status = 1; goto handle_error; } if (emails_enabled == 0) { char buffer[300] = ""; sprintf(buffer, "Emails are disabled for owner: %d", owner_id); strcpy(email_response, buffer); return_status = 1; goto handle_error; } if (template_id) { if (m2_get_email_template_settings(template_id, email_format, email_template_name, template_from, template_subject)) { char buffer[300] = ""; sprintf(buffer, "Failed to retrieve email template settings for template: %d", template_id); strcpy(email_response, buffer); return_status = 1; goto handle_error; } } if (strlen(login)) { char escaped_login[300] = ""; strncpy(escaped_login, login, sizeof(escaped_login)); m2_escape_string(escaped_login, '\''); sprintf(login_cmd, "-xu $'%s'", escaped_login); } if (strlen(password)) { char escaped_password[300] = ""; strncpy(escaped_password, password, sizeof(escaped_password)); m2_escape_string(escaped_password, '\''); sprintf(password_cmd, "-xp $'%s'", escaped_password); } if (attachement && strlen(attachement)) { sprintf(attachement_cmd, "-a '%s'", attachement); } if (from && strlen(from)) { strncpy(from_cmd, from, sizeof(from_cmd)); } else if (strlen(template_from)) { strncpy(from_cmd, template_from, sizeof(from_cmd)); } else { strncpy(from_cmd, global_from, sizeof(from_cmd)); } if (subject && strlen(subject)) { strncpy(subject_cmd, subject, sizeof(subject_cmd)); } else if (strlen(template_subject)) { strncpy(subject_cmd, template_subject, sizeof(subject_cmd)); } if (!strlen(subject_cmd)) { strcpy(email_response, "Email 'subject' is empty"); return_status = 1; goto handle_error; } if (!strlen(from_cmd)) { strcpy(email_response, "Email 'from' is empty"); return_status = 1; goto handle_error; } if (strlen(email_format)) { if (strcmp("plain", email_format) == 0) { sprintf(format_cmd, " -o 'message-content-type=text/plain'"); } else if (strcmp("html", email_format) == 0) { sprintf(format_cmd, " -o 'message-content-type=text/html'"); } } int messege_len = strlen(message); // If message is empty, sendEmail will not send email if (messege_len == 0) strcpy(message, " "); // Create buffer for escaped message (double memory in case there are a lot of characters to escape) char *message_escaped = calloc(messege_len * 2 + 1, sizeof(char)); if (message_escaped == NULL) { m2_log("Failed to allocate memory for 'message_escaped'\n"); goto handle_error; } strcpy(message_escaped, message); m2_escape_string(message_escaped, '\''); strncpy(subject_escaped, subject_cmd, sizeof(subject_escaped)); m2_escape_string(subject_escaped, '\''); strncpy(from_escaped, from_cmd, sizeof(from_escaped)); m2_escape_string(from_escaped, '\''); strncpy(to_escaped, to, sizeof(to_escaped)); m2_escape_string(to_escaped, '\''); char *debug_message = NULL; if (global_debug) { debug_message = calloc(strlen(message_escaped) + 1, sizeof(char)); if (debug_message == NULL) { m2_log("Failed to allocate memory for 'debug_message'\n"); goto handle_error; } strcpy(debug_message, message_escaped); strncpy(debug_login, login_cmd, sizeof(debug_login)); strncpy(debug_password, password_cmd, sizeof(debug_password)); } else { debug_message = calloc(strlen("[FILTERED]") + 1, sizeof(char)); if (debug_message == NULL) { m2_log("Failed to allocate memory for 'debug_message'\n"); goto handle_error; } strcpy(debug_message, "[FILTERED]"); strcpy(debug_login, "-xu [FILTERED]"); strcpy(debug_password, "-xp [FILTERED]"); } int email_cmd_len = 0; email_cmd_len += strlen(message_escaped); email_cmd_len += strlen(from_escaped); email_cmd_len += strlen(login_cmd); email_cmd_len += strlen(password_cmd); email_cmd_len += strlen(to_escaped); email_cmd_len += strlen(subject_escaped); email_cmd_len += strlen(email_server_ip); email_cmd_len += strlen(attachement_cmd); email_cmd_len += strlen(format_cmd); email_cmd_len += 500; // Plus some additional space for static text char *emailcmd = calloc(email_cmd_len, sizeof(char)); if (emailcmd == NULL) { m2_log("Failed to allocate memory for 'emailcmd'\n"); goto handle_error; } char *emailcmd_debug = calloc(email_cmd_len, sizeof(char)); sprintf(emailcmd, "/usr/local/m2/sendEmail -f $'%s' %s %s -t $'%s' -u $'%s' -s '%s:%d' -m $'%s' %s -o 'message-charset=UTF-8' -o tls='auto' %s", from_escaped, login_cmd, password_cmd, to_escaped, subject_escaped, email_server_ip, email_server_port, message_escaped, attachement_cmd, format_cmd); sprintf(emailcmd_debug, "/usr/local/m2/sendEmail -f $'%s' %s %s -t $'%s' -u $'%s' -s '%s:%d' -m $'%s' %s -o 'message-charset=UTF-8' -o tls='auto' %s", from_escaped, debug_login, debug_password, to_escaped, subject_escaped, email_server_ip, email_server_port, debug_message, attachement_cmd, format_cmd); m2_log("%s\n", emailcmd_debug); char *emailcmd_escaped = calloc(email_cmd_len * 2 + 1, sizeof(char)); if (emailcmd_escaped == NULL) { m2_log("Failed to allocate memory for 'emailcmd_escaped'\n"); goto handle_error; } strcpy(emailcmd_escaped, emailcmd); m2_escape_string(emailcmd_escaped, '\''); if (strlen(email_template_name)) { strncpy(email_name_to_db, email_template_name, sizeof(email_name_to_db)); } else if (email_name && strlen(email_name)) { strncpy(email_name_to_db, email_name, sizeof(email_name_to_db)); } char *sqlcmd = calloc(strlen(emailcmd_escaped) + 2000, sizeof(char)); if (sqlcmd == NULL) { m2_log("Failed to allocate memory for 'sqlcmd'\n"); goto handle_error; } sprintf(sqlcmd, "INSERT INTO email_jobs(uniqueid, created_at, user_id, email_id, email_name, send_from, send_to, sendemail_text, status, sent_at) " "VALUES ('%s', NOW(), %d, %d, '%s', '%s', '%s', '%s', 'new', NOW())", uuid, user_id, template_id, email_name_to_db, from_cmd, to, emailcmd_escaped); if (m2_mysql_query(sqlcmd)) { return_status = 1; strcpy(email_response, "SQL error"); goto handle_error; } else { email_record_created = 1; } FILE *email_pipe = popen(emailcmd, "r"); if (email_pipe != NULL) { fgets(email_response, sizeof(email_response) - 1, email_pipe); if (strlen(email_response) > 0 && email_response[strlen(email_response) - 1] == '\n') { email_response[strlen(email_response) - 1] = '\0'; } int status = pclose(email_pipe); if (WEXITSTATUS(status) == 0) { return_status = 0; } else { char buffer[600] = ""; sprintf(buffer, "SendEmail error:
%s", email_response); strncpy(email_response, buffer, sizeof(email_response) - 1); return_status = 1; } } else { strcpy(email_response, "Failed to run sendEmail email command"); return_status = 1; } handle_error: if (return_status != 0) { strcpy(email_status, "failed"); strncpy(email_reason, email_response, sizeof(email_reason) - 1); m2_escape_string(email_reason, '\''); } else { strcpy(email_status, "completed"); strcpy(email_reason, ""); } if (email_record_created) { sprintf(sqlcmd, "UPDATE email_jobs set status = '%s', updated_at = NOW(), status_reason = '%s' WHERE uniqueid = '%s'", email_status, email_reason, uuid); m2_mysql_query(sqlcmd); } if (message_escaped) {free(message_escaped); message_escaped = NULL;} if (debug_message) {free(debug_message); debug_message = NULL;} if (emailcmd) {free(emailcmd); emailcmd = NULL;} if (emailcmd_escaped) {free(emailcmd_escaped); emailcmd_escaped = NULL;} if (emailcmd_debug) {free(emailcmd_debug); emailcmd_debug = NULL;} if (sqlcmd) {free(sqlcmd); sqlcmd = NULL;} if (response) { if (return_status == 0) { strcpy(response, "Email sent"); } else { strcpy(response, email_response); } } return return_status; } /* Read configuration from /etc/m2/system.conf */ int m2_read_config() { FILE *file; char var[256] = ""; char val[256] = ""; file = fopen(CONFPATH, "r"); if (!file) { m2_log("Cannot read configuration variables from: " CONFPATH "\n"); return 1; } // default values strcpy(dbhost, ""); strcpy(dbname, ""); strcpy(dbuser, ""); strcpy(dbpass, ""); dbport = 0; global_debug = 0; // read values from conf file while (fscanf(file, "%s = %s", var, val) != EOF) { if (!strcmp(var, "dbhost")) { strcpy(dbhost, val); } if (!strcmp(var, "dbname")) { strcpy(dbname, val); } if (!strcmp(var, "dbuser")) { strcpy(dbuser, val); } if (!strcmp(var, "dbsecret")) { strcpy(dbpass, val); } if (!strcmp(var, "dbport")) { dbport = atoi(val); } if (!strcmp(var, "debug_scripts") || !strcmp(var, "scripts_debug")) { global_debug = atoi(val); } } // print db settings to log file m2_log("Database configuration: host = %s, database = %s, user = %s, port = %d\n", dbhost, dbname, dbuser, dbport); fclose(file); return 0; } /* Log messages to database with current timestamp */ void _m2_log(char *msg, int new_line) { if (msg[0] == '\0') { return; } char date_str[20] = ""; time_t t; struct tm tmp; t = time(NULL); localtime_r(&t, &tmp); strftime(date_str, sizeof(date_str), DATE_FORMAT, &tmp); // open log file FILE *logfile = fopen(LOG_PATH, "a+"); if (logfile == NULL) { #ifdef IGNORE_LOG_ERRORS return #endif perror("error"); printf("Cannot open " LOG_PATH "\n"); exit(1); } if (new_line) { fprintf(logfile, "\n[%s] %s", date_str, msg); } else { #ifdef LOG_TO_CONSOLE printf("%s\n", msg); #endif fprintf(logfile, "[%s] %s", date_str, msg); } fclose(logfile); } /* Check for duplicate processes */ int m2_check_process_lock() { char buffer[128] = ""; char process_list[4096] = ""; // count how many processes exists with the same path FILE *pipe = popen("ps -ef | grep -v grep | grep -v '/bin/sh -c' | grep -v launcher.sh | grep '" SCRIPT_PATH "$' | wc -l", "r"); fgets(buffer, 64, pipe); // count how many processes exists with the same path FILE *pipe_list = popen("ps -ef | grep -v grep | grep -v '/bin/sh -c' | grep -v launcher.sh | grep '" SCRIPT_PATH "$'", "r"); fgets(process_list, 4094, pipe_list); // if more than one, exit current process if (atoi(buffer) > 1) { m2_log("Process locked!\n"); m2_log("Found the following processes running:\n"); m2_log("%s\n", process_list); pclose(pipe); pclose(pipe_list); return 1; } pclose(pipe); pclose(pipe_list); return 0; } /* Check for duplicate processes (by specific process name) */ int m2_check_process_lock_by_name(char *name) { char buffer[128] = ""; char process_list[4096] = ""; char cmd1[2048] = ""; char cmd2[2048] = ""; if (!strlen(name)) return 0; sprintf(cmd1, "ps -ef | grep -v grep | grep -v '/bin/sh -c' | grep -v launcher.sh | grep '%s$' | wc -l", name); sprintf(cmd2, "ps -ef | grep -v grep | grep -v '/bin/sh -c' | grep -v launcher.sh | grep '%s$'", name); // count how many processes exists with the same path FILE *pipe = popen(cmd1, "r"); fgets(buffer, 64, pipe); // count how many processes exists with the same path FILE *pipe_list = popen(cmd2, "r"); fgets(process_list, 4094, pipe_list); // check if at least one is running if (atoi(buffer) > 0) { m2_log("Process locked!\n"); m2_log("Found the following processes running:\n"); m2_log("%s\n", process_list); pclose(pipe); pclose(pipe_list); return 1; } pclose(pipe); pclose(pipe_list); return 0; } /* MySQL cleanup (used in atexit of every script) */ void m2_mysql_cleanup() { m2_mysql_close(); m2_mysql_close_multi(-1); m2_cloud_mysql_close(); mysql_library_end(); } /* Close main MySQL connection */ int m2_mysql_close() { mysql_close(&mysql); return 0; } /* Close multi MySQL connections */ int m2_mysql_close_multi(int index) { #ifdef M2_SQL_CONNECTIONS int i = 0; int j = M2_SQL_CONNECTIONS; if (index >= 0) { i = index; j = index + 1; } for (i = 0; i < j; i++) { mysql_close(&mysql_multi[i]); } #endif return 0; } /* Close main MySQL connection */ int m2_cloud_mysql_close() { if (cloud_mysql_enabled) { mysql_close(&cloud_mysql); } return 0; } /* Get connection index for currently free MySQL connection */ int m2_mysql_get_connection() { #ifdef M2_SQL_CONNECTIONS // we are dealing with global variables so we should lock them pthread_mutex_lock(&m2_mysql_mutex); int connection = 0; // search for available connections while (mysql_connections[connection] == 1) { connection++; if (connection >= M2_SQL_CONNECTIONS) connection = 0; } // mark connection as busy mysql_connections[connection] = 1; pthread_mutex_unlock(&m2_mysql_mutex); return connection; #else return 0; #endif } /* Initialize multiple MySQL connections */ int m2_mysql_connect_multi(int index) { #ifdef M2_SQL_CONNECTIONS int i = 0; int j = M2_SQL_CONNECTIONS; // check if we have valid number connections if (M2_SQL_CONNECTIONS <= 0 || M2_SQL_CONNECTIONS >= 8) { m2_log("M2_SQL_CONNECTIONS is invalid! Accepted values are 1-8, but got value: %d\n", M2_SQL_CONNECTIONS); m2_log("Aborting script...\n"); exit(1); } if (index >= 0) { i = index; j = index + 1; } if (mysql_multi == NULL) { mysql_multi = malloc(sizeof(MYSQL)*M2_SQL_CONNECTIONS); } for (i = 0; i < j; i++) { if (!mysql_init(&mysql_multi[i])) { m2_log("%s\n", mysql_error(&mysql_multi[i])); return 1; } if (!mysql_real_connect(&mysql_multi[i], dbhost, dbuser, dbpass, dbname, dbport, NULL, 0)) { m2_log("%s\n", mysql_error(&mysql_multi[i])); return 1; } if (mysql_query(&mysql_multi[i], "SET NAMES utf8")) { m2_log("%s\n", mysql_error(&mysql_multi[i])); return 1; } mysql_multi_initialized = 1; } #endif return 0; } /* Initialize main MySQL connection */ int m2_mysql_connect() { if (!mysql_init(&mysql)) { m2_log("%s\n", mysql_error(&mysql)); return 1; } if (!mysql_real_connect(&mysql, dbhost, dbuser, dbpass, dbname, dbport, NULL, 0)) { m2_log("%s\n", mysql_error(&mysql)); return 1; } if (mysql_query(&mysql, "SET NAMES utf8")) { m2_log("%s\n", mysql_error(&mysql)); return 1; } return 0; } /* Check if cloud DB is configured in conflines and connect to cloud DB */ void m2_cloud_connect() { MYSQL_RES *result; MYSQL_ROW row; system("/usr/local/m2/check_aws_connection.sh"); char query[2048] = "SELECT value, name FROM conflines WHERE name IN ('AWS_DB_Host_write','AWS_DB_Port','AWS_DB_Name','AWS_DB_Username','AWS_DB_Password')"; if (m2_mysql_query(query)) { exit(1); } result = mysql_store_result(&mysql); if (result) { cloud_mysql_enabled = 1; while ((row = mysql_fetch_row(result)) != NULL) { if (row[0] && row[1] && strcmp(row[1], "AWS_DB_Host_write") == 0) strcpy(cloud_mysql_host, row[0]); if (row[0] && row[1] && strcmp(row[1], "AWS_DB_Name") == 0) strcpy(cloud_mysql_database, row[0]); if (row[0] && row[1] && strcmp(row[1], "AWS_DB_Username") == 0) strcpy(cloud_mysql_user, row[0]); if (row[0] && row[1] && strcmp(row[1], "AWS_DB_Password") == 0) strcpy(cloud_mysql_pass, row[0]); if (row[0] && row[1] && strcmp(row[1], "AWS_DB_Port") == 0) cloud_mysql_port = atoi(row[0]); } if (!strlen(cloud_mysql_host)) cloud_mysql_enabled = 0; if (!strlen(cloud_mysql_database)) cloud_mysql_enabled = 0; if (!strlen(cloud_mysql_user)) cloud_mysql_enabled = 0; if (!strlen(cloud_mysql_pass)) cloud_mysql_enabled = 0; if (cloud_mysql_port == 0) cloud_mysql_enabled = 0; mysql_free_result(result); } if (cloud_mysql_enabled) { m2_log("Cloud database connection details: host: %s, user: %s, database: %s, port: %d\n", cloud_mysql_host, cloud_mysql_user, cloud_mysql_database, cloud_mysql_port); if (m2_cloud_mysql_connect()) { // couldn't connect, disable cloud DB and use local database m2_log("Couldn't connect to cloud database, reverting back to local database\n"); cloud_mysql_enabled = 0; } else { m2_log("Successfully connected to cloud database\n"); } } else { m2_log("Cloud database is not enabled\n"); } } /* Initialize cloud MySQL connection */ int m2_cloud_mysql_connect() { if (!mysql_init(&cloud_mysql)) { m2_log("%s\n", mysql_error(&cloud_mysql)); return 1; } if (!mysql_real_connect(&cloud_mysql, cloud_mysql_host, cloud_mysql_user, cloud_mysql_pass, cloud_mysql_database, cloud_mysql_port, NULL, 0)) { m2_log("%s\n", mysql_error(&cloud_mysql)); return 1; } if (mysql_query(&cloud_mysql, "SET NAMES utf8")) { m2_log("%s\n", mysql_error(&cloud_mysql)); return 1; } return 0; } /* Reconnect to MySQL */ int m2_mysql_reconnect() { int mysql_reconnect_delay = 3; int mysql_reconnect_retries = 0; int mysql_max_reconnects = 10; int error = 1; int err_num = 0; while (error) { error = mysql_ping(&mysql); err_num = mysql_errno(&mysql); switch (err_num) { case 0: // reconnected break; case CR_SERVER_GONE_ERROR: m2_log("MySQL server has gone away. Attempting to reconnect (%d/%d)\n", mysql_reconnect_retries + 1, mysql_max_reconnects); m2_mysql_close(); m2_mysql_connect(); break; case CR_SERVER_LOST: m2_log("MySQL server lost. Attempting to reconnect (%d/%d)\n", mysql_reconnect_retries + 1, mysql_max_reconnects); m2_mysql_close(); m2_mysql_connect(); break; default: m2_log("Unknown MySQL connection error: (%d) %s\n", err_num, mysql_error(&mysql)); exit(1); } if (err_num == 0) { m2_log("Reconnected to MySQL successfully\n"); return 0; } mysql_reconnect_retries++; if (mysql_reconnect_retries >= mysql_max_reconnects) { m2_log("Retried %d time to connect to MySQL, giving up...\n", mysql_max_reconnects); exit(1); } sleep(mysql_reconnect_delay); } return 0; } /* Reconnect to MySQL (for multi connections) */ int m2_mysql_reconnect_multi(int index) { #ifdef M2_SQL_CONNECTIONS int mysql_reconnect_delay = 3; int i = 0; int j = M2_SQL_CONNECTIONS; if (index >= 0) { i = index; j = index + 1; } for (i = 0; i < j; i++) { int mysql_reconnect_retries = 0; int mysql_max_reconnects = 10; int error = 1; int err_num = 0; m2_log("Attempting to reconnect MySQL connection [%d]\n", i); while (error) { error = mysql_ping(&mysql_multi[i]); err_num = mysql_errno(&mysql_multi[i]); switch (err_num) { case 0: // reconnected break; case CR_SERVER_GONE_ERROR: m2_log("Server has gone away on MySQL connection [%d]. Attempting to reconnect (%d/%d)\n", i, mysql_reconnect_retries + 1, mysql_max_reconnects); m2_mysql_close_multi(i); m2_mysql_connect_multi(i); break; case CR_SERVER_LOST: m2_log("Server lost on MySQL connection [%d]. Attempting to reconnect (%d/%d)\n", i, mysql_reconnect_retries + 1, mysql_max_reconnects); m2_mysql_close_multi(i); m2_mysql_connect_multi(i); break; default: m2_log("Unknown error on MySQL connection [%d]: (%d) %s\n", i, err_num, mysql_error(&mysql_multi[i])); exit(1); } if (err_num == 0) { m2_log("Reconnected successfully to MySQL connection [%d]\n", i); break; } mysql_reconnect_retries++; if (mysql_reconnect_retries >= mysql_max_reconnects) { m2_log("Retried %d times to connect to MySQL connection [%d], giving up...\n", mysql_max_reconnects, i); exit(1); } sleep(mysql_reconnect_delay); } } #endif return 0; } /* Handle multi-connection MySQL queries */ int m2_mysql_query_multi(const char *query, int *connection) { int retry_delay = 3; int max_retries = 3; int retried = 0; if (global_debug) { m2_log("%s\n", query); } if (!mysql_multi_initialized) { m2_log("ERROR: MySQL multi connections are not initialized, missing '#define M2_SQL_CONNECTIONS'\n"); exit(1); } *connection = m2_mysql_get_connection(); retry_multi_sql: if (mysql_query(&mysql_multi[*connection], query)) { char mysql_error_msg[1024] = ""; strcpy(mysql_error_msg, mysql_error(&mysql_multi[*connection])); // Handle deadlocks - retry query after N seconds if (strstr(mysql_error_msg, "Deadlock found when trying to get lock")) { m2_log("Deadlock was detected on MySQL connection [%d], will try to restart query in %d seconds\n", *connection, retry_delay); sleep(retry_delay); retried++; if (retried >= max_retries) { m2_log("Tried to execute query %d times on MySQL connection [%d], but all attempts failed. Aborting...\n", *connection, max_retries); m2_log("SQL ERROR: %s\n", mysql_error_msg); m2_log("SQL QUERY: %s\n", query); mysql_connections[*connection] = 0; return 1; } else { goto retry_multi_sql; } } else // Handle shutdown/server gone away errors if (strstr(mysql_error_msg, "Server shutdown in progress") || strstr(mysql_error_msg, "MySQL server has gone away") || strstr(mysql_error_msg, "Query execution was interrupted") || strstr(mysql_error_msg, "Lost connection to MySQL server during query")) { if (strstr(mysql_error_msg, "Server shutdown in progress")) { m2_log("MySQL shutdown was detected on MySQL connection [%d], will try to reconnect and restart query in %d seconds\n", *connection, retry_delay); } else if (strstr(mysql_error_msg, "MySQL server has gone away")) { m2_log("MySQL server has gone away on MySQL connection [%d], will try to reconnect and restart query in %d seconds\n", *connection, retry_delay); } else if (strstr(mysql_error_msg, "Lost connection to MySQL server during query")) { m2_log("Lost connection to MySQL during query on MySQL connection [%d], will try to reconnect and restart query in %d seconds\n", *connection, retry_delay); } else if (strstr(mysql_error_msg, "Query execution was interrupted")) { m2_log("Query execution was interrupted on MySQL connection [%d], will try to reconnect and restart query in %d seconds\n", *connection, retry_delay); } sleep(retry_delay); m2_mysql_reconnect_multi(*connection); retried++; if (retried >= max_retries) { m2_log("Tried to execute query %d times on MySQL connection [%d], but all attempts failed. Aborting...\n", max_retries, *connection); m2_log("SQL ERROR: %s\n", mysql_error_msg); m2_log("SQL QUERY: %s\n", query); mysql_connections[*connection] = 0; return 1; } else { goto retry_multi_sql; } } // Normal error else { m2_log("SQL ERROR: %s\n", mysql_error_msg); m2_log("SQL QUERY: %s\n", query); // mark connection as available mysql_connections[*connection] = 0; return 1; } } return 0; } /* Handle main connection MySQL queries */ int m2_mysql_query(const char *query) { int retry_delay = 3; int max_retries = 3; int retried = 0; if (global_debug) { m2_log("%s\n", query); } retry_sql: // Send query if (mysql_query(&mysql, query)) { char mysql_error_msg[1024] = ""; strcpy(mysql_error_msg, mysql_error(&mysql)); // Handle deadlocks - retry query after N seconds if (strstr(mysql_error_msg, "Deadlock found when trying to get lock")) { m2_log("Deadlock was detected, will try to restart query in %d seconds\n", retry_delay); sleep(retry_delay); retried++; if (retried >= max_retries) { m2_log("Tried to execute query %d times but all attempts failed. Aborting...\n", max_retries); m2_log("SQL ERROR: %s\n", mysql_error_msg); m2_log("SQL QUERY: %s\n", query); return 1; } else { goto retry_sql; } } else // Handle shutdown/server gone away errors if (strstr(mysql_error_msg, "Server shutdown in progress") || strstr(mysql_error_msg, "MySQL server has gone away") || strstr(mysql_error_msg, "Query execution was interrupted") || strstr(mysql_error_msg, "Lost connection to MySQL server during query")) { if (strstr(mysql_error_msg, "Server shutdown in progress")) { m2_log("MySQL shutdown was detected, will try to reconnect and restart query in %d seconds\n", retry_delay); } else if (strstr(mysql_error_msg, "MySQL server has gone away")) { m2_log("MySQL server has gone away, will try to reconnect and restart query in %d seconds\n", retry_delay); } else if (strstr(mysql_error_msg, "Lost connection to MySQL server during query")) { m2_log("Lost connection to MySQL during query, will try to reconnect and restart query in %d seconds\n", retry_delay); } else if (strstr(mysql_error_msg, "Query execution was interrupted")) { m2_log("Query execution was interrupted, will try to reconnect and restart query in %d seconds\n", retry_delay); } sleep(retry_delay); m2_mysql_reconnect(); retried++; if (retried >= max_retries) { m2_log("Tried to execute query %d times but all attempts failed. Aborting...\n", max_retries); m2_log("SQL ERROR: %s\n", mysql_error_msg); m2_log("SQL QUERY: %s\n", query); return 1; } else { goto retry_sql; } } // Normal error else { m2_log("SQL ERROR: %s\n", mysql_error_msg); m2_log("SQL QUERY: %s\n", query); return 1; } } return 0; } /* Handle cloud connection MySQL queries */ int m2_cloud_mysql_query(const char *query) { MYSQL *connection = NULL; int max_retries = 3; int retried = 0; if (cloud_mysql_enabled) { connection = &cloud_mysql; } else { connection = &mysql; } if (global_debug) { m2_log("%s\n", query); } retry_sql: // try to send query // if query fails, log and report error if (mysql_query(connection, query)) { char mysql_error_msg[1024] = ""; strcpy(mysql_error_msg, mysql_error(connection)); // check if this is deadlock error: Deadlock found when trying to get lock; try restarting transaction // let's try to do what messages suggest - try to restart transaction in few seconds if (strstr(mysql_error_msg, "Deadlock found when trying to get lock")) { m2_log("Deadlock was found, will try to restart query in 3 seconds\n"); sleep(3); retried++; if (retried >= max_retries) { m2_log("Tried to insert data %d times and all attempts failed. Aborting...\n", max_retries); m2_log("SQL ERROR: %s\n", mysql_error_msg); m2_log("SQL QUERY: %s\n", query); return 1; } goto retry_sql;; } else { m2_log("SQL ERROR: %s\n", mysql_error_msg); m2_log("SQL QUERY: %s\n", query); return 1; } } return 0; } /* Get current date string */ int m2_get_current_date(char *date) { time_t t; struct tm tmp; t = time(NULL); localtime_r(&t, &tmp); char date_tmp[256] = ""; if (date) { strftime(date_tmp, sizeof(date_tmp), DATE_FORMAT, &tmp); strcpy(date, date_tmp); } return 0; } /* Date compare function */ int m2_compare_dates(char *date1, char *date2) { time_t t1, t2; struct tm tm1, tm2; memset(&tm1, 0, sizeof(struct tm)); memset(&tm2, 0, sizeof(struct tm)); strptime(date1, DATE_FORMAT, &tm1); strptime(date2, DATE_FORMAT, &tm2); t1 = mktime(&tm1); t2 = mktime(&tm2); if (t1 > t2) return 1; return 0; } /* Function to escape characters in a string from: This is my brother's car to: This is my brother\'s car */ void m2_escape_string(char *string, char c) { int i; int len = strlen(string); if (!len) return; for (i = 0; i < len; i++) { if (string[i] == c) { // Do not escape already escaped characters if (i > 0 && string[i - 1] == '\\') { continue; } memmove(string + i + 1, string + i, strlen(string + i)); string[i] = '\\'; i++; len++; } } string[len] = '\0'; } /* Initialize M2 script */ void _m2_init(char *header, int argc, char *argv[]) { int i = 0; for (i = 0; i < argc; i++) { if (strcmp(argv[i], "-v") == 0) { printf(SCRIPT_VERSION); exit(0); } } #ifndef NO_PROCESS_LOCK if (m2_check_process_lock()) exit(1); #endif // print header m2_log_header(header); #ifdef SCRIPT_VERSION m2_log("Script version: " SCRIPT_VERSION "\n"); #endif // get config if (m2_read_config()) { exit(1); } // initialize main MySQL connection if (m2_mysql_connect() == 1) { exit(1); } #ifdef M2_SQL_CONNECTIONS // initialize multiple MySQL connections m2_log("Initializing %d MySQL connections\n", M2_SQL_CONNECTIONS); if (m2_mysql_connect_multi(-1) == 1) { exit(1); } #endif atexit(m2_mysql_cleanup); } /* Get background task by task_id */ int m2_task_get(int task, int *owner_id, int *user_id, char *data1, char *data2, char *data3, char *data4, char *data5, char *data6, char *data7, char *data8, char *data9, char *data10) { MYSQL_RES *result; MYSQL_ROW row; char sqlcmd[1024] = ""; int task_found = 0; int local_user_id = 0; int local_owner_id = 0; sprintf(sqlcmd, "SELECT id, owner_id, user_id, data1, data2, data3, data4, data5, data6, data7, data8, data9, data10 FROM background_tasks WHERE task_id = %d AND status = 1 ORDER BY created_at LIMIT 1", task); if (m2_mysql_query(sqlcmd)) { exit(1); } else { result = mysql_store_result(&mysql); if (result) { while ((row = mysql_fetch_row(result))) { task_found = 1; if (row[0]) task_id = atoi(row[0]); if (row[1]) local_owner_id = atoi(row[1]); if (row[2]) local_user_id = atoi(row[2]); if (row[3] && data1) strcpy(data1, row[3]); if (row[4] && data2) strcpy(data2, row[4]); if (row[5] && data3) strcpy(data3, row[5]); if (row[6] && data4) strcpy(data4, row[6]); if (row[7] && data5) strcpy(data5, row[7]); if (row[8] && data6) strcpy(data6, row[8]); if (row[9] && data7) strcpy(data7, row[9]); if (row[10] && data8) strcpy(data8, row[10]); if (row[11] && data9) strcpy(data9, row[11]); if (row[12] && data10) strcpy(data10, row[12]); } } mysql_free_result(result); } if (task_found == 0) { m2_log("Task not found!\n"); return 1; } if (user_id != NULL) *user_id = local_user_id; if (owner_id != NULL) *owner_id = local_owner_id; m2_log("Task retrieved - id: %i, user_id: %i, data1: %s, data2: %s, data3: %s, data4: %s, data5: %s, data6: %s, data7: %s, data8: %s, data9: %s, data10: %s\n", task_id, user_id == NULL ? 0 : *user_id, data1 == NULL ? "NULL" : data1, data2 == NULL ? "NULL" : data2, data3 == NULL ? "NULL" : data3, data4 == NULL ? "NULL" : data4, data5 == NULL ? "NULL" : data5, data6 == NULL ? "NULL" : data6, data7 == NULL ? "NULL" : data7, data8 == NULL ? "NULL" : data8, data9 == NULL ? "NULL" : data9, data10 == NULL ? "NULL" : data10); return 0; } /* Finish background task */ int m2_task_finish() { char sqlcmd[1024] = ""; sprintf(sqlcmd,"UPDATE background_tasks SET status = 2, finished_at = NOW(), expected_to_finish_at = NULL, updated_at = NOW(), percent_completed = 100 WHERE id = %i", task_id); if (m2_mysql_query(sqlcmd)) exit(1); m2_log("Task finished\n"); m2_task_unlock(3); return 0; } /* Lock background task */ int m2_task_lock() { char sqlcmd[1024] = ""; sprintf(sqlcmd,"UPDATE background_tasks SET status = 2, started_at = NOW(), updated_at = NOW(), percent_completed = 0 WHERE id = %i", task_id); if (m2_mysql_query(sqlcmd)) exit(1); m2_log("Task locked\n"); return 0; } /* Function to unlock task (change status) Status: 1 - WAITING 2 - IN PROGRESS 3 - DONE 4 - FAILED */ int m2_task_unlock(int status) { char sqlcmd[1024] = ""; sprintf(sqlcmd,"UPDATE background_tasks SET status = %i, expected_to_finish_at = NULL, updated_at = NOW() WHERE id = %i", status, task_id); if (m2_mysql_query(sqlcmd)) exit(1); return 0; } /* Get specific variable value from config file */ int m2_get_variable(char *variable, char *value) { FILE *file; char var[256] = ""; char val[256] = ""; int found = 0; // Reset variable strcpy(value, ""); file = fopen(CONFPATH, "r"); if (!file) { m2_log("Cannot read configuration variables from: " CONFPATH "\n"); return 1; } // default values strcpy(dbhost, ""); strcpy(dbname, ""); strcpy(dbuser, ""); strcpy(dbpass, ""); dbport = 0; // read values from conf file while (fscanf(file, "%s = %s", var, val) != EOF) { if (!strcmp(var, variable)) { strcpy(value, val); found = 1; } } if (global_debug) { if (found) { m2_log("Variable: %s = %s\n", variable, value); } else { m2_log("Variable %s not found\n", variable); } } fclose(file); return 0; } /* Get free HDD space (percent) */ int m2_get_hdd_usage() { int hdd_usage = 0; FILE *pipe; pipe = popen("df / | grep -Po '(?=\\d+%)\\d+'", "r"); if (pipe != NULL) { char pipe_buffer[64] = ""; fgets(pipe_buffer, 60, pipe); hdd_usage = atoi(pipe_buffer); } pclose(pipe); return hdd_usage; } /* Apply taxes */ void m2_apply_taxes(double *amount, int compound, int tax1, int tax2, int tax3, int tax4, double tax1_value, double tax2_value, double tax3_value, double tax4_value) { double tax = 0; if (tax1_value > 0) { tax1 = 1; } if (compound) { if (tax1 && tax1_value != 0) *amount += (*amount * tax1_value / 100.0); if (tax2 && tax2_value != 0) *amount += (*amount * tax2_value / 100.0); if (tax3 && tax3_value != 0) *amount += (*amount * tax3_value / 100.0); if (tax4 && tax4_value != 0) *amount += (*amount * tax4_value / 100.0); } else { tax = 0; if (tax1 && tax1_value != 0) tax += (*amount * tax1_value / 100.0); if (tax2 && tax2_value != 0) tax += (*amount * tax2_value / 100.0); if (tax3 && tax3_value != 0) tax += (*amount * tax3_value / 100.0); if (tax4 && tax4_value != 0) tax += (*amount * tax4_value / 100.0); } *amount += tax; } /* Get Elasticsearch host from conflines */ void get_elasticsearch_host() { MYSQL_RES *result; MYSQL_ROW row; if (m2_mysql_query("SELECT value FROM conflines WHERE name = 'ES_IP'")) { exit(1); } // get results result = mysql_store_result(&mysql); if (result) { if (mysql_num_rows(result)) { while (( row = mysql_fetch_row(result) )) { if (row[0]) { strcpy(elasticsearch_host, row[0]); } } } } // check if ES is running on this server if (strcmp(elasticsearch_host, "localhost")) { char cmd[1024] = ""; char response[1024] = ""; sprintf(cmd, "/sbin/ifconfig | /bin/grep -F '%s' | /usr/bin/wc -l", elasticsearch_host); FILE *pipe = popen(cmd, "r"); fgets(response, 1024, pipe); if (atoi(response) > 0) { m2_log("Changing ES host from '%s' to 'localhost'\n", elasticsearch_host); strcpy(elasticsearch_host, "localhost"); } pclose(pipe); } mysql_free_result(result); } /* Function to escape special XML characters in a string " " ' ' < < > > */ void m2_escape_xml_string(char *string) { int i; int len = strlen(string); char tmp_buffer[10000] = ""; if (!len) return; check_again: len = strlen(string); for (i = 0; i < len; i++) { if (string[i] == '"') { strcpy(tmp_buffer, string + i + 1); strcpy(string + i, """); strcat(string, tmp_buffer); goto check_again; } else if (string[i] == '\'') { strcpy(tmp_buffer, string + i + 1); strcpy(string + i, "'"); strcat(string, tmp_buffer); goto check_again; } else if (string[i] == '<') { strcpy(tmp_buffer, string + i + 1); strcpy(string + i, "<"); strcat(string, tmp_buffer); goto check_again; } else if (string[i] == '>') { strcpy(tmp_buffer, string + i + 1); strcpy(string + i, ">"); strcat(string, tmp_buffer); goto check_again; } } } /* Get last day of current month */ int m2_get_last_day_of_month() { int day = 0; char buffer[256] = ""; char cmd[256] = "date -d \"-$(date +%d) days month\" +%d"; FILE *pipe = popen(cmd, "r"); if (pipe) { fgets(buffer, 256, pipe); day = atoi(buffer); pclose(pipe); } return day; } /* Subtract X hours from date */ void m2_subtract_hours(char *output_date, char *input_date, int hours) { char buffer[256] = ""; char cmd[256] = ""; sprintf(cmd, "date -d '%s %d hour ago' +'%s'", input_date, hours, DATE_FORMAT); FILE *pipe = popen(cmd, "r"); if (pipe) { fgets(buffer, 256, pipe); pclose(pipe); } if (strlen(buffer)) { strncpy(output_date, buffer, strlen(buffer) - 1); } } /* Subtract X days from date */ void m2_subtract_days(char *output_date, char *input_date, int days) { char buffer[256] = ""; char cmd[256] = ""; sprintf(cmd, "date -d '%s %d day ago' +'%s'", input_date, days, DATE_FORMAT); FILE *pipe = popen(cmd, "r"); if (pipe) { fgets(buffer, 256, pipe); pclose(pipe); } if (strlen(buffer)) { strncpy(output_date, buffer, strlen(buffer) - 1); } } /* Execute system command and return response */ int m2_execute_system_command(char *cmd, m2_system_cmd_response_t *resp) { char cmd_to_execute[1024] = ""; char response[1024] = ""; int status = 0; FILE *pipe; if (resp) { resp->return_code = 0; } m2_log("Executing command: %s\n", cmd); sprintf(cmd_to_execute, "%s 2>&1", cmd); pipe = popen(cmd_to_execute, "r"); if (pipe != NULL) { fread(response, sizeof(response) - 1, 1, pipe); status = WEXITSTATUS(pclose(pipe)); int response_len = strlen(response); // remove last new line if (response[response_len - 1] == 10) response[response_len - 1] = 0; if (status == 0) { m2_log("Command executed successfully\n"); if (resp) { strcpy(resp->data, response); strcpy(resp->error, ""); } } else { m2_log("Command returned error code: %d\n", status); m2_log("Error message: %s\n", response); if (resp) { strcpy(resp->error, response); strcpy(resp->data, ""); } } } else { m2_log("Failed open command: %s\n", cmd); if (resp) { strcpy(resp->error, "Failed execute command"); strcpy(resp->data, ""); } status = -1; } if (resp) resp->return_code = status; return status; } /* Internal string replace function */ char *_m2_str_replace(char *orig, char *rep, char *with) { char *result; // the return string char *ins; // the next insert point char *tmp; // varies int len_rep; // length of rep (the string to remove) int len_with; // length of with (the string to replace rep with) int len_front; // distance between rep and end of last rep int count; // number of replacements // sanity checks and initialization if (!orig || !rep) return NULL; len_rep = strlen(rep); if (len_rep == 0) return NULL; // empty rep causes infinite loop during count if (!with) with = ""; len_with = strlen(with); // count the number of replacements needed ins = orig; for (count = 0; (tmp = strstr(ins, rep)); ++count) { ins = tmp + len_rep; } tmp = result = malloc(strlen(orig) + (len_with - len_rep) * count + 1); if (!result) return NULL; // first time through the loop, all the variable are set correctly // from here on, // tmp points to the end of the result string // ins points to the next occurrence of rep in orig // orig points to the remainder of orig after "end of rep" while (count--) { ins = strstr(orig, rep); len_front = ins - orig; tmp = strncpy(tmp, orig, len_front) + len_front; tmp = strcpy(tmp, with) + len_with; orig += len_front + len_rep; // move to next "end of rep" } strcpy(tmp, orig); return result; } /* String replace function */ void m2_str_replace(char *original, char *replace, char *with) { char *new_string = NULL; new_string = _m2_str_replace(original, replace, with); if (new_string) { strcpy(original, new_string); free(new_string); } } /* Function check if script should be run in background By default some scripts run in background on Centos 6 All scripts in Centos 7 should run in foreground */ void run_in_background_check(int argc, char *argv[]) { int i = 0; for (i = 0; i < argc; i++) { if (strcmp(argv[i], "-nc") == 0) { run_in_background = 0; } } } /* Fixes paths like "aaa\bbb\ccc" to be "/aaa/bbb/ccc" */ void m2_fix_ftp_path(char *path, int path_size) { int i = 0; char new_path[520] = ""; if (!strlen(path)) return; if (path[0] != '/') { sprintf(new_path, "/%s", path); } else { strcpy(new_path, path); } for (i = 0; i < strlen(new_path); i++) { if (new_path[i] == '\\') { new_path[i] = '/'; } } strncpy(path, new_path, path_size); } /* Get all FTP settings (credentials and paths) from Conflines */ void m2_get_ftp_settings() { MYSQL_RES *result; MYSQL_ROW row; char sql_cmd[2048] = ""; sprintf(sql_cmd, "SELECT " "(SELECT value FROM conflines WHERE name = 'ftp_user'), " "(SELECT value FROM conflines WHERE name = 'ftp_pass'), " "(SELECT value FROM conflines WHERE name = 'ftp_host'), " "(SELECT value FROM conflines WHERE name = 'ftp_port'), " "(SELECT value FROM conflines WHERE name = 'ftp_archived_calls_path'), " "(SELECT value FROM conflines WHERE name = 'ftp_backups_path'), " "(SELECT value FROM conflines WHERE name = 'ftp_backups_automatic_cdr_export_path') " ); if (m2_mysql_query(sql_cmd)) { exit(1); } result = mysql_store_result(&mysql); if (result) { if (mysql_num_rows(result)) { row = mysql_fetch_row(result); // check if we get result if (row[0]) strcpy(ftp_user, row[0]); else strcpy(ftp_user, ""); if (row[1]) strcpy(ftp_pass, row[1]); else strcpy(ftp_pass, ""); if (row[2]) strcpy(ftp_host, row[2]); else strcpy(ftp_host, ""); if (row[3]) strcpy(ftp_port, row[3]); else strcpy(ftp_port, ""); if (row[4]) strcpy(ftp_archived_calls_path, row[4]); else strcpy(ftp_archived_calls_path, ""); if (row[5]) strcpy(ftp_backups_path, row[5]); else strcpy(ftp_backups_path, ""); if (row[6]) strcpy(ftp_backups_automatic_cdr_export_path, row[6]); else strcpy(ftp_backups_automatic_cdr_export_path, ""); } } m2_fix_ftp_path(ftp_archived_calls_path, sizeof(ftp_archived_calls_path)); m2_fix_ftp_path(ftp_backups_path, sizeof(ftp_backups_path)); m2_fix_ftp_path(ftp_backups_automatic_cdr_export_path, sizeof(ftp_backups_automatic_cdr_export_path)); mysql_free_result(result); } /* Upload File to FTP server and catch error */ int m2_upload_file_to_ftp(char *ftp_filename, char *to_ftp_path) { char cmd[2000] = ""; m2_system_cmd_response_t response; sprintf(cmd, "/usr/bin/curl -T %s -u %s:%s ftp://%s:%s%s/", ftp_filename, ftp_user, ftp_pass, ftp_host, ftp_port, to_ftp_path); m2_execute_system_command(cmd, &response); m2_log("Status: [%d], response: [%s], error: [%s]\n", response.return_code, response.data, response.error); if (response.return_code == 0) { m2_log("File successfully uploaded to FTP server\n"); return 1; } else { m2_log("Failed to Upload File on FTP server\n"); return 0; } } /* Get all SFTP settings (credentials and paths) from Conflines */ void m2_get_sftp_settings() { MYSQL_RES *result; MYSQL_ROW row; char sql_cmd[2048] = ""; sprintf(sql_cmd, "SELECT " "(SELECT value FROM conflines WHERE name = 'sftp_user'), " "(SELECT value FROM conflines WHERE name = 'sftp_pass'), " "(SELECT value FROM conflines WHERE name = 'sftp_host'), " "(SELECT value FROM conflines WHERE name = 'sftp_port'), " "(SELECT value FROM conflines WHERE name = 'sftp_use_key'), " "(SELECT value FROM conflines WHERE name = 'sftp_backups_automatic_cdr_export_path') " ); if (m2_mysql_query(sql_cmd)) { exit(1); } result = mysql_store_result(&mysql); if (result) { if (mysql_num_rows(result)) { row = mysql_fetch_row(result); // check if we get result if (row[0]) strcpy(sftp_user, row[0]); else strcpy(sftp_user, ""); if (row[1]) strcpy(sftp_pass, row[1]); else strcpy(sftp_pass, ""); if (row[2]) strcpy(sftp_host, row[2]); else strcpy(sftp_host, ""); if (row[3]) strcpy(sftp_port, row[3]); else strcpy(sftp_port, ""); if (row[4]) strcpy(sftp_use_key, row[4]); else strcpy(sftp_use_key, ""); if (row[5]) strcpy(sftp_backups_automatic_cdr_export_path, row[5]); else strcpy(sftp_backups_automatic_cdr_export_path, ""); } } m2_fix_ftp_path(sftp_backups_automatic_cdr_export_path, sizeof(sftp_backups_automatic_cdr_export_path)); mysql_free_result(result); } /* Upload File to SFTP server and catch error */ int m2_upload_file_to_sftp(char *sftp_filename, char *to_sftp_path) { char cmd[2000] = ""; m2_system_cmd_response_t response; if (atoi(sftp_use_key) == 1) { if (!strlen(sftp_key_path)) { m2_log("SFTP key path is not set\n"); return 0; } sprintf(cmd, "/usr/bin/curl -T %s -u %s: --key %s sftp://%s:%s%s/", sftp_filename, sftp_user, sftp_key_path, sftp_host, sftp_port, to_sftp_path); } else { sprintf(cmd, "/usr/bin/curl -T %s -u %s:%s sftp://%s:%s%s/", sftp_filename, sftp_user, sftp_pass, sftp_host, sftp_port, to_sftp_path); } m2_execute_system_command(cmd, &response); m2_log("Status: [%d], response: [%s], error: [%s]\n", response.return_code, response.data, response.error); if (response.return_code == 0) { m2_log("File successfully uploaded to SFTP server\n"); return 1; } else { m2_log("Failed to Upload File on SFTP server\n"); return 0; } } /* Get web_dir and web_url from conf file */ int m2_get_web_config(char *web_url, char *web_dir) { FILE *file; char var[256] = ""; char val[256] = ""; char conf_path[200] = ""; m2_log("Parsing web url and web dir variables\n"); if (access(M2_GUICONFPATH, F_OK) == 0) { strcpy(conf_path, M2_GUICONFPATH); } else if (access(M2_GUICONFPATH_ALT, F_OK) == 0) { strcpy(conf_path, M2_GUICONFPATH_ALT); } else { m2_log("Unknown GUI conf path\n"); exit(1); } file = fopen(conf_path, "r"); if (!file) { m2_log("Cannot read configuration variables from: %s\n", conf_path); return 1; } // read values from conf file while (fscanf(file, "%s = %s", var, val) != EOF) { if (!strcmp(var, "Web_Dir")) { strcpy(web_dir, val + 1); } if (!strcmp(var, "Web_URL")) { strcpy(web_url, val + 1); } strcpy(var, ""); strcpy(val, ""); } // Web_Dir can be written in two forms: // // Web_Dir = Rails.env.to_s == 'production' ? "/billing" : '' // or // Web_Dir = "/billing" // // we should check which is it and then parse properly if (strlen(web_dir) && strcmp(web_dir, "ails.env.to_s") == 0) { // yes ails.env.to_s not Rails.env.to_s FILE *fp; char new_line[256] = ""; char *line = NULL; size_t len = 0; ssize_t read; char *ptr_start = NULL; char *ptr_end = NULL; fp = fopen(conf_path, "r"); if (!fp) { m2_log("Cannot read configuration variables from: %s\n", conf_path); return 1; } while (( read = getline(&line, &len, fp)) != -1) { if (strstr(line, "Web_Dir =")) { strcpy(new_line, line); ptr_start = strstr(new_line, "?"); ptr_end = strstr(new_line, ":"); if (ptr_start && ptr_end && strlen(ptr_start) && strlen(ptr_end)) { *(ptr_end - 1) = '\0'; strcpy(web_dir, ptr_start + 3); } break; } } if (line) { free(line); line = NULL; } fclose(fp); } // remove quotes if (strlen(web_dir) && strlen(web_url)) { web_url[strlen(web_url) - 1] = 0; web_dir[strlen(web_dir) - 1] = 0; m2_log("Web dir: %s, web url: %s\n", web_dir, web_url); } else { m2_log("Failed to parse web_dir (%s) or web_url (%s)\n", web_dir, web_url); return 1; } fclose(file); return 0; } /* Format given date by given format */ void m2_format_date(char *in_date, char *format, char *out_date) { char local_out_date[50] = ""; struct tm local_in_date_tm; memset(&local_in_date_tm, 0, sizeof(struct tm)); strptime(in_date, DATE_FORMAT, &local_in_date_tm); strftime(local_out_date, sizeof(local_out_date), format, &local_in_date_tm); strcpy(out_date, local_out_date); }