Index: res_config_mysql.c =================================================================== --- res_config_mysql.c (revision 315) +++ res_config_mysql.c (working copy) @@ -60,6 +60,8 @@ AST_MUTEX_DEFINE_STATIC(mysql_lock); #define RES_CONFIG_MYSQL_CONF "res_mysql.conf" +#define READ_OPT 0 +#define WRITE_OPT 1 MYSQL mysql; static char dbhost[50]; static char dbuser[50]; @@ -70,8 +72,19 @@ static int connected; static time_t connect_time; +MYSQL mysql_write; +static char dbhost_write[50]; +static char dbuser_write[50]; +static char dbpass_write[50]; +static char dbname_write[50]; +static char dbsock_write[50]; +static int dbport_write; +static int connected_write; +static time_t connect_time_write; + static int parse_config(void); -static int mysql_reconnect(const char *database); +static int mysql_reconnect(const char *database, int write); + static int realtime_mysql_status(int fd, int argc, char **argv); static char cli_realtime_mysql_status_usage[] = @@ -94,25 +107,25 @@ char *chunk; char *op; const char *newparam, *newval; - struct ast_variable *var=NULL, *prev=NULL; + struct ast_variable *var = NULL, *prev = NULL; - if(!table) { - ast_log(LOG_WARNING, "MySQL RealTime: No table specified.\n"); + if (!table) { + ast_log(LOG_WARNING, "MySQL RealTime [read]: No table specified.\n"); return NULL; } /* Get the first parameter and first value in our list of passed paramater/value pairs */ newparam = va_arg(ap, const char *); newval = va_arg(ap, const char *); - if(!newparam || !newval) { - ast_log(LOG_WARNING, "MySQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n"); + if (!newparam || !newval) { + ast_log(LOG_WARNING, "MySQL RealTime [read]: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n"); mysql_close(&mysql); return NULL; } /* Must connect to the server before anything else, as the escape function requires the mysql handle. */ ast_mutex_lock(&mysql_lock); - if (!mysql_reconnect(database)) { + if (!mysql_reconnect(database,READ_OPT)) { ast_mutex_unlock(&mysql_lock); return NULL; } @@ -120,7 +133,7 @@ /* Create the first part of the query using the first parameter/value pairs we just extracted If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */ - if(!strchr(newparam, ' ')) op = " ="; else op = ""; + if (!strchr(newparam, ' ')) op = " ="; else op = ""; if ((valsz = strlen (newval)) * 2 + 1 > sizeof(buf)) valsz = (sizeof(buf) - 1) / 2; @@ -128,7 +141,7 @@ snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op, buf); while((newparam = va_arg(ap, const char *))) { newval = va_arg(ap, const char *); - if(!strchr(newparam, ' ')) op = " ="; else op = ""; + if (!strchr(newparam, ' ')) op = " ="; else op = ""; if ((valsz = strlen (newval)) * 2 + 1 > sizeof(buf)) valsz = (sizeof(buf) - 1) / 2; mysql_real_escape_string(&mysql, buf, newval, valsz); @@ -136,18 +149,18 @@ } va_end(ap); - ast_log(LOG_DEBUG, "MySQL RealTime: Retrieve SQL: %s\n", sql); + ast_log(LOG_DEBUG, "MySQL RealTime [read]: Retrieve SQL: %s\n", sql); /* Execution. */ - if(mysql_real_query(&mysql, sql, strlen(sql))) { - ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database. Check debug for more info.\n"); - ast_log(LOG_DEBUG, "MySQL RealTime: Query: %s\n", sql); - ast_log(LOG_DEBUG, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&mysql)); + if (mysql_real_query(&mysql, sql, strlen(sql))) { + ast_log(LOG_WARNING, "MySQL RealTime [read]: Failed to query database. Check debug for more info.\n"); + ast_log(LOG_DEBUG, "MySQL RealTime [read]: Query: %s\n", sql); + ast_log(LOG_DEBUG, "MySQL RealTime [read]: Query Failed because: %s\n", mysql_error(&mysql)); ast_mutex_unlock(&mysql_lock); return NULL; } - if((result = mysql_store_result(&mysql))) { + if ((result = mysql_store_result(&mysql))) { numFields = mysql_num_fields(result); fields = mysql_fetch_fields(result); @@ -156,22 +169,20 @@ stringp = row[i]; while(stringp) { chunk = strsep(&stringp, ";"); - if(chunk && !ast_strlen_zero(ast_strip(chunk))) { - if(prev) { + if (chunk && !ast_strlen_zero(ast_strip(chunk))) { + if (prev) { prev->next = ast_variable_new(fields[i].name, chunk); if (prev->next) { prev = prev->next; } - } else { + } else prev = var = ast_variable_new(fields[i].name, chunk); - } } } } } - } else { - ast_log(LOG_WARNING, "MySQL RealTime: Could not find any rows in table %s.\n", table); - } + } else + ast_log(LOG_WARNING, "MySQL RealTime [read]: Could not find any rows in table %s.\n", table); ast_mutex_unlock(&mysql_lock); mysql_free_result(result); @@ -193,11 +204,11 @@ char *op; const char *newparam, *newval; struct ast_realloca ra; - struct ast_variable *var=NULL; + struct ast_variable *var = NULL; struct ast_config *cfg = NULL; struct ast_category *cat = NULL; - if(!table) { + if (!table) { ast_log(LOG_WARNING, "MySQL RealTime: No table specified.\n"); return NULL; } @@ -207,27 +218,27 @@ cfg = ast_config_new(); if (!cfg) { /* If I can't alloc memory at this point, why bother doing anything else? */ - ast_log(LOG_WARNING, "Out of memory!\n"); + ast_log(LOG_WARNING, "MySQL RealTime [read]: Out of memory!\n"); return NULL; } /* Get the first parameter and first value in our list of passed paramater/value pairs */ newparam = va_arg(ap, const char *); newval = va_arg(ap, const char *); - if(!newparam || !newval) { - ast_log(LOG_WARNING, "MySQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n"); + if (!newparam || !newval) { + ast_log(LOG_WARNING, "MySQL RealTime [read]: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n"); mysql_close(&mysql); return NULL; } initfield = ast_strdupa(newparam); - if(initfield && (op = strchr(initfield, ' '))) { + if (initfield && (op = strchr(initfield, ' '))) { *op = '\0'; } /* Must connect to the server before anything else, as the escape function requires the mysql handle. */ ast_mutex_lock(&mysql_lock); - if (!mysql_reconnect(database)) { + if (!mysql_reconnect(database, READ_OPT)) { ast_mutex_unlock(&mysql_lock); return NULL; } @@ -235,7 +246,7 @@ /* Create the first part of the query using the first parameter/value pairs we just extracted If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */ - if(!strchr(newparam, ' ')) op = " ="; else op = ""; + if (!strchr(newparam, ' ')) op = " ="; else op = ""; if ((valsz = strlen (newval)) * 2 + 1 > sizeof(buf)) valsz = (sizeof(buf) - 1) / 2; @@ -243,47 +254,47 @@ snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op, buf); while((newparam = va_arg(ap, const char *))) { newval = va_arg(ap, const char *); - if(!strchr(newparam, ' ')) op = " ="; else op = ""; + if (!strchr(newparam, ' ')) op = " ="; else op = ""; if ((valsz = strlen (newval)) * 2 + 1 > sizeof(buf)) valsz = (sizeof(buf) - 1) / 2; mysql_real_escape_string(&mysql, buf, newval, valsz); snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s '%s'", newparam, op, buf); } - if(initfield) { + if (initfield) { snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " ORDER BY %s", initfield); } va_end(ap); - ast_log(LOG_DEBUG, "MySQL RealTime: Retrieve SQL: %s\n", sql); + ast_log(LOG_DEBUG, "MySQL RealTime [read]: Retrieve SQL: %s\n", sql); /* Execution. */ - if(mysql_real_query(&mysql, sql, strlen(sql))) { - ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database. Check debug for more info.\n"); - ast_log(LOG_DEBUG, "MySQL RealTime: Query: %s\n", sql); - ast_log(LOG_DEBUG, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&mysql)); + if (mysql_real_query(&mysql, sql, strlen(sql))) { + ast_log(LOG_WARNING, "MySQL RealTime [read]: Failed to query database. Check debug for more info.\n"); + ast_log(LOG_DEBUG, "MySQL RealTime [read]: Query: %s\n", sql); + ast_log(LOG_DEBUG, "MySQL RealTime [read]: Query Failed because: %s\n", mysql_error(&mysql)); ast_mutex_unlock(&mysql_lock); return NULL; } - if((result = mysql_store_result(&mysql))) { + if ((result = mysql_store_result(&mysql))) { numFields = mysql_num_fields(result); fields = mysql_fetch_fields(result); while((row = mysql_fetch_row(result))) { var = NULL; cat = ast_category_new(""); - if(!cat) { - ast_log(LOG_WARNING, "Out of memory!\n"); + if (!cat) { + ast_log(LOG_WARNING, "MySQL RealTime [read]: Out of memory!\n"); continue; } for(i = 0; i < numFields; i++) { stringp = row[i]; while(stringp) { chunk = strsep(&stringp, ";"); - if(chunk && !ast_strlen_zero(ast_strip(chunk))) { - if(initfield && !strcmp(initfield, fields[i].name)) { + if (chunk && !ast_strlen_zero(ast_strip(chunk))) { + if (initfield && !strcmp(initfield, fields[i].name)) { ast_category_rename(cat, chunk); } var = ast_variable_new(fields[i].name, chunk); @@ -293,9 +304,8 @@ } ast_category_append(cfg, cat); } - } else { - ast_log(LOG_WARNING, "MySQL RealTime: Could not find any rows in table %s.\n", table); - } + } else + ast_log(LOG_WARNING, "MySQL RealTime [read]: Could not find any rows in table %s.\n", table); ast_mutex_unlock(&mysql_lock); mysql_free_result(result); @@ -311,23 +321,23 @@ int valsz; const char *newparam, *newval; - if(!table) { - ast_log(LOG_WARNING, "MySQL RealTime: No table specified.\n"); + if (!table) { + ast_log(LOG_WARNING, "MySQL RealTime [write]: No table specified.\n"); return -1; } /* Get the first parameter and first value in our list of passed paramater/value pairs */ newparam = va_arg(ap, const char *); newval = va_arg(ap, const char *); - if(!newparam || !newval) { - ast_log(LOG_WARNING, "MySQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n"); - mysql_close(&mysql); + if (!newparam || !newval) { + ast_log(LOG_WARNING, "MySQL RealTime [write]: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n"); + mysql_close(&mysql_write); return -1; } /* Must connect to the server before anything else, as the escape function requires the mysql handle. */ ast_mutex_lock(&mysql_lock); - if (!mysql_reconnect(database)) { + if (!mysql_reconnect(database,WRITE_OPT)) { ast_mutex_unlock(&mysql_lock); return -1; } @@ -352,21 +362,21 @@ mysql_real_escape_string(&mysql, buf, lookup, valsz); snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " WHERE %s = '%s'", keyfield, buf); - ast_log(LOG_DEBUG,"MySQL RealTime: Update SQL: %s\n", sql); + ast_log(LOG_DEBUG,"MySQL RealTime [write]: Update SQL: %s\n", sql); /* Execution. */ - if(mysql_real_query(&mysql, sql, strlen(sql))) { - ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database. Check debug for more info.\n"); - ast_log(LOG_DEBUG, "MySQL RealTime: Query: %s\n", sql); - ast_log(LOG_DEBUG, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&mysql)); + if (mysql_real_query(&mysql_write, sql, strlen(sql))) { + ast_log(LOG_WARNING, "MySQL RealTime [write]: Failed to query database. Check debug for more info.\n"); + ast_log(LOG_DEBUG, "MySQL RealTime [write]: Query: %s\n", sql); + ast_log(LOG_DEBUG, "MySQL RealTime [write]: Query Failed because: %s\n", mysql_error(&mysql)); ast_mutex_unlock(&mysql_lock); return -1; } - numrows = mysql_affected_rows(&mysql); + numrows = mysql_affected_rows(&mysql_write); ast_mutex_unlock(&mysql_lock); - ast_log(LOG_DEBUG,"MySQL RealTime: Updated %llu rows on table: %s\n", numrows, table); + ast_log(LOG_DEBUG,"MySQL RealTime [write]: Updated %llu rows on table: %s\n", numrows, table); /* From http://dev.mysql.com/doc/mysql/en/mysql-affected-rows.html * An integer greater than zero indicates the number of rows affected @@ -374,7 +384,7 @@ * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.) */ - if(numrows >= 0) + if (numrows >= 0) return (int)numrows; return -1; @@ -393,31 +403,32 @@ last[0] = '\0'; - if(!file || !strcmp(file, RES_CONFIG_MYSQL_CONF)) { + if (!file || !strcmp(file, RES_CONFIG_MYSQL_CONF)) { ast_log(LOG_WARNING, "MySQL RealTime: Cannot configure myself.\n"); return NULL; } - snprintf(sql, sizeof(sql), "SELECT category, var_name, var_val, cat_metric FROM %s WHERE filename='%s' and commented=0 ORDER BY filename, cat_metric desc, var_metric asc, category, var_name, var_val, id", table, file); + snprintf(sql, sizeof(sql), "SELECT category, var_name, var_val, cat_metric FROM %s WHERE filename = '%s' and commented = 0 ORDER BY filename, cat_metric desc, var_metric asc, category, var_name, var_val, id", table, file); ast_log(LOG_DEBUG, "MySQL RealTime: Static SQL: %s\n", sql); /* We now have our complete statement; Lets connect to the server and execute it. */ ast_mutex_lock(&mysql_lock); - if(!mysql_reconnect(database)) { + if (!mysql_reconnect(database,READ_OPT)) { ast_mutex_unlock(&mysql_lock); return NULL; } - if(mysql_real_query(&mysql, sql, strlen(sql))) { - ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database. Check debug for more info.\n"); - ast_log(LOG_DEBUG, "MySQL RealTime: Query: %s\n", sql); - ast_log(LOG_DEBUG, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&mysql)); + if (mysql_real_query(&mysql, sql, strlen(sql))) { + ast_log(LOG_WARNING, "MySQL RealTime [read]: Failed to query database. Check debug for more info.\n"); + ast_log(LOG_DEBUG, "MySQL RealTime [read]: Query: %s\n", sql); + ast_log(LOG_DEBUG, "MySQL RealTime [read]: Query Failed because: %s\n", mysql_error(&mysql)); ast_mutex_unlock(&mysql_lock); return NULL; } - if((result = mysql_store_result(&mysql))) { + + if ((result = mysql_store_result(&mysql))) { num_rows = mysql_num_rows(result); ast_log(LOG_DEBUG, "MySQL RealTime: Found %llu rows.\n", num_rows); @@ -425,7 +436,7 @@ but I believe that would require another loop that we don't need. */ while((row = mysql_fetch_row(result))) { - if(!strcmp(row[1], "#include")) { + if (!strcmp(row[1], "#include")) { if (!ast_config_internal_load(row[2], cfg, 0)) { mysql_free_result(result); ast_mutex_unlock(&mysql_lock); @@ -434,7 +445,7 @@ continue; } - if(strcmp(last, row[0]) || last_cat_metric != atoi(row[3])) { + if (strcmp(last, row[0]) || last_cat_metric != atoi(row[3])) { cur_cat = ast_category_new(row[0]); if (!cur_cat) { ast_log(LOG_WARNING, "Out of memory!\n"); @@ -447,9 +458,8 @@ new_v = ast_variable_new(row[1], row[2]); ast_variable_append(cur_cat, new_v); } - } else { + } else ast_log(LOG_WARNING, "MySQL RealTime: Could not find config '%s' in database.\n", file); - } mysql_free_result(result); ast_mutex_unlock(&mysql_lock); @@ -471,13 +481,17 @@ ast_mutex_lock(&mysql_lock); - if(!mysql_reconnect(NULL)) { - ast_log(LOG_WARNING, "MySQL RealTime: Couldn't establish connection. Check debug.\n"); - ast_log(LOG_DEBUG, "MySQL RealTime: Cannot Connect: %s\n", mysql_error(&mysql)); + if (!mysql_reconnect(NULL,READ_OPT)) { + ast_log(LOG_WARNING, "MySQL RealTime [read]: Couldn't establish connection. Check debug.\n"); + ast_log(LOG_DEBUG, "MySQL RealTime [read]: Cannot Connect: %s\n", mysql_error(&mysql)); } + if (!mysql_reconnect(NULL,WRITE_OPT)) { + ast_log(LOG_WARNING, "MySQL RealTime [write]: Couldn't establish connection. Check debug.\n"); + ast_log(LOG_DEBUG, "MySQL RealTime [write]: Cannot Connect: %s\n", mysql_error(&mysql_write)); + } ast_config_engine_register(&mysql_engine); - if(option_verbose) { + if (option_verbose) { ast_verbose("MySQL RealTime driver loaded.\n"); } ast_cli_register(&cli_realtime_mysql_status); @@ -493,9 +507,10 @@ ast_mutex_lock(&mysql_lock); mysql_close(&mysql); + mysql_close(&mysql_write); ast_cli_unregister(&cli_realtime_mysql_status); ast_config_engine_deregister(&mysql_engine); - if(option_verbose) { + if (option_verbose) { ast_verbose("MySQL RealTime unloaded.\n"); } @@ -513,14 +528,21 @@ ast_mutex_lock(&mysql_lock); mysql_close(&mysql); + mysql_close(&mysql_write); connected = 0; + connected_write = 0; parse_config(); - if(!mysql_reconnect(NULL)) { - ast_log(LOG_WARNING, "MySQL RealTime: Couldn't establish connection. Check debug.\n"); - ast_log(LOG_DEBUG, "MySQL RealTime: Cannot Connect: %s\n", mysql_error(&mysql)); + if (!mysql_reconnect(NULL,READ_OPT)) { + ast_log(LOG_WARNING, "MySQL RealTime [read]: Couldn't establish connection. Check debug.\n"); + ast_log(LOG_DEBUG, "MySQL RealTime [read]: Cannot Connect: %s\n", mysql_error(&mysql)); } + if (!mysql_reconnect(NULL,WRITE_OPT)) { + ast_log(LOG_WARNING, "MySQL RealTime [write]: Couldn't establish connection. Check debug.\n"); + ast_log(LOG_DEBUG, "MySQL RealTime [write]: Cannot Connect: %s\n", mysql_error(&mysql_write)); + } + ast_verbose(VERBOSE_PREFIX_2 "MySQL RealTime reloaded.\n"); /* Done reloading. Release lock so others can now use driver. */ @@ -536,157 +558,295 @@ config = ast_config_load(RES_CONFIG_MYSQL_CONF); - if(config) { - if(!(s=ast_variable_retrieve(config, "general", "dbuser"))) { - ast_log(LOG_WARNING, "MySQL RealTime: No database user found, using 'asterisk' as default.\n"); + if (config) { + /* The modificaions here allow us to have single database setups (using the general section) + * for backwards compatibility and separate read/write (using "read" and "write" sections */ + if (s = ast_variable_retrieve(config, "general", "dbuser")) { + strncpy(dbuser, s, sizeof(dbuser) - 1); + strncpy(dbuser_write, s, sizeof(dbuser_write) - 1); + } else { strncpy(dbuser, "asterisk", sizeof(dbuser) - 1); - } else { + strncpy(dbuser_write, "asterisk", sizeof(dbuser_write) - 1); + } + if (!(s = ast_variable_retrieve(config, "read", "dbuser"))) + ast_log(LOG_DEBUG, "MySQL RealTime: No database read user found, using '%s' as default.\n", dbuser); + else strncpy(dbuser, s, sizeof(dbuser) - 1); - } + if (!(s = ast_variable_retrieve(config, "write", "dbuser"))) + ast_log(LOG_DEBUG, "MySQL RealTime: No database write user found, using '%s' as default.\n", dbuser_write); + else + strncpy(dbuser_write, s, sizeof(dbuser_write) - 1); - if(!(s=ast_variable_retrieve(config, "general", "dbpass"))) { - ast_log(LOG_WARNING, "MySQL RealTime: No database password found, using 'asterisk' as default.\n"); + if (s = ast_variable_retrieve(config, "general", "dbpass")) { + strncpy(dbpass, s, sizeof(dbpass) - 1); + strncpy(dbpass_write, s, sizeof(dbpass_write) - 1); + } else { strncpy(dbpass, "asterisk", sizeof(dbpass) - 1); - } else { + strncpy(dbpass_write, "asterisk", sizeof(dbpass_write) - 1); + } + if (!(s = ast_variable_retrieve(config, "read", "dbpass"))) + ast_log(LOG_DEBUG, "MySQL RealTime: No database read password found, using '%s' as default.\n", dbpass); + else strncpy(dbpass, s, sizeof(dbpass) - 1); - } + if (!(s = ast_variable_retrieve(config, "write", "dbpass"))) + ast_log(LOG_DEBUG, "MySQL RealTime: No database write password found, using '%s' as default.\n", dbpass_write); + else + strncpy(dbpass_write, s, sizeof(dbpass_write) - 1); - if(!(s=ast_variable_retrieve(config, "general", "dbhost"))) { - ast_log(LOG_WARNING, "MySQL RealTime: No database host found, using localhost via socket.\n"); + if (s = ast_variable_retrieve(config, "general", "dbhost")) { + strncpy(dbhost, s, sizeof(dbhost) - 1); + strncpy(dbhost_write, s, sizeof(dbhost_write) - 1); + } else { dbhost[0] = '\0'; - } else { - strncpy(dbhost, s, sizeof(dbhost) - 1); - } - - if(!(s=ast_variable_retrieve(config, "general", "dbname"))) { - ast_log(LOG_WARNING, "MySQL RealTime: No database name found, using 'asterisk' as default.\n"); + dbhost_write[0] = '\0'; + } + if (!(s = ast_variable_retrieve(config, "read", "dbhost"))) { + ast_log(LOG_DEBUG, "MySQL RealTime: No database read host found, using '%s' as default.\n", dbhost); + ast_log(LOG_DEBUG, "MySQL RealTime: If the read host is set to localhost, we will connect via socket.\n"); + } else + strncpy(dbhost, s, sizeof(dbhost) - 1); + if (!(s = ast_variable_retrieve(config, "write", "dbhost"))) { + ast_log(LOG_DEBUG, "MySQL RealTime: No database write host found, using '%s' as default.\n", dbhost_write); + ast_log(LOG_DEBUG, "MySQL RealTime: If the write host is set to localhost, we will connect via socket.\n"); + } else + strncpy(dbhost_write, s, sizeof(dbhost_write) - 1); + + if (s = ast_variable_retrieve(config, "general", "dbname")) { + strncpy(dbname, s, sizeof(dbname) - 1); + strncpy(dbname_write, s, sizeof(dbname_write) - 1); + } else { strncpy(dbname, "asterisk", sizeof(dbname) - 1); - } else { + strncpy(dbname_write, "asterisk", sizeof(dbname_write) - 1); + } + if (!(s = ast_variable_retrieve(config, "read", "dbname"))) + ast_log(LOG_DEBUG, "MySQL RealTime: No database read name found, using '%s' as default.\n", dbname); + else strncpy(dbname, s, sizeof(dbname) - 1); - } - - if(!(s=ast_variable_retrieve(config, "general", "dbport"))) { - ast_log(LOG_WARNING, "MySQL RealTime: No database port found, using 3306 as default.\n"); + if (!(s = ast_variable_retrieve(config, "write", "dbname"))) + ast_log(LOG_DEBUG, "MySQL RealTime: No database write name found, using '%s' as default.\n", dbname_write); + else + strncpy(dbname_write, s, sizeof(dbname_write) - 1); + + if (s = ast_variable_retrieve(config, "general", "dbport")) { + dbport = atoi(s); + dbport_write = atoi(s); + } else { dbport = 3306; - } else { + dbport_write = 3306; + } + if (!(s = ast_variable_retrieve(config, "read", "dbport"))) + ast_log(LOG_DEBUG, "MySQL RealTime: No database read port found, using %i as default.\n", dbport); + else dbport = atoi(s); - } + if (!(s = ast_variable_retrieve(config, "write", "dbport"))) + ast_log(LOG_DEBUG, "MySQL RealTime: No database write port found, using %i as default.\n", dbport_write); + else + dbport_write = atoi(s); - if(dbhost && !(s=ast_variable_retrieve(config, "general", "dbsock"))) { - ast_log(LOG_WARNING, "MySQL RealTime: No database socket found, using '/tmp/mysql.sock' as default.\n"); + if (s = ast_variable_retrieve(config, "general", "dbsock")) { + strncpy(dbsock, s, sizeof(dbsock) - 1); + strncpy(dbsock_write, s, sizeof(dbsock_write) - 1); + } else { strncpy(dbsock, "/tmp/mysql.sock", sizeof(dbsock) - 1); - } else { + strncpy(dbsock_write, "/tmp/mysql_write.sock", sizeof(dbsock_write) - 1); + } + if (dbhost && !(s = ast_variable_retrieve(config, "read", "dbsock"))) + ast_log(LOG_DEBUG, "MySQL RealTime: No database read socket found, using '%s' as default.\n", dbsock); + else strncpy(dbsock, s, sizeof(dbsock) - 1); - } + if (dbhost_write && !(s = ast_variable_retrieve(config, "write", "dbsock"))) + ast_log(LOG_DEBUG, "MySQL RealTime: No database write socket found, using '%s' as default.\n", dbsock_write); + else + strncpy(dbsock_write, s, sizeof(dbsock_write) - 1); } ast_config_destroy(config); - if(dbhost) { - ast_log(LOG_DEBUG, "MySQL RealTime Host: %s\n", dbhost); - ast_log(LOG_DEBUG, "MySQL RealTime Port: %i\n", dbport); - } else { - ast_log(LOG_DEBUG, "MySQL RealTime Socket: %s\n", dbsock); - } - ast_log(LOG_DEBUG, "MySQL RealTime User: %s\n", dbuser); - ast_log(LOG_DEBUG, "MySQL RealTime Password: %s\n", dbpass); + if (dbhost) { + ast_log(LOG_DEBUG, "MySQL RealTime READ Host: %s\n", dbhost); + ast_log(LOG_DEBUG, "MySQL RealTime READ Port: %i\n", dbport); + } else + ast_log(LOG_DEBUG, "MySQL RealTime READ Socket: %s\n", dbsock); + ast_log(LOG_DEBUG, "MySQL RealTime READ User: %s\n", dbuser); + ast_log(LOG_DEBUG, "MySQL RealTime READ Password: %s\n", dbpass); + if (dbhost_write) { + ast_log(LOG_DEBUG, "MySQL RealTime WRITE Host: %s\n", dbhost_write); + ast_log(LOG_DEBUG, "MySQL RealTime WRITE Port: %i\n", dbport_write); + } else + ast_log(LOG_DEBUG, "MySQL RealTime WRITE Socket: %s\n", dbsock_write); + ast_log(LOG_DEBUG, "MySQL RealTime WRITE User: %s\n", dbuser_write); + ast_log(LOG_DEBUG, "MySQL RealTime WRITE Password: %s\n", dbpass_write); + return 1; } -static int mysql_reconnect(const char *database) +static int mysql_reconnect(const char *database, int write) { char my_database[50]; #ifdef MYSQL_OPT_RECONNECT my_bool trueval = 1; #endif - if(!database || ast_strlen_zero(database)) - ast_copy_string(my_database, dbname, sizeof(my_database)); + if (!database || ast_strlen_zero(database)) + if (!write) /* READ */ + ast_copy_string(my_database, dbname, sizeof(my_database)); + else + ast_copy_string(my_database, dbname_write, sizeof(my_database)); else ast_copy_string(my_database, database, sizeof(my_database)); /* mutex lock should have been locked before calling this function. */ -reconnect_tryagain: - if((!connected) && (dbhost || dbsock) && dbuser && dbpass && my_database) { - if(!mysql_init(&mysql)) { - ast_log(LOG_WARNING, "MySQL RealTime: Insufficient memory to allocate MySQL resource.\n"); - connected = 0; - return 0; - } - if(mysql_real_connect(&mysql, dbhost, dbuser, dbpass, my_database, dbport, dbsock, 0)) { + if (!write) { /* READ */ +reconnect_read: + if ((!connected) && (dbhost || dbsock) && dbuser && dbpass && my_database) { + if (!mysql_init(&mysql)) { + ast_log(LOG_WARNING, "MySQL RealTime [read]: Insufficient memory to allocate MySQL resource.\n"); + connected = 0; + return 0; + } + if (mysql_real_connect(&mysql, dbhost, dbuser, dbpass, my_database, dbport, dbsock, 0)) { #ifdef MYSQL_OPT_RECONNECT - /* The default is no longer to automatically reconnect on failure, - * (as of 5.0.3) so we have to set that option here. */ - mysql_options(&mysql, MYSQL_OPT_RECONNECT, &trueval); + /* The default is no longer to automatically reconnect on failure, + * (as of 5.0.3) so we have to set that option here. */ + mysql_options(&mysql, MYSQL_OPT_RECONNECT, &trueval); #endif - ast_log(LOG_DEBUG, "MySQL RealTime: Successfully connected to database.\n"); + ast_log(LOG_DEBUG, "MySQL RealTime [read]: Successfully connected to database.\n"); + connected = 1; + connect_time = time(NULL); + return 1; + } else { + ast_log(LOG_ERROR, "MySQL RealTime [read]: Failed to connect database server %s on %s (err %d). Check debug for more info.\n", dbname, dbhost, mysql_errno(&mysql)); + ast_log(LOG_DEBUG, "MySQL RealTime [read]: Cannot Connect (%d): %s\n", mysql_errno(&mysql), mysql_error(&mysql)); + connected = 0; + return 0; + } + } else { + /* MySQL likes to return an error, even if it reconnects successfully. + * So the postman pings twice. */ + if (mysql_ping(&mysql) != 0 && mysql_ping(&mysql) != 0) { + connected = 0; + ast_log(LOG_ERROR, "MySQL RealTime [read]: Ping failed (%d). Trying an explicit reconnect.\n", mysql_errno(&mysql)); + ast_log(LOG_DEBUG, "MySQL RealTime [read]: Server Error (%d): %s\n", mysql_errno(&mysql), mysql_error(&mysql)); + goto reconnect_read; + } + connected = 1; - connect_time = time(NULL); + + if (mysql_select_db(&mysql, my_database) != 0) { + ast_log(LOG_WARNING, "MySQL RealTime [read]: Unable to select database: %s. Still Connected (%d).\n", my_database, mysql_errno(&mysql)); + ast_log(LOG_DEBUG, "MySQL RealTime [read]: Database Select Failed (%d): %s\n", mysql_error(&mysql), mysql_errno(&mysql)); + return 0; + } + + ast_log(LOG_DEBUG, "MySQL RealTime [read]: Everything is fine.\n"); return 1; + } + } else { /* WRITE */ +reconnect_write: + if ((!connected_write) && (dbhost_write || dbsock_write) && dbuser_write && dbpass_write && my_database) { + if (!mysql_init(&mysql_write)) { + ast_log(LOG_WARNING, "MySQL Realtime [write]: Insufficient memory to allocate MySQL resource.\n"); + connected_write = 0; + return 0; + } + if (mysql_real_connect(&mysql_write, dbhost_write, dbuser_write, dbpass_write, my_database, dbport_write, dbsock_write, 0)) { +#ifdef MYSQL_OPT_RECONNECT + /* The default is no longer to automatically connect on failure, + * (as of 5.0.3) so we have to set that option here. */ + mysql_options(&mysql_write, MYSQL_OPT_RECONNECT, &trueval); +#endif + ast_log(LOG_DEBUG, "MySQL Realtime [write]: Successfully connected to database.\n"); + connected_write = 1; + connect_time_write = time(NULL); + return 1; + } else { + ast_log(LOG_ERROR, "MySQL Realtime [write]: Failed to connect to database server %s on %s (err %d). Check debug for more info.\n", dbname_write, dbhost_write, mysql_errno(&mysql_write)); + ast_log(LOG_DEBUG, "MySQL Realtime [write]: Cannot Connect (%d): %s\n", mysql_errno(&mysql_write), mysql_error(&mysql_write)); + connected_write = 0; + return 0; + } } else { - ast_log(LOG_ERROR, "MySQL RealTime: Failed to connect database server %s on %s (err %d). Check debug for more info.\n", dbname, dbhost, mysql_errno(&mysql)); - ast_log(LOG_DEBUG, "MySQL RealTime: Cannot Connect (%d): %s\n", mysql_errno(&mysql), mysql_error(&mysql)); - connected = 0; - return 0; - } - } else { - /* MySQL likes to return an error, even if it reconnects successfully. - * So the postman pings twice. */ - if (mysql_ping(&mysql) != 0 && mysql_ping(&mysql) != 0) { - connected = 0; - ast_log(LOG_ERROR, "MySQL RealTime: Ping failed (%d). Trying an explicit reconnect.\n", mysql_errno(&mysql)); - ast_log(LOG_DEBUG, "MySQL RealTime: Server Error (%d): %s\n", mysql_errno(&mysql), mysql_error(&mysql)); - goto reconnect_tryagain; - } + /* MySQL likes to return an error, even if it reconnects successfully. + * So the postman pings twice. */ + if (mysql_ping(&mysql_write) != 0 && mysql_ping(&mysql_write) != 0) { + connected_write = 0; + ast_log(LOG_ERROR, "MySQL RealTime [write]: Ping failed (%d). Trying an explicit reconnect.\n", mysql_errno(&mysql_write)); + ast_log(LOG_DEBUG, "MySQL RealTime [write]: Server Error (%d): %s\n", mysql_errno(&mysql_write), mysql_error(&mysql_write)); + goto reconnect_write; + } - connected = 1; + connected_write = 1; - if(mysql_select_db(&mysql, my_database) != 0) { - ast_log(LOG_WARNING, "MySQL RealTime: Unable to select database: %s. Still Connected (%d).\n", my_database, mysql_errno(&mysql)); - ast_log(LOG_DEBUG, "MySQL RealTime: Database Select Failed (%d): %s\n", mysql_error(&mysql), mysql_errno(&mysql)); - return 0; + if (mysql_select_db(&mysql_write, my_database) != 0) { + ast_log(LOG_WARNING, "MySQL RealTime [write]: Unable to select database: %s. Still Connected (%d).\n", my_database, mysql_errno(&mysql_write)); + ast_log(LOG_DEBUG, "MySQL RealTime [write]: Database Select Failed (%d): %s\n", mysql_error(&mysql_write), mysql_errno(&mysql_write)); + return 0; + } + + ast_log(LOG_DEBUG, "MySQL RealTime [write]: Everything is fine.\n"); + return 1; } - - ast_log(LOG_DEBUG, "MySQL RealTime: Everything is fine.\n"); - return 1; } } static int realtime_mysql_status(int fd, int argc, char **argv) { char status[256], status2[100] = ""; + char status3[256], status4[100] = ""; int ctime = time(NULL) - connect_time; + int ctime2 = time(NULL) - connect_time_write; + int connect_status; + int connect_status2; - if(mysql_reconnect(NULL)) { - if(dbhost) { - snprintf(status, 255, "Connected to %s@%s, port %d", dbname, dbhost, dbport); - } else if(dbsock) { - snprintf(status, 255, "Connected to %s on socket file %s", dbname, dbsock); - } else { - snprintf(status, 255, "Connected to %s@%s", dbname, dbhost); - } + if (mysql_reconnect(NULL,READ_OPT)) { + connect_status = 1; + if (dbhost) + snprintf(status, 255, "[read]: Connected to %s@%s, port %d", dbname, dbhost, dbport); + else if (dbsock) + snprintf(status, 255, "[read]: Connected to %s on socket file %s", dbname, dbsock); + else + snprintf(status, 255, "[read]: Connected to %s@%s", dbname, dbhost); - if(dbuser && *dbuser) { + if (dbuser && *dbuser) snprintf(status2, 99, " with username %s", dbuser); - } - if (ctime > 31536000) { + if (ctime > 31536000) ast_cli(fd, "%s%s for %d years, %d days, %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 31536000, (ctime % 31536000) / 86400, (ctime % 86400) / 3600, (ctime % 3600) / 60, ctime % 60); - } else if (ctime > 86400) { + else if (ctime > 86400) ast_cli(fd, "%s%s for %d days, %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 86400, (ctime % 86400) / 3600, (ctime % 3600) / 60, ctime % 60); - } else if (ctime > 3600) { + else if (ctime > 3600) ast_cli(fd, "%s%s for %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 3600, (ctime % 3600) / 60, ctime % 60); - } else if (ctime > 60) { + else if (ctime > 60) ast_cli(fd, "%s%s for %d minutes, %d seconds.\n", status, status2, ctime / 60, ctime % 60); - } else { + else ast_cli(fd, "%s%s for %d seconds.\n", status, status2, ctime); - } + } + + if (mysql_reconnect(NULL,WRITE_OPT)) { + connect_status2 = 1; + if (dbhost_write) + snprintf(status3, 255, "[write]: Connected to %s@%s, port %d", dbname_write, dbhost_write, dbport_write); + else if (dbsock_write) + snprintf(status3, 255, "[write]: Connected to %s on socket file %s", dbname_write, dbsock_write); + else + snprintf(status3, 255, "[write]: Connected to %s@%s", dbname_write, dbhost_write); - return RESULT_SUCCESS; - } else { - return RESULT_FAILURE; + if (dbuser_write && *dbuser_write) + snprintf(status4, 99, " with username %s", dbuser_write); + if (ctime2 > 31536000) + ast_cli(fd, "%s%s for %d years, %d days, %d hours, %d minutes, %d seconds.\n", status3, status4, ctime2 / 31536000, (ctime2 % 31536000) / 86400, (ctime2 % 86400) / 3600, (ctime2 % 3600) / 60, ctime2 % 60); + else if (ctime2 > 86400) + ast_cli(fd, "%s%s for %d days, %d hours, %d minutes, %d seconds.\n", status3, status4, ctime2 / 86400, (ctime2 % 86400) / 3600, (ctime2 % 3600) / 60, ctime2 % 60); + else if (ctime2 > 3600) + ast_cli(fd, "%s%s for %d hours, %d minutes, %d seconds.\n", status3, status4, ctime2 / 3600, (ctime2 % 3600) / 60, ctime2 % 60); + else if (ctime2 > 60) + ast_cli(fd, "%s%s for %d minutes, %d seconds.\n", status3, status4, ctime2 / 60, ctime2 % 60); + else + ast_cli(fd, "%s%s for %d seconds.\n", status3, status4, ctime2); } + + if (connect_status == 1 && connect_status2 == 1) return RESULT_SUCCESS; else return RESULT_FAILURE; } AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "MySQL RealTime Configuration Driver"); Index: configs/res_mysql.conf.sample =================================================================== --- configs/res_mysql.conf.sample (revision 315) +++ configs/res_mysql.conf.sample (working copy) @@ -13,3 +13,19 @@ ;dbpass = mypass ;dbport = 3306 ;dbsock = /tmp/mysql.sock + +[read] +;dbhost = 127.0.0.1 +;dbname = asterisk +;dbuser = myuser +;dbpass = mypass +;dbport = 3306 +;dbsock = /tmp/mysql.sock + +[write] +;dbhost = 127.0.0.1 +;dbname = asterisk +;dbuser = myuser +;dbpass = mypass +;dbport = 3306 +;dbsock = /tmp/mysql.sock