Index: res_config_mysql.c =================================================================== RCS file: /usr/cvsroot/asterisk-addons/res_config_mysql.c,v retrieving revision 1.5 diff -u -r1.5 res_config_mysql.c --- res_config_mysql.c 22 Jan 2005 21:09:56 -0000 1.5 +++ res_config_mysql.c 26 Jan 2005 23:57:42 -0000 @@ -1,29 +1,33 @@ /* * Asterisk -- A telephony toolkit for Linux. * - * Copyright (C) 1999-2004, Digium, Inc. + * Copyright (C) 1999-2005, Digium, Inc. * * Mark Spencer - Asterisk Author * Matthew Boehm - MySQL RealTime Driver Author * * res_config_mysql.c * - * v1.4 - (12-02-04) - Added realtime_multi_mysql function - * This function will return an ast_config with categories, - * unlike standard realtime_mysql which only returns - * a linked list of ast_variables + * v1.5.1 - (01-26-05) - Added better(?) locking stuff * - * v1.3 - (12-01-04) - Added support other operators - * Ex: =, !=, LIKE, NOT LIKE, RLIKE, etc... + * v1.5 - (01-26-05) - Brought up to date with new config.h changes (bug #3406) + * - Added in extra locking provided by georg (bug #3248) * - * v1.2 - (11-DD-04) - Added reload. Updated load and unload. - * Code beautification (doc/CODING-GUIDELINES) + * v1.4 - (12-02-04) - Added realtime_multi_mysql function + * This function will return an ast_config with categories, + * unlike standard realtime_mysql which only returns + * a linked list of ast_variables + * + * v1.3 - (12-01-04) - Added support other operators + * Ex: =, !=, LIKE, NOT LIKE, RLIKE, etc... + * + * v1.2 - (11-DD-04) - Added reload. Updated load and unload. + * Code beautification (doc/CODING-GUIDELINES) */ #include #include #include -#include #include #include #include @@ -36,7 +40,6 @@ #include static char *res_config_mysql_desc = "MySQL RealTime Configuration Driver"; -static struct ast_config_reg reg_mysql; AST_MUTEX_DEFINE_STATIC(mysql_lock); #define RES_CONFIG_MYSQL_CONF "res_mysql.conf" @@ -122,36 +125,30 @@ return NULL; } - /* Toss up here. Each has its pros and cons. Gonna use store result cause most people will be running database - either locally or on same network. And since the tables are small text based, shouldn't be that big of a problem - unless you're retrieving a few million rows. - mysql_store_result: Will copy all the rows returned by the query to local memory. This save network time, - but could possibly use up lots of memory if the result set is large. - mysql_use_result: Will keep the result set on the server and will retrieve each row at a time from server. - Reduces local memory usage, but increases network traffic for large sets. Also, the server - could go down part way thru our retrieval process and we wouldn't get all the rows. */ - - result = mysql_store_result(&mysql); - 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, ";"); - if(chunk && !ast_strlen_zero(ast_strip(chunk))) { - if(prev) { - prev->next = ast_new_variable(fields[i].name, chunk); - if (prev->next) { - prev = prev->next; + if((result = mysql_store_result(&mysql))) { + 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, ";"); + 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 { + prev = var = ast_variable_new(fields[i].name, chunk); } - } else { - prev = var = ast_new_variable(fields[i].name, chunk); } } } } + } else { + ast_log(LOG_WARNING, "MySQL RealTime: Could not find any rows in table %s.\n", table); } ast_mutex_unlock(&mysql_lock); @@ -168,13 +165,12 @@ int numFields, i; char sql[256]; const char *initfield = NULL; - char *title = NULL; char *stringp; char *chunk; char *op; const char *newparam, *newval; struct ast_realloca ra; - struct ast_variable *var=NULL, *prev=NULL; + struct ast_variable *var=NULL; struct ast_config *cfg = NULL; struct ast_category *cat = NULL; @@ -185,6 +181,13 @@ memset(&ra, 0, sizeof(ra)); + 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"); + 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 *); @@ -233,50 +236,29 @@ return NULL; } - result = mysql_store_result(&mysql); - numFields = mysql_num_fields(result); - fields = mysql_fetch_fields(result); - - while ((row = mysql_fetch_row(result))) { - var = NULL; - prev = NULL; - title = NULL; - 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) && !title) { - title = ast_restrdupa(&ra, chunk); - } - if (prev) { - prev->next = ast_new_variable(fields[i].name, chunk); - if (prev->next) { - prev = prev->next; + if((result = mysql_store_result(&mysql))) { + numFields = mysql_num_fields(result); + fields = mysql_fetch_fields(result); + + while((row = mysql_fetch_row(result))) { + var = NULL; + 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)) { + ast_category_rename(cat, chunk); } - } else { - prev = var = ast_new_variable(fields[i].name, chunk); + var = ast_variable_new(fields[i].name, chunk); + ast_variable_append(cat, var); } } } + ast_category_append(cfg, cat); } - if(var) { - cat = ast_new_category(title ? title : ""); - if(cat) { - cat->root = var; - if(!cfg) { - cfg = ast_new_config(); - } - if(cfg) { - ast_category_append(cfg, cat); - } else { - ast_category_destroy(cat); - } - } else { - ast_log(LOG_WARNING, "Out of memory!\n"); - ast_destroy_realtime(var); - } - } + } else { + ast_log(LOG_WARNING, "MySQL RealTime: Could not find any rows in table %s.\n", table); } ast_mutex_unlock(&mysql_lock); @@ -344,7 +326,7 @@ return -1; } -static struct ast_config *config_mysql(const char *database, const char *table, const char *file, struct ast_config *new_config_s, struct ast_category **new_cat_p, struct ast_variable **new_v_p, int recur) +static struct ast_config *config_mysql(const char *database, const char *table, const char *file, struct ast_config *cfg) { MYSQL_RES *result; MYSQL_ROW row; @@ -367,18 +349,6 @@ 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); - if(new_config_s) { - new = new_config_s; - cat_started++; - } else { - new = ast_new_config(); - } - - if(!new) { - ast_log(LOG_WARNING, "MySQL RealTime: Cannot create new config. Out of memory!\n"); - return NULL; - } - 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. */ @@ -399,65 +369,50 @@ num_rows = mysql_num_rows(result); ast_log(LOG_DEBUG, "MySQL RealTime: Found %llu rows.\n", num_rows); - cat_started = 0; - - cur_cat = *new_cat_p; - cur_v = *new_v_p; - - if(cur_cat) { - cat_started = 1; - } - - if(cur_v) { - var_started = 1; - } - /* There might exist a better way to access the column names other than counting, but I believe that would require another loop that we don't need. */ while((row = mysql_fetch_row(result))) { - if(!strcmp(row[1], "#include") && recur < MAX_INCLUDE_LEVEL) { - config_mysql(database, table, row[2], new, &cur_cat, &cur_v, recur + 1); - } else { - if(strcmp(last, row[0]) || last_cat_metric != atoi(row[3])) { - strncpy(last, row[0], sizeof(last) - 1); - last_cat_metric = atoi(row[3]); - new_cat = (struct ast_category *) ast_new_category(row[0]); - - if(!cat_started) { - cat_started++; - new->root = new_cat; - cur_cat = new->root; - } else { - cur_cat->next = new_cat; - cur_cat = cur_cat->next; - } - var_started = 0; + if(!strcmp(row[1], "#include")) { + if (!ast_config_internal_load(row[2], cfg)) { + mysql_free_result(result); + ast_mutex_unlock(&mysql_lock); + return NULL; } + continue; + } - new_v = ast_new_variable(row[1], row[2]); - - if (!var_started) { - var_started++; - cur_cat->root = new_v; - cur_v = cur_cat->root; - } else { - cur_v->next = new_v; - cur_v = cur_v->next; + 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"); + break; } + strcpy(last, row[0]); + last_cat_metric = atoi(row[3]); + ast_category_append(cfg, cur_cat); } + new_v = ast_variable_new(row[1], row[2]); + ast_variable_append(cur_cat, new_v); } - mysql_free_result(result); - ast_mutex_unlock(&mysql_lock); } 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); } - return new; + mysql_free_result(result); + ast_mutex_unlock(&mysql_lock); + + return cfg; } +static struct ast_config_engine mysql_engine = { + .name = "mysql", + .load_func = config_mysql, + .realtime_func = realtime_mysql, + .realtime_multi_func = realtime_multi_mysql, + .update_func = update_mysql +}; + int load_module (void) { parse_config(); @@ -467,56 +422,54 @@ ast_log(LOG_DEBUG, "MySQL RealTime: Cannot Connect: %s\n", mysql_error(&mysql)); } - memset(®_mysql, 0, sizeof(struct ast_config_reg)); - strncpy(reg_mysql.name, "mysql", sizeof(reg_mysql.name) - 1); - - reg_mysql.static_func = config_mysql; - reg_mysql.realtime_func = realtime_mysql; - reg_mysql.update_func = update_mysql; - reg_mysql.realtime_multi_func = realtime_multi_mysql; - - ast_cust_config_register(®_mysql); - ast_verbose(VERBOSE_PREFIX_2 "MySQL RealTime driver loaded.\n"); + ast_config_engine_register(&mysql_engine); + if(option_verbose) { + ast_verbose("MySQL RealTime driver loaded.\n"); + } ast_cli_register(&cli_realtime_mysql_status); + return 0; } 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_cust_config_deregister(®_mysql); - ast_verbose(VERBOSE_PREFIX_2 "MySQL RealTime unloaded.\n"); - - if(ast_mutex_lock(&mysql_lock)) { - ast_mutex_unlock(&mysql_lock); + ast_config_engine_deregister(&mysql_engine); + if(option_verbose) { + ast_verbose("MySQL RealTime unloaded.\n"); } STANDARD_HANGUP_LOCALUSERS; + /* Unlock so something else can destroy the lock. */ + ast_mutex_unlock(&mysql_lock); + return 0; } int reload (void) { - if(ast_mutex_lock(&mysql_lock)) { - ast_log(LOG_WARNING, "MySQL RealTime: Couldn't reload. Use count %i.\n", usecount()); - return 1; - } + /* Aquire control before doing anything to the module itself. */ + ast_mutex_lock(&mysql_lock); - ast_mutex_unlock(&mysql_lock); mysql_close(&mysql); - connected = 0; - parse_config(); + /* Need to unlock so that mysql_reconnect can regain the lock. */ + ast_mutex_unlock(&mysql_lock); + if(!mysql_reconnect()) { 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)); } ast_verbose(VERBOSE_PREFIX_2 "MySQL RealTime reloaded.\n"); + return 0; } @@ -525,7 +478,7 @@ struct ast_config *config; char *s; - config = ast_load(RES_CONFIG_MYSQL_CONF); + config = ast_config_load(RES_CONFIG_MYSQL_CONF); if(config) { if(!(s=ast_variable_retrieve(config, "general", "dbuser"))) { @@ -570,7 +523,7 @@ strncpy(dbsock, s, sizeof(dbsock) - 1); } } - ast_destroy(config); + ast_config_destroy(config); if(dbhost) { ast_log(LOG_DEBUG, "MySQL RealTime Host: %s\n", dbhost); @@ -591,10 +544,9 @@ int usecount (void) { - /* Try and get a lock. If unsuccessful, than that means a query is currently being run. - Lets be nice and don't interrupt the query. */ - if(ast_mutex_lock(&mysql_lock)) { - ast_log(LOG_DEBUG, "MySQL RealTime: Usecount 1. Query in progress.\n"); + /* Try and get a lock. If unsuccessful, than that means another thread is using the mysql object. */ + if(ast_mutex_trylock(&mysql_lock)) { + ast_log(LOG_DEBUG, "MySQL RealTime: Module usage count is 1.\n"); return 1; } ast_mutex_unlock(&mysql_lock);