Index: res_config_mysql.c =================================================================== --- res_config_mysql.c (revision 356) +++ res_config_mysql.c (working copy) @@ -60,19 +60,26 @@ AST_MUTEX_DEFINE_STATIC(mysql_lock); #define RES_CONFIG_MYSQL_CONF "res_mysql.conf" -MYSQL mysql; -static char dbhost[50]; -static char dbuser[50]; -static char dbpass[50]; -static char dbname[50]; -static char dbsock[50]; -static int dbport; -static int connected; -static time_t connect_time; +#define READHANDLE 0 +#define WRITEHANDLE 1 +static struct mysql_conn { + ast_mutex_t lock; + MYSQL handle; + char host[50]; + char name[50]; + char user[50]; + char pass[50]; + char sock[50]; + int port; + int connected; + time_t connect_time; +} dbread, dbwrite; + static int parse_config(void); -static int mysql_reconnect(const char *database); +static int mysql_reconnect(struct mysql_conn *conn); static int realtime_mysql_status(int fd, int argc, char **argv); +static int load_mysql_config(struct ast_config *config, const char *category, struct mysql_conn *conn); static char cli_realtime_mysql_status_usage[] = "Usage: realtime mysql status\n" @@ -106,14 +113,13 @@ 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); 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)) { - ast_mutex_unlock(&mysql_lock); + ast_mutex_lock(&dbread.lock); + if (!mysql_reconnect(&dbread)) { + ast_mutex_unlock(&dbread.lock); return NULL; } @@ -127,7 +133,7 @@ if ((valsz = strlen (newval)) * 2 + 1 > sizeof(buf)) valsz = (sizeof(buf) - 1) / 2; - mysql_real_escape_string(&mysql, buf, newval, valsz); + mysql_real_escape_string(&dbread.handle, buf, newval, valsz); 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 *); @@ -137,7 +143,7 @@ op = ""; if ((valsz = strlen (newval)) * 2 + 1 > sizeof(buf)) valsz = (sizeof(buf) - 1) / 2; - mysql_real_escape_string(&mysql, buf, newval, valsz); + mysql_real_escape_string(&dbread.handle, buf, newval, valsz); snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s '%s'", newparam, op, buf); } va_end(ap); @@ -145,23 +151,21 @@ ast_log(LOG_DEBUG, "MySQL RealTime: Retrieve SQL: %s\n", sql); /* Execution. */ - if (mysql_real_query(&mysql, sql, strlen(sql))) { + if (mysql_real_query(&dbread.handle, 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)); - ast_mutex_unlock(&mysql_lock); + ast_log(LOG_DEBUG, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&dbread.handle)); + ast_mutex_unlock(&dbread.lock); return NULL; } - if ((result = mysql_store_result(&mysql))) { + if ((result = mysql_store_result(&dbread.handle))) { numFields = mysql_num_fields(result); fields = mysql_fetch_fields(result); while ((row = mysql_fetch_row(result))) { for (i = 0; i < numFields; i++) { - stringp = row[i]; - while (stringp) { - chunk = strsep(&stringp, ";"); + for (stringp = ast_strdupa(row[i]), chunk = strsep(&stringp, ";"); chunk; chunk = strsep(&stringp, ";")) { if (chunk && !ast_strlen_zero(ast_strip(chunk))) { if (prev) { prev->next = ast_variable_new(fields[i].name, chunk); @@ -175,11 +179,11 @@ } } } - } else { + } else { ast_log(LOG_WARNING, "MySQL RealTime: Could not find any rows in table %s.\n", table); } - ast_mutex_unlock(&mysql_lock); + ast_mutex_unlock(&dbread.lock); mysql_free_result(result); return var; @@ -199,7 +203,7 @@ 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; @@ -222,7 +226,7 @@ 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); + ast_config_destroy(cfg); return NULL; } @@ -233,26 +237,30 @@ /* 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)) { - ast_mutex_unlock(&mysql_lock); + if (!mysql_reconnect(&dbread)) { + ast_mutex_unlock(&dbread.lock); + ast_config_destroy(cfg); return NULL; } /* 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; - mysql_real_escape_string(&mysql, buf, newval, valsz); + mysql_real_escape_string(&dbread.handle, buf, newval, valsz); 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 ((valsz = strlen (newval)) * 2 + 1 > sizeof(buf)) + if ((valsz = strlen(newval)) * 2 + 1 > sizeof(buf)) valsz = (sizeof(buf) - 1) / 2; - mysql_real_escape_string(&mysql, buf, newval, valsz); + mysql_real_escape_string(&dbread.handle, buf, newval, valsz); snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s '%s'", newparam, op, buf); } @@ -265,15 +273,15 @@ ast_log(LOG_DEBUG, "MySQL RealTime: Retrieve SQL: %s\n", sql); /* Execution. */ - if (mysql_real_query(&mysql, sql, strlen(sql))) { + if (mysql_real_query(&dbread.handle, 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)); + ast_log(LOG_DEBUG, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&dbread.handle)); ast_mutex_unlock(&mysql_lock); return NULL; } - if ((result = mysql_store_result(&mysql))) { + if ((result = mysql_store_result(&dbread.handle))) { numFields = mysql_num_fields(result); fields = mysql_fetch_fields(result); @@ -285,9 +293,7 @@ continue; } for (i = 0; i < numFields; i++) { - stringp = row[i]; - while (stringp) { - chunk = strsep(&stringp, ";"); + for (stringp = ast_strdupa(row[i]), chunk = strsep(&stringp, ";"); chunk; chunk = strsep(&stringp, ";")) { if (chunk && !ast_strlen_zero(ast_strip(chunk))) { if (initfield && !strcmp(initfield, fields[i].name)) { ast_category_rename(cat, chunk); @@ -303,7 +309,7 @@ ast_log(LOG_WARNING, "MySQL RealTime: Could not find any rows in table %s.\n", table); } - ast_mutex_unlock(&mysql_lock); + ast_mutex_unlock(&dbread.lock); mysql_free_result(result); return cfg; @@ -319,7 +325,7 @@ if (!table) { ast_log(LOG_WARNING, "MySQL RealTime: No table specified.\n"); - return -1; + return -1; } /* Get the first parameter and first value in our list of passed paramater/value pairs */ @@ -327,52 +333,51 @@ 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); - return -1; + 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)) { - ast_mutex_unlock(&mysql_lock); + ast_mutex_lock(&dbwrite.lock); + if (!mysql_reconnect(&dbwrite)) { + ast_mutex_unlock(&dbwrite.lock); return -1; } /* 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 ((valsz = strlen (newval)) * 1 + 1 > sizeof(buf)) + if ((valsz = strlen(newval)) * 2 + 1 > sizeof(buf)) valsz = (sizeof(buf) - 1) / 2; - mysql_real_escape_string(&mysql, buf, newval, valsz); + mysql_real_escape_string(&dbwrite.handle, buf, newval, valsz); snprintf(sql, sizeof(sql), "UPDATE %s SET %s = '%s'", table, newparam, buf); while((newparam = va_arg(ap, const char *))) { newval = va_arg(ap, const char *); - if ((valsz = strlen (newval)) * 2 + 1 > sizeof(buf)) + if ((valsz = strlen(newval)) * 2 + 1 > sizeof(buf)) valsz = (sizeof(buf) - 1) / 2; - mysql_real_escape_string(&mysql, buf, newval, valsz); + mysql_real_escape_string(&dbwrite.handle, buf, newval, valsz); snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", %s = '%s'", newparam, buf); } va_end(ap); - if ((valsz = strlen (lookup)) * 1 + 1 > sizeof(buf)) + if ((valsz = strlen(lookup)) * 2 + 1 > sizeof(buf)) valsz = (sizeof(buf) - 1) / 2; - mysql_real_escape_string(&mysql, buf, lookup, valsz); + mysql_real_escape_string(&dbwrite.handle, 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: Update SQL: %s\n", sql); /* Execution. */ - if (mysql_real_query(&mysql, sql, strlen(sql))) { + if (mysql_real_query(&dbwrite.handle, 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)); - ast_mutex_unlock(&mysql_lock); + ast_log(LOG_DEBUG, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&dbwrite.handle)); + ast_mutex_unlock(&dbwrite.lock); return -1; } - numrows = mysql_affected_rows(&mysql); - ast_mutex_unlock(&mysql_lock); + numrows = mysql_affected_rows(&dbwrite.handle); + ast_mutex_unlock(&dbwrite.lock); - ast_log(LOG_DEBUG,"MySQL RealTime: Updated %llu rows on table: %s\n", numrows, table); + ast_log(LOG_DEBUG, "MySQL RealTime: 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 @@ -380,10 +385,7 @@ * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.) */ - if (numrows >= 0) - return (int)numrows; - - return -1; + return (int)numrows; } static int store_mysql(const char *database, const char *table, va_list ap) @@ -405,65 +407,61 @@ newval = va_arg(ap, const char *); if (!newparam || !newval) { ast_log(LOG_WARNING, "MySQL RealTime: Realtime storage requires at least 1 parameter and 1 value to search on.\n"); - mysql_close(&mysql); 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)) { - ast_mutex_unlock(&mysql_lock); + ast_mutex_lock(&dbwrite.lock); + if (!mysql_reconnect(&dbwrite)) { + ast_mutex_unlock(&dbwrite.lock); return -1; } /* 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 ((valsz = strlen(newval)) * 1 + 1 > sizeof(buf)) + if ((valsz = strlen(newval)) * 2 + 1 > sizeof(buf)) valsz = (sizeof(buf) - 1) / 2; if (newval) { - mysql_real_escape_string(&mysql, buf, newval, valsz); + mysql_real_escape_string(&dbwrite.handle, buf, newval, valsz); } else { - buf[0] = 0; + buf[0] = '\0'; } - snprintf(fields, sizeof(fields), "%s",newparam); - snprintf(values, sizeof(values), "'%s'",buf); + snprintf(fields, sizeof(fields), "%s", newparam); + snprintf(values, sizeof(values), "'%s'", buf); while ((newparam = va_arg(ap, const char *))) { newval = va_arg(ap, const char *); if (newval) { if ((valsz = strlen (newval)) * 2 + 1 > sizeof(buf)) valsz = (sizeof(buf) - 1) / 2; - mysql_real_escape_string(&mysql, buf, newval, valsz); + mysql_real_escape_string(&dbwrite.handle, buf, newval, valsz); } else { - buf[0] = 0; + buf[0] = '\0'; } - snprintf(fields + strlen(fields), sizeof(fields), ", %s",newparam); - snprintf(values + strlen(values), sizeof(values), ", '%s'",buf); + snprintf(fields + strlen(fields), sizeof(fields), ", %s", newparam); + snprintf(values + strlen(values), sizeof(values), ", '%s'", buf); } va_end(ap); snprintf(sql, sizeof(sql), "INSERT into %s (%s) values (%s)", table, fields, values); ast_log(LOG_DEBUG,"MySQL RealTime: Insert SQL: %s\n", sql); /* Execution. */ - if (mysql_real_query(&mysql, sql, strlen(sql))) { + if (mysql_real_query(&dbwrite.handle, 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)); - ast_mutex_unlock(&mysql_lock); + ast_log(LOG_DEBUG, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&dbwrite.handle)); + ast_mutex_unlock(&dbwrite.lock); return -1; } - insertid = mysql_insert_id(&mysql); - ast_mutex_unlock(&mysql_lock); + insertid = mysql_insert_id(&dbwrite.handle); + ast_mutex_unlock(&dbwrite.lock); - ast_log(LOG_DEBUG,"MySQL RealTime: row inserted on table: %s, id: %llu\n", table, insertid); + ast_log(LOG_DEBUG, "MySQL RealTime: row inserted on table: %s, id: %llu\n", table, insertid); /* From http://dev.mysql.com/doc/mysql/en/mysql-affected-rows.html * An integer greater than zero indicates the number of rows affected * Zero indicates that no records were updated * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.) */ - if (insertid >= 0) - return (int)insertid; - - return -1; + return (int)insertid; } static int destroy_mysql(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap) @@ -484,13 +482,12 @@ newval = va_arg(ap, const char *);*/ if (ast_strlen_zero(keyfield) || ast_strlen_zero(lookup)) { ast_log(LOG_WARNING, "MySQL RealTime: Realtime destroying requires at least 1 parameter and 1 value to search on.\n"); - mysql_close(&mysql); 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(&dbwrite)) { ast_mutex_unlock(&mysql_lock); return -1; } @@ -498,38 +495,34 @@ /* 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 ((valsz = strlen (lookup)) * 1 + 1 > sizeof(buf)) + if ((valsz = strlen (lookup)) * 2 + 1 > sizeof(buf)) valsz = (sizeof(buf) - 1) / 2; - mysql_real_escape_string(&mysql, buf, lookup, valsz); + mysql_real_escape_string(&dbwrite.handle, buf, lookup, valsz); snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE %s = '%s'", table, keyfield, buf); while ((newparam = va_arg(ap, const char *))) { newval = va_arg(ap, const char *); if ((valsz = strlen (newval)) * 2 + 1 > sizeof(buf)) valsz = (sizeof(buf) - 1) / 2; - mysql_real_escape_string(&mysql, buf, newval, valsz); + mysql_real_escape_string(&dbwrite.handle, buf, newval, valsz); snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s = '%s'", newparam, buf); } va_end(ap); - //if ((valsz = strlen (lookup)) * 1 + 1 > sizeof(buf)) - // valsz = (sizeof(buf) - 1) / 2; - //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: Delete SQL: %s\n", sql); + ast_log(LOG_DEBUG, "MySQL RealTime: Delete SQL: %s\n", sql); /* Execution. */ - if (mysql_real_query(&mysql, sql, strlen(sql))) { + if (mysql_real_query(&dbwrite.handle, 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)); - ast_mutex_unlock(&mysql_lock); + ast_log(LOG_DEBUG, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&dbwrite.handle)); + ast_mutex_unlock(&dbwrite.lock); return -1; } - numrows = mysql_affected_rows(&mysql); - ast_mutex_unlock(&mysql_lock); + numrows = mysql_affected_rows(&dbwrite.handle); + ast_mutex_unlock(&dbwrite.lock); - ast_log(LOG_DEBUG,"MySQL RealTime: Deleted %llu rows on table: %s\n", numrows, table); + ast_log(LOG_DEBUG, "MySQL RealTime: Deleted %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 @@ -537,10 +530,7 @@ * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.) */ - if (numrows >= 0) - return (int)numrows; - - return -1; + return (int)numrows; } static struct ast_config *config_mysql(const char *database, const char *table, const char *file, struct ast_config *cfg, int withcomments) @@ -566,21 +556,21 @@ 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)) { - ast_mutex_unlock(&mysql_lock); + ast_mutex_lock(&dbread.lock); + if (!mysql_reconnect(&dbread)) { + ast_mutex_unlock(&dbread.lock); return NULL; } - if (mysql_real_query(&mysql, sql, strlen(sql))) { + if (mysql_real_query(&dbread.handle, 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)); - ast_mutex_unlock(&mysql_lock); + ast_log(LOG_DEBUG, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&dbread.handle)); + ast_mutex_unlock(&dbread.lock); return NULL; } - if ((result = mysql_store_result(&mysql))) { + if ((result = mysql_store_result(&dbread.handle))) { num_rows = mysql_num_rows(result); ast_log(LOG_DEBUG, "MySQL RealTime: Found %llu rows.\n", num_rows); @@ -592,6 +582,7 @@ if (!ast_config_internal_load(row[2], cfg, 0)) { mysql_free_result(result); ast_mutex_unlock(&mysql_lock); + ast_config_destroy(cfg); return NULL; } continue; @@ -616,7 +607,7 @@ } mysql_free_result(result); - ast_mutex_unlock(&mysql_lock); + ast_mutex_unlock(&dbread.lock); return cfg; } @@ -633,188 +624,216 @@ static int load_module(void) { + int writeokay = 0; parse_config(); - ast_mutex_lock(&mysql_lock); + ast_mutex_init(&dbread.lock); + ast_mutex_init(&dbwrite.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(&dbwrite)) + ast_log(LOG_WARNING, "MySQL RealTime: Couldn't establish write connection: %s\n", mysql_error(&dbwrite.handle)); + else + writeokay = 1; + + if (!mysql_reconnect(&dbread)) { + if (!writeokay) + ast_log(LOG_WARNING, "MySQL RealTime: Couldn't establish read connection, either: %s\n", mysql_error(&dbread.handle)); } ast_config_engine_register(&mysql_engine); - if (option_verbose) { - ast_verbose("MySQL RealTime driver loaded.\n"); - } + if (option_verbose > 1) + ast_verbose(VERBOSE_PREFIX_2 "MySQL RealTime driver loaded.\n"); ast_cli_register(&cli_realtime_mysql_status); - ast_mutex_unlock(&mysql_lock); - return 0; } static int unload_module(void) { - /* Aquire control before doing anything to the module itself. */ - ast_mutex_lock(&mysql_lock); - - mysql_close(&mysql); ast_cli_unregister(&cli_realtime_mysql_status); ast_config_engine_deregister(&mysql_engine); - if (option_verbose) { - ast_verbose("MySQL RealTime unloaded.\n"); - } + if (option_verbose > 1) + ast_verbose(VERBOSE_PREFIX_2 "MySQL RealTime unloaded.\n"); ast_module_user_hangup_all(); - /* Unlock so something else can destroy the lock. */ - ast_mutex_unlock(&mysql_lock); + usleep(1); + ast_mutex_destroy(&dbread.lock); + ast_mutex_destroy(&dbwrite.lock); + mysql_close(&dbwrite.handle); + mysql_close(&dbread.handle); + return 0; } static int reload(void) { - /* Aquire control before doing anything to the module itself. */ - ast_mutex_lock(&mysql_lock); + int writeokay = 0; - mysql_close(&mysql); - connected = 0; + ast_mutex_lock(&dbwrite.lock); + ast_mutex_lock(&dbread.lock); + mysql_close(&dbwrite.handle); + mysql_close(&dbread.handle); 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(&dbwrite)) + ast_log(LOG_WARNING, "MySQL RealTime: Cannot connect write handle: %s\n", mysql_error(&dbwrite.handle)); + else + writeokay = 1; + + if (!mysql_reconnect(&dbread)) { + if (!writeokay) + ast_log(LOG_WARNING, "MySQL RealTime: Cannot Connect read handle, either: %s\n", mysql_error(&dbread.handle)); } + if (option_verbose > 1) + ast_verbose(VERBOSE_PREFIX_2 "MySQL RealTime reloaded.\n"); - ast_verbose(VERBOSE_PREFIX_2 "MySQL RealTime reloaded.\n"); - /* Done reloading. Release lock so others can now use driver. */ - ast_mutex_unlock(&mysql_lock); + ast_mutex_unlock(&dbread.lock); + ast_mutex_unlock(&dbwrite.lock); return 0; } -static int parse_config (void) +static int parse_config(void) { struct ast_config *config; - const char *s; 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"); - strncpy(dbuser, "asterisk", sizeof(dbuser) - 1); - } else { - strncpy(dbuser, s, sizeof(dbuser) - 1); - } + const char *catg = "write"; + int haswriteconfig = 0; + if (!ast_category_exist(config, catg)) + catg = "general"; + if (!ast_category_exist(config, catg)) + ast_log(LOG_WARNING, "No configuration set for writing to the MySQL database.\n"); + else + haswriteconfig = load_mysql_config(config, catg, &dbwrite); - if (!(s=ast_variable_retrieve(config, "general", "dbpass"))) { - ast_log(LOG_WARNING, "MySQL RealTime: No database password found, using 'asterisk' as default.\n"); - strncpy(dbpass, "asterisk", sizeof(dbpass) - 1); - } else { - strncpy(dbpass, s, sizeof(dbpass) - 1); - } + if (!ast_category_exist(config, "read")) { + if (haswriteconfig) { + /* Copy from write config */ + ast_copy_string(dbread.host, dbwrite.host, sizeof(dbread.host)); + ast_copy_string(dbread.sock, dbwrite.sock, sizeof(dbread.sock)); + ast_copy_string(dbread.name, dbwrite.name, sizeof(dbread.name)); + ast_copy_string(dbread.user, dbwrite.user, sizeof(dbread.user)); + ast_copy_string(dbread.pass, dbwrite.pass, sizeof(dbread.pass)); + dbread.port = dbwrite.port; + } else + ast_log(LOG_WARNING, "It's kind of silly to be loading res_config_mysql.so if there's no configuration settings.\n"); + } else + load_mysql_config(config, "read", &dbread); + } + ast_config_destroy(config); - if (!(s=ast_variable_retrieve(config, "general", "dbhost"))) { - ast_log(LOG_WARNING, "MySQL RealTime: No database host found, using localhost via socket.\n"); - dbhost[0] = '\0'; - } else { - strncpy(dbhost, s, sizeof(dbhost) - 1); - } + return 0; +} - if (!(s=ast_variable_retrieve(config, "general", "dbname"))) { - ast_log(LOG_WARNING, "MySQL RealTime: No database name found, using 'asterisk' as default.\n"); - strncpy(dbname, "asterisk", sizeof(dbname) - 1); - } else { - strncpy(dbname, s, sizeof(dbname) - 1); - } +static int load_mysql_config(struct ast_config *config, const char *category, struct mysql_conn *conn) +{ + const char *s; - if (!(s=ast_variable_retrieve(config, "general", "dbport"))) { - ast_log(LOG_WARNING, "MySQL RealTime: No database port found, using 3306 as default.\n"); - dbport = 3306; - } else { - dbport = atoi(s); - } + if (!(s = ast_variable_retrieve(config, category, "dbuser"))) { + ast_log(LOG_WARNING, "MySQL RealTime: No database user found, using 'asterisk' as default.\n"); + s = "asterisk"; + } + ast_copy_string(conn->user, s, sizeof(conn->user)); - 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"); - strncpy(dbsock, "/tmp/mysql.sock", sizeof(dbsock) - 1); - } else { - strncpy(dbsock, s, sizeof(dbsock) - 1); - } + if (!(s = ast_variable_retrieve(config, category, "dbpass"))) { + ast_log(LOG_WARNING, "MySQL RealTime: No database password found, using 'asterisk' as default.\n"); + s = "asterisk"; } - ast_config_destroy(config); + ast_copy_string(conn->pass, s, sizeof(conn->pass)); - 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); + if (!(s = ast_variable_retrieve(config, category, "dbhost"))) { + ast_log(LOG_WARNING, "MySQL RealTime: No database host found, using localhost via socket.\n"); + s = ""; } - ast_log(LOG_DEBUG, "MySQL RealTime User: %s\n", dbuser); - ast_log(LOG_DEBUG, "MySQL RealTime Password: %s\n", dbpass); + ast_copy_string(conn->host, s, sizeof(conn->host)); + if (!(s = ast_variable_retrieve(config, category, "dbname"))) { + ast_log(LOG_WARNING, "MySQL RealTime: No database name found, using 'asterisk' as default.\n"); + s = "asterisk"; + } + ast_copy_string(conn->name, s, sizeof(conn->name) - 1); + + if (!(s = ast_variable_retrieve(config, category, "dbport"))) { + ast_log(LOG_WARNING, "MySQL RealTime: No database port found, using 3306 as default.\n"); + conn->port = 3306; + } else + conn->port = atoi(s); + + if (!conn->host && !(s = ast_variable_retrieve(config, "general", "dbsock"))) { + ast_log(LOG_WARNING, "MySQL RealTime: No database socket found, using '/tmp/mysql.sock' as default.\n"); + s = "/tmp/mysql.sock"; + } + ast_copy_string(conn->sock, s, sizeof(conn->sock)); + + + if (!ast_strlen_zero(conn->host)) { + ast_log(LOG_DEBUG, "MySQL RealTime host: %s\n", conn->host); + ast_log(LOG_DEBUG, "MySQL RealTime port: %i\n", conn->port); + } else + ast_log(LOG_DEBUG, "MySQL RealTime socket: %s\n", conn->sock); + ast_log(LOG_DEBUG, "MySQL RealTime database name: %s\n", conn->name); + ast_log(LOG_DEBUG, "MySQL RealTime user: %s\n", conn->user); + ast_log(LOG_DEBUG, "MySQL RealTime password: %s\n", conn->pass); + return 1; } -static int mysql_reconnect(const char *database) +static int mysql_reconnect(struct mysql_conn *conn) { 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)); - 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)) { + if ((!conn->connected) && (!ast_strlen_zero(conn->host) || conn->sock) && !ast_strlen_zero(conn->user) && !ast_strlen_zero(conn->name)) { + if (!mysql_init(&conn->handle)) { ast_log(LOG_WARNING, "MySQL RealTime: Insufficient memory to allocate MySQL resource.\n"); - connected = 0; + conn->connected = 0; return 0; } - if (mysql_real_connect(&mysql, dbhost, dbuser, dbpass, my_database, dbport, dbsock, 0)) { + if (mysql_real_connect(&conn->handle, conn->host, conn->user, conn->pass, conn->name, conn->port, conn->sock, 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); #endif ast_log(LOG_DEBUG, "MySQL RealTime: Successfully connected to database.\n"); - connected = 1; - connect_time = time(NULL); + conn->connected = 1; + conn->connect_time = time(NULL); return 1; } 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; + ast_log(LOG_ERROR, "MySQL RealTime: Failed to connect database server %s on %s (err %d). Check debug for more info.\n", conn->name, !ast_strlen_zero(conn->host) ? conn->host : conn->sock, mysql_errno(&conn->handle)); + ast_log(LOG_DEBUG, "MySQL RealTime: Cannot Connect (%d): %s\n", mysql_errno(&conn->handle), mysql_error(&conn->handle)); + conn->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)); + if (mysql_ping(&conn->handle) != 0 && mysql_ping(&conn->handle) != 0) { + conn->connected = 0; + ast_log(LOG_ERROR, "MySQL RealTime: Ping failed (%d). Trying an explicit reconnect.\n", mysql_errno(&conn->handle)); + ast_log(LOG_DEBUG, "MySQL RealTime: Server Error (%d): %s\n", mysql_errno(&conn->handle), mysql_error(&conn->handle)); goto reconnect_tryagain; } - connected = 1; + conn->connected = 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)); + if (mysql_select_db(&conn->handle, conn->name) != 0) { + ast_log(LOG_WARNING, "MySQL RealTime: Unable to select database: %s. Still Connected (%d) - %s.\n", conn->name, mysql_errno(&conn->handle), mysql_error(&conn->handle)); return 0; } - ast_log(LOG_DEBUG, "MySQL RealTime: Everything is fine.\n"); + ast_log(LOG_DEBUG, "MySQL RealTime: Connection okay.\n"); return 1; } } @@ -822,37 +841,33 @@ static int realtime_mysql_status(int fd, int argc, char **argv) { char status[256], status2[100] = ""; - int ctime = time(NULL) - connect_time; + int ctime = time(NULL) - dbread.connect_time; - 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(&dbread)) { + if (!ast_strlen_zero(dbread.host)) + snprintf(status, sizeof(status), "Connected to %s@%s, port %d", dbread.name, dbread.host, dbread.port); + else + snprintf(status, sizeof(status), "Connected to %s on socket file %s", dbread.name, dbread.sock); - if (dbuser && *dbuser) { - snprintf(status2, 99, " with username %s", dbuser); - } + if (!ast_strlen_zero(dbread.user)) + snprintf(status2, sizeof(status2), " with username %s", dbread.user); - 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) { - 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) { - 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) { - ast_cli(fd, "%s%s for %d minutes, %d seconds.\n", status, status2, ctime / 60, ctime % 60); - } else { + if (ctime > 31536000) + ast_cli(fd, "%s%s for %.1f years.\n", status, status2, (double)ctime / 31536000.0); + else if (ctime > 86400 * 30) + ast_cli(fd, "%s%s for %d days.\n", status, status2, ctime / 86400); + else if (ctime > 86400) + ast_cli(fd, "%s%s for %d days, %d hours.\n", status, status2, ctime / 86400, (ctime % 86400) / 3600); + else if (ctime > 3600) + ast_cli(fd, "%s%s for %d hours, %d minutes.\n", status, status2, ctime / 3600, (ctime % 3600) / 60); + else if (ctime > 60) + ast_cli(fd, "%s%s for %d minutes.\n", status, status2, ctime / 60); + else ast_cli(fd, "%s%s for %d seconds.\n", status, status2, ctime); - } return RESULT_SUCCESS; - } else { + } else return RESULT_FAILURE; - } } AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "MySQL RealTime Configuration Driver",