Mon May 14 04:43:00 2007

Asterisk developer's documentation


res_config_mysql.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- A telephony toolkit for Linux.
00003  *
00004  * Copyright (C) 1999-2005, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>  - Asterisk Author
00007  * Matthew Boehm <mboehm@cytelcom.com> - MySQL RealTime Driver Author
00008  *
00009  * res_config_mysql.c <mysql plugin for RealTime configuration engine>
00010  *
00011  * v2.0   - (10-07-05) - mutex_lock fixes (bug #4973, comment #0034602)
00012  *
00013  * v1.9   - (08-19-05) - Added support to correctly honor the family database specified
00014  *                       in extconfig.conf (bug #4973)
00015  *
00016  * v1.8   - (04-21-05) - Modified return values of update_mysql to better indicate
00017  *                       what really happened.
00018  *
00019  * v1.7   - (01-28-05) - Fixed non-initialization of ast_category struct
00020  *                       in realtime_multi_mysql function which caused segfault. 
00021  *
00022  * v1.6   - (00-00-00) - Skipped to bring comments into sync with version number in CVS.
00023  *
00024  * v1.5.1 - (01-26-05) - Added better(?) locking stuff
00025  *
00026  * v1.5   - (01-26-05) - Brought up to date with new config.h changes (bug #3406)
00027  *                     - Added in extra locking provided by georg (bug #3248)
00028  *
00029  * v1.4   - (12-02-04) - Added realtime_multi_mysql function
00030  *                        This function will return an ast_config with categories,
00031  *                        unlike standard realtime_mysql which only returns
00032  *                        a linked list of ast_variables
00033  *
00034  * v1.3   - (12-01-04) - Added support other operators
00035  *                       Ex: =, !=, LIKE, NOT LIKE, RLIKE, etc...
00036  *
00037  * v1.2   - (11-DD-04) - Added reload. Updated load and unload.
00038  *                       Code beautification (doc/CODING-GUIDELINES)
00039  */
00040 /*** MODULEINFO
00041    <depend>mysqlclient</depend>
00042  ***/
00043 
00044 #include <asterisk.h>
00045 
00046 #include <asterisk/channel.h>
00047 #include <asterisk/logger.h>
00048 #include <asterisk/config.h>
00049 #include <asterisk/module.h>
00050 #include <asterisk/lock.h>
00051 #include <asterisk/options.h>
00052 #include <asterisk/cli.h>
00053 #include <asterisk/utils.h>
00054 #include <stdlib.h>
00055 #include <stdio.h>
00056 #include <string.h>
00057 #include <stdio.h>
00058 #include <mysql/mysql.h>
00059 #include <mysql/mysql_version.h>
00060 #include <mysql/errmsg.h>
00061 
00062 #define AST_MODULE "res_config_mysql"
00063 
00064 AST_MUTEX_DEFINE_STATIC(mysql_lock);
00065 #define RES_CONFIG_MYSQL_CONF "res_mysql.conf"
00066 MYSQL         mysql;
00067 static char   dbhost[50];
00068 static char   dbuser[50];
00069 static char   dbpass[50];
00070 static char   dbname[50];
00071 static char   dbsock[50];
00072 static int    dbport;
00073 static int    connected;
00074 static time_t connect_time;
00075 
00076 static int parse_config(void);
00077 static int mysql_reconnect(const char *database);
00078 static int realtime_mysql_status(int fd, int argc, char **argv);
00079 
00080 static char cli_realtime_mysql_status_usage[] =
00081 "Usage: realtime mysql status\n"
00082 "       Shows connection information for the MySQL RealTime driver\n";
00083 
00084 static struct ast_cli_entry cli_realtime_mysql_status = {
00085         { "realtime", "mysql", "status", NULL }, realtime_mysql_status,
00086         "Shows connection information for the MySQL RealTime driver", cli_realtime_mysql_status_usage, NULL };
00087 
00088 static struct ast_variable *realtime_mysql(const char *database, const char *table, va_list ap)
00089 {
00090    MYSQL_RES *result;
00091    MYSQL_ROW row;
00092    MYSQL_FIELD *fields;
00093    int numFields, i, valsz;
00094    char sql[512];
00095    char buf[511]; /* Keep this size uneven as it is 2n+1. */
00096    char *stringp;
00097    char *chunk;
00098    char *op;
00099    const char *newparam, *newval;
00100    struct ast_variable *var=NULL, *prev=NULL;
00101 
00102    if(!table) {
00103       ast_log(LOG_WARNING, "MySQL RealTime: No table specified.\n");
00104       return NULL;
00105    }
00106 
00107    /* Get the first parameter and first value in our list of passed paramater/value pairs */
00108    newparam = va_arg(ap, const char *);
00109    newval = va_arg(ap, const char *);
00110    if(!newparam || !newval)  {
00111       ast_log(LOG_WARNING, "MySQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
00112       return NULL;
00113    }
00114 
00115    /* Must connect to the server before anything else, as the escape function requires the mysql handle. */
00116    ast_mutex_lock(&mysql_lock);
00117    if (!mysql_reconnect(database)) {
00118       ast_mutex_unlock(&mysql_lock);
00119       return NULL;
00120    }
00121 
00122    /* Create the first part of the query using the first parameter/value pairs we just extracted
00123       If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
00124 
00125    if(!strchr(newparam, ' ')) op = " ="; else op = "";
00126 
00127    if ((valsz = strlen (newval)) * 2 + 1 > sizeof(buf))
00128       valsz = (sizeof(buf) - 1) / 2;
00129    mysql_real_escape_string(&mysql, buf, newval, valsz);
00130    snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op, buf);
00131    while((newparam = va_arg(ap, const char *))) {
00132       newval = va_arg(ap, const char *);
00133       if(!strchr(newparam, ' ')) op = " ="; else op = "";
00134       if ((valsz = strlen (newval)) * 2 + 1 > sizeof(buf))
00135          valsz = (sizeof(buf) - 1) / 2;
00136       mysql_real_escape_string(&mysql, buf, newval, valsz);
00137       snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s '%s'", newparam, op, buf);
00138    }
00139    va_end(ap);
00140 
00141    ast_log(LOG_DEBUG, "MySQL RealTime: Retrieve SQL: %s\n", sql);
00142 
00143    /* Execution. */
00144    if(mysql_real_query(&mysql, sql, strlen(sql))) {
00145       ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database. Check debug for more info.\n");
00146       ast_log(LOG_DEBUG, "MySQL RealTime: Query: %s\n", sql);
00147       ast_log(LOG_DEBUG, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&mysql));
00148       ast_mutex_unlock(&mysql_lock);
00149       return NULL;
00150    }
00151 
00152    if((result = mysql_store_result(&mysql))) {
00153       numFields = mysql_num_fields(result);
00154       fields = mysql_fetch_fields(result);
00155 
00156       while((row = mysql_fetch_row(result))) {
00157          for(i = 0; i < numFields; i++) {
00158             stringp = row[i];
00159             while(stringp) {
00160                chunk = strsep(&stringp, ";");
00161                if(chunk && !ast_strlen_zero(ast_strip(chunk))) {
00162                   if(prev) {
00163                      prev->next = ast_variable_new(fields[i].name, chunk);
00164                      if (prev->next) {
00165                         prev = prev->next;
00166                      }
00167                   } else {
00168                      prev = var = ast_variable_new(fields[i].name, chunk);
00169                   }
00170                }
00171             }
00172          }
00173       }
00174    } else {                                
00175       ast_log(LOG_WARNING, "MySQL RealTime: Could not find any rows in table %s.\n", table);
00176    }
00177 
00178    ast_mutex_unlock(&mysql_lock);
00179    mysql_free_result(result);
00180 
00181    return var;
00182 }
00183 
00184 static struct ast_config *realtime_multi_mysql(const char *database, const char *table, va_list ap)
00185 {
00186    MYSQL_RES *result;
00187    MYSQL_ROW row;
00188    MYSQL_FIELD *fields;
00189    int numFields, i, valsz;
00190    char sql[512];
00191    char buf[511]; /* Keep this size uneven as it is 2n+1. */
00192    const char *initfield = NULL;
00193    char *stringp;
00194    char *chunk;
00195    char *op;
00196    const char *newparam, *newval;
00197    struct ast_realloca ra;
00198    struct ast_variable *var=NULL;
00199    struct ast_config *cfg = NULL;
00200    struct ast_category *cat = NULL;
00201 
00202    if(!table) {
00203       ast_log(LOG_WARNING, "MySQL RealTime: No table specified.\n");
00204       return NULL;
00205    }
00206    
00207    memset(&ra, 0, sizeof(ra));
00208 
00209    cfg = ast_config_new();
00210    if (!cfg) {
00211       /* If I can't alloc memory at this point, why bother doing anything else? */
00212       ast_log(LOG_WARNING, "Out of memory!\n");
00213       return NULL;
00214    }
00215 
00216    /* Get the first parameter and first value in our list of passed paramater/value pairs */
00217    newparam = va_arg(ap, const char *);
00218    newval = va_arg(ap, const char *);
00219    if(!newparam || !newval)  {
00220       ast_log(LOG_WARNING, "MySQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
00221       ast_config_destroy(cfg);
00222       return NULL;
00223    }
00224 
00225    initfield = ast_strdupa(newparam);
00226    if(initfield && (op = strchr(initfield, ' '))) {
00227       *op = '\0';
00228    }
00229 
00230    /* Must connect to the server before anything else, as the escape function requires the mysql handle. */
00231    ast_mutex_lock(&mysql_lock);
00232    if (!mysql_reconnect(database)) {
00233       ast_mutex_unlock(&mysql_lock);
00234       return NULL;
00235    }
00236 
00237    /* Create the first part of the query using the first parameter/value pairs we just extracted
00238       If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
00239 
00240    if(!strchr(newparam, ' ')) op = " ="; else op = "";
00241 
00242    if ((valsz = strlen (newval)) * 2 + 1 > sizeof(buf))
00243       valsz = (sizeof(buf) - 1) / 2;
00244    mysql_real_escape_string(&mysql, buf, newval, valsz);
00245    snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op, buf);
00246    while((newparam = va_arg(ap, const char *))) {
00247       newval = va_arg(ap, const char *);
00248       if(!strchr(newparam, ' ')) op = " ="; else op = "";
00249       if ((valsz = strlen (newval)) * 2 + 1 > sizeof(buf))
00250          valsz = (sizeof(buf) - 1) / 2;
00251       mysql_real_escape_string(&mysql, buf, newval, valsz);
00252       snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s '%s'", newparam, op, buf);
00253    }
00254 
00255    if(initfield) {
00256       snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " ORDER BY %s", initfield);
00257    }
00258 
00259    va_end(ap);
00260 
00261    ast_log(LOG_DEBUG, "MySQL RealTime: Retrieve SQL: %s\n", sql);
00262 
00263    /* Execution. */
00264    if(mysql_real_query(&mysql, sql, strlen(sql))) {
00265       ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database. Check debug for more info.\n");
00266       ast_log(LOG_DEBUG, "MySQL RealTime: Query: %s\n", sql);
00267       ast_log(LOG_DEBUG, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&mysql));
00268       ast_mutex_unlock(&mysql_lock);
00269       ast_config_destroy(cfg);
00270       return NULL;
00271    }
00272 
00273    if((result = mysql_store_result(&mysql))) {
00274       numFields = mysql_num_fields(result);
00275       fields = mysql_fetch_fields(result);
00276 
00277       while((row = mysql_fetch_row(result))) {
00278          var = NULL;
00279          cat = ast_category_new("");
00280          if(!cat) {
00281             ast_log(LOG_WARNING, "Out of memory!\n");
00282             continue;
00283          }
00284          for(i = 0; i < numFields; i++) {
00285             stringp = row[i];
00286             while(stringp) {
00287                chunk = strsep(&stringp, ";");
00288                if(chunk && !ast_strlen_zero(ast_strip(chunk))) {
00289                   if(initfield && !strcmp(initfield, fields[i].name)) {
00290                      ast_category_rename(cat, chunk);
00291                   }
00292                   var = ast_variable_new(fields[i].name, chunk);
00293                   ast_variable_append(cat, var);
00294                }
00295             }
00296          }
00297          ast_category_append(cfg, cat);
00298       }
00299    } else {
00300       ast_log(LOG_WARNING, "MySQL RealTime: Could not find any rows in table %s.\n", table);
00301    }
00302 
00303    ast_mutex_unlock(&mysql_lock);
00304    mysql_free_result(result);
00305 
00306    return cfg;
00307 }
00308 
00309 static int update_mysql(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
00310 {
00311    my_ulonglong numrows;
00312    char sql[512];
00313    char buf[511]; /* Keep this size uneven as it is 2n+1. */
00314    int valsz;
00315    const char *newparam, *newval;
00316 
00317    if(!table) {
00318       ast_log(LOG_WARNING, "MySQL RealTime: No table specified.\n");
00319                return -1;
00320    }
00321 
00322    /* Get the first parameter and first value in our list of passed paramater/value pairs */
00323    newparam = va_arg(ap, const char *);
00324    newval = va_arg(ap, const char *);
00325    if(!newparam || !newval)  {
00326       ast_log(LOG_WARNING, "MySQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
00327                return -1;
00328    }
00329 
00330    /* Must connect to the server before anything else, as the escape function requires the mysql handle. */
00331    ast_mutex_lock(&mysql_lock);
00332    if (!mysql_reconnect(database)) {
00333       ast_mutex_unlock(&mysql_lock);
00334       return -1;
00335    }
00336 
00337    /* Create the first part of the query using the first parameter/value pairs we just extracted
00338       If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
00339 
00340    if ((valsz = strlen (newval)) * 1 + 1 > sizeof(buf))
00341       valsz = (sizeof(buf) - 1) / 2;
00342    mysql_real_escape_string(&mysql, buf, newval, valsz);
00343    snprintf(sql, sizeof(sql), "UPDATE %s SET %s = '%s'", table, newparam, buf);
00344    while((newparam = va_arg(ap, const char *))) {
00345       newval = va_arg(ap, const char *);
00346       if ((valsz = strlen (newval)) * 2 + 1 > sizeof(buf))
00347          valsz = (sizeof(buf) - 1) / 2;
00348       mysql_real_escape_string(&mysql, buf, newval, valsz);
00349       snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", %s = '%s'", newparam, buf);
00350    }
00351    va_end(ap);
00352    if ((valsz = strlen (lookup)) * 1 + 1 > sizeof(buf))
00353       valsz = (sizeof(buf) - 1) / 2;
00354    mysql_real_escape_string(&mysql, buf, lookup, valsz);
00355    snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " WHERE %s = '%s'", keyfield, buf);
00356 
00357    ast_log(LOG_DEBUG,"MySQL RealTime: Update SQL: %s\n", sql);
00358 
00359    /* Execution. */
00360    if(mysql_real_query(&mysql, sql, strlen(sql))) {
00361       ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database. Check debug for more info.\n");
00362       ast_log(LOG_DEBUG, "MySQL RealTime: Query: %s\n", sql);
00363       ast_log(LOG_DEBUG, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&mysql));
00364       ast_mutex_unlock(&mysql_lock);
00365       return -1;
00366    }
00367 
00368    numrows = mysql_affected_rows(&mysql);
00369    ast_mutex_unlock(&mysql_lock);
00370 
00371    ast_log(LOG_DEBUG,"MySQL RealTime: Updated %llu rows on table: %s\n", numrows, table);
00372 
00373    /* From http://dev.mysql.com/doc/mysql/en/mysql-affected-rows.html
00374     * An integer greater than zero indicates the number of rows affected
00375     * Zero indicates that no records were updated
00376     * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
00377    */
00378 
00379    if(numrows >= 0)
00380       return (int)numrows;
00381 
00382    return -1;
00383 }
00384 
00385 static struct ast_config *config_mysql(const char *database, const char *table, const char *file, struct ast_config *cfg, int withcomments)
00386 {
00387    MYSQL_RES *result;
00388    MYSQL_ROW row;
00389    my_ulonglong num_rows;
00390    struct ast_variable *new_v;
00391    struct ast_category *cur_cat;
00392    char sql[250] = "";
00393    char last[80] = "";
00394    int last_cat_metric = 0;
00395 
00396    last[0] = '\0';
00397 
00398    if(!file || !strcmp(file, RES_CONFIG_MYSQL_CONF)) {
00399       ast_log(LOG_WARNING, "MySQL RealTime: Cannot configure myself.\n");
00400       return NULL;
00401    }
00402 
00403    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);
00404 
00405    ast_log(LOG_DEBUG, "MySQL RealTime: Static SQL: %s\n", sql);
00406 
00407    /* We now have our complete statement; Lets connect to the server and execute it. */
00408    ast_mutex_lock(&mysql_lock);
00409    if(!mysql_reconnect(database)) {
00410       ast_mutex_unlock(&mysql_lock);
00411       return NULL;
00412    }
00413 
00414    if(mysql_real_query(&mysql, sql, strlen(sql))) {
00415       ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database. Check debug for more info.\n");
00416       ast_log(LOG_DEBUG, "MySQL RealTime: Query: %s\n", sql);
00417       ast_log(LOG_DEBUG, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&mysql));
00418       ast_mutex_unlock(&mysql_lock);
00419       return NULL;
00420    }
00421 
00422    if((result = mysql_store_result(&mysql))) {
00423       num_rows = mysql_num_rows(result);
00424       ast_log(LOG_DEBUG, "MySQL RealTime: Found %llu rows.\n", num_rows);
00425 
00426       /* There might exist a better way to access the column names other than counting,
00427                    but I believe that would require another loop that we don't need. */
00428 
00429       while((row = mysql_fetch_row(result))) {
00430          if(!strcmp(row[1], "#include")) {
00431             if (!ast_config_internal_load(row[2], cfg, 0)) {
00432                mysql_free_result(result);
00433                ast_mutex_unlock(&mysql_lock);
00434                return NULL;
00435             }
00436             continue;
00437          }
00438 
00439          if(strcmp(last, row[0]) || last_cat_metric != atoi(row[3])) {
00440             cur_cat = ast_category_new(row[0]);
00441             if (!cur_cat) {
00442                ast_log(LOG_WARNING, "Out of memory!\n");
00443                break;
00444             }
00445             strcpy(last, row[0]);
00446             last_cat_metric = atoi(row[3]);
00447             ast_category_append(cfg, cur_cat);
00448          }
00449          new_v = ast_variable_new(row[1], row[2]);
00450          ast_variable_append(cur_cat, new_v);
00451       }
00452    } else {
00453       ast_log(LOG_WARNING, "MySQL RealTime: Could not find config '%s' in database.\n", file);
00454    }
00455 
00456    mysql_free_result(result);
00457    ast_mutex_unlock(&mysql_lock);
00458 
00459    return cfg;
00460 }
00461 
00462 static struct ast_config_engine mysql_engine = {
00463    .name = "mysql",
00464    .load_func = config_mysql,
00465    .realtime_func = realtime_mysql,
00466    .realtime_multi_func = realtime_multi_mysql,
00467    .update_func = update_mysql
00468 };
00469 
00470 static int load_module(void)
00471 {
00472    parse_config();
00473 
00474    ast_mutex_lock(&mysql_lock);
00475 
00476    if(!mysql_reconnect(NULL)) {
00477       ast_log(LOG_WARNING, "MySQL RealTime: Couldn't establish connection. Check debug.\n");
00478       ast_log(LOG_DEBUG, "MySQL RealTime: Cannot Connect: %s\n", mysql_error(&mysql));
00479    }
00480 
00481    ast_config_engine_register(&mysql_engine);
00482    if(option_verbose) {
00483       ast_verbose("MySQL RealTime driver loaded.\n");
00484    }
00485    ast_cli_register(&cli_realtime_mysql_status);
00486 
00487    ast_mutex_unlock(&mysql_lock);
00488 
00489    return 0;
00490 }
00491 
00492 static int unload_module(void)
00493 {
00494    /* Aquire control before doing anything to the module itself. */
00495    ast_mutex_lock(&mysql_lock);
00496 
00497    mysql_close(&mysql);
00498    ast_cli_unregister(&cli_realtime_mysql_status);
00499    ast_config_engine_deregister(&mysql_engine);
00500    if(option_verbose) {
00501       ast_verbose("MySQL RealTime unloaded.\n");
00502    }
00503 
00504    ast_module_user_hangup_all();
00505 
00506    /* Unlock so something else can destroy the lock. */
00507    ast_mutex_unlock(&mysql_lock);
00508 
00509    return 0;
00510 }
00511 
00512 static int reload(void)
00513 {
00514    /* Aquire control before doing anything to the module itself. */
00515    ast_mutex_lock(&mysql_lock);
00516 
00517    mysql_close(&mysql);
00518    connected = 0;
00519    parse_config();
00520 
00521    if(!mysql_reconnect(NULL)) {
00522       ast_log(LOG_WARNING, "MySQL RealTime: Couldn't establish connection. Check debug.\n");
00523       ast_log(LOG_DEBUG, "MySQL RealTime: Cannot Connect: %s\n", mysql_error(&mysql));
00524    }
00525 
00526    ast_verbose(VERBOSE_PREFIX_2 "MySQL RealTime reloaded.\n");
00527 
00528    /* Done reloading. Release lock so others can now use driver. */
00529    ast_mutex_unlock(&mysql_lock);
00530 
00531    return 0;
00532 }
00533 
00534 static int parse_config (void)
00535 {
00536    struct ast_config *config;
00537    const char *s;
00538 
00539    config = ast_config_load(RES_CONFIG_MYSQL_CONF);
00540 
00541    if(config) {
00542       if(!(s=ast_variable_retrieve(config, "general", "dbuser"))) {
00543          ast_log(LOG_WARNING, "MySQL RealTime: No database user found, using 'asterisk' as default.\n");
00544          strncpy(dbuser, "asterisk", sizeof(dbuser) - 1);
00545       } else {
00546          strncpy(dbuser, s, sizeof(dbuser) - 1);
00547       }
00548 
00549       if(!(s=ast_variable_retrieve(config, "general", "dbpass"))) {
00550                         ast_log(LOG_WARNING, "MySQL RealTime: No database password found, using 'asterisk' as default.\n");
00551                         strncpy(dbpass, "asterisk", sizeof(dbpass) - 1);
00552                 } else {
00553                         strncpy(dbpass, s, sizeof(dbpass) - 1);
00554                 }
00555 
00556       if(!(s=ast_variable_retrieve(config, "general", "dbhost"))) {
00557                         ast_log(LOG_WARNING, "MySQL RealTime: No database host found, using localhost via socket.\n");
00558          dbhost[0] = '\0';
00559                 } else {
00560                         strncpy(dbhost, s, sizeof(dbhost) - 1);
00561                 }
00562 
00563       if(!(s=ast_variable_retrieve(config, "general", "dbname"))) {
00564                         ast_log(LOG_WARNING, "MySQL RealTime: No database name found, using 'asterisk' as default.\n");
00565          strncpy(dbname, "asterisk", sizeof(dbname) - 1);
00566                 } else {
00567                         strncpy(dbname, s, sizeof(dbname) - 1);
00568                 }
00569 
00570       if(!(s=ast_variable_retrieve(config, "general", "dbport"))) {
00571                         ast_log(LOG_WARNING, "MySQL RealTime: No database port found, using 3306 as default.\n");
00572          dbport = 3306;
00573                 } else {
00574          dbport = atoi(s);
00575                 }
00576 
00577       if(dbhost && !(s=ast_variable_retrieve(config, "general", "dbsock"))) {
00578                         ast_log(LOG_WARNING, "MySQL RealTime: No database socket found, using '/tmp/mysql.sock' as default.\n");
00579                         strncpy(dbsock, "/tmp/mysql.sock", sizeof(dbsock) - 1);
00580                 } else {
00581                         strncpy(dbsock, s, sizeof(dbsock) - 1);
00582                 }
00583    }
00584    ast_config_destroy(config);
00585 
00586    if(dbhost) {
00587       ast_log(LOG_DEBUG, "MySQL RealTime Host: %s\n", dbhost);
00588       ast_log(LOG_DEBUG, "MySQL RealTime Port: %i\n", dbport);
00589    } else {
00590       ast_log(LOG_DEBUG, "MySQL RealTime Socket: %s\n", dbsock);
00591    }
00592    ast_log(LOG_DEBUG, "MySQL RealTime User: %s\n", dbuser);
00593    ast_log(LOG_DEBUG, "MySQL RealTime Password: %s\n", dbpass);
00594 
00595    return 1;
00596 }
00597 
00598 static int mysql_reconnect(const char *database)
00599 {
00600    char my_database[50];
00601 #ifdef MYSQL_OPT_RECONNECT
00602    my_bool trueval = 1;
00603 #endif
00604 
00605    if(!database || ast_strlen_zero(database))
00606       ast_copy_string(my_database, dbname, sizeof(my_database));
00607    else
00608       ast_copy_string(my_database, database, sizeof(my_database));
00609 
00610    /* mutex lock should have been locked before calling this function. */
00611 
00612 reconnect_tryagain:
00613    if((!connected) && (dbhost || dbsock) && dbuser && dbpass && my_database) {
00614       if(!mysql_init(&mysql)) {
00615          ast_log(LOG_WARNING, "MySQL RealTime: Insufficient memory to allocate MySQL resource.\n");
00616          connected = 0;
00617          return 0;
00618       }
00619       if(mysql_real_connect(&mysql, dbhost, dbuser, dbpass, my_database, dbport, dbsock, 0)) {
00620 #ifdef MYSQL_OPT_RECONNECT
00621          /* The default is no longer to automatically reconnect on failure,
00622           * (as of 5.0.3) so we have to set that option here. */
00623          mysql_options(&mysql, MYSQL_OPT_RECONNECT, &trueval);
00624 #endif
00625          ast_log(LOG_DEBUG, "MySQL RealTime: Successfully connected to database.\n");
00626          connected = 1;
00627          connect_time = time(NULL);
00628          return 1;
00629       } else {
00630          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));
00631          ast_log(LOG_DEBUG, "MySQL RealTime: Cannot Connect (%d): %s\n", mysql_errno(&mysql), mysql_error(&mysql));
00632          connected = 0;
00633          return 0;
00634       }
00635    } else {
00636       /* MySQL likes to return an error, even if it reconnects successfully.
00637        * So the postman pings twice. */
00638       if (mysql_ping(&mysql) != 0 && mysql_ping(&mysql) != 0) {
00639          connected = 0;
00640          ast_log(LOG_ERROR, "MySQL RealTime: Ping failed (%d).  Trying an explicit reconnect.\n", mysql_errno(&mysql));
00641          ast_log(LOG_DEBUG, "MySQL RealTime: Server Error (%d): %s\n", mysql_errno(&mysql), mysql_error(&mysql));
00642          goto reconnect_tryagain;
00643       }
00644 
00645       connected = 1;
00646 
00647       if(mysql_select_db(&mysql, my_database) != 0) {
00648          ast_log(LOG_WARNING, "MySQL RealTime: Unable to select database: %s. Still Connected (%d).\n", my_database, mysql_errno(&mysql));
00649          ast_log(LOG_DEBUG, "MySQL RealTime: Database Select Failed (%d): %s\n", mysql_error(&mysql), mysql_errno(&mysql));
00650          return 0;
00651       }
00652 
00653       ast_log(LOG_DEBUG, "MySQL RealTime: Everything is fine.\n");
00654       return 1;
00655    }
00656 }
00657 
00658 static int realtime_mysql_status(int fd, int argc, char **argv)
00659 {
00660    char status[256], status2[100] = "";
00661    int ctime = time(NULL) - connect_time;
00662 
00663    if(mysql_reconnect(NULL)) {
00664       if(dbhost) {
00665          snprintf(status, 255, "Connected to %s@%s, port %d", dbname, dbhost, dbport);
00666       } else if(dbsock) {
00667          snprintf(status, 255, "Connected to %s on socket file %s", dbname, dbsock);
00668       } else {
00669          snprintf(status, 255, "Connected to %s@%s", dbname, dbhost);
00670       }
00671 
00672       if(dbuser && *dbuser) {
00673          snprintf(status2, 99, " with username %s", dbuser);
00674       }
00675 
00676       if (ctime > 31536000) {
00677          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);
00678       } else if (ctime > 86400) {
00679          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);
00680       } else if (ctime > 3600) {
00681          ast_cli(fd, "%s%s for %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 3600, (ctime % 3600) / 60, ctime % 60);
00682       } else if (ctime > 60) {
00683          ast_cli(fd, "%s%s for %d minutes, %d seconds.\n", status, status2, ctime / 60, ctime % 60);
00684       } else {
00685          ast_cli(fd, "%s%s for %d seconds.\n", status, status2, ctime);
00686       }
00687 
00688       return RESULT_SUCCESS;
00689    } else {
00690       return RESULT_FAILURE;
00691    }
00692 }
00693 
00694 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "MySQL RealTime Configuration Driver");

Generated on Mon May 14 04:43:00 2007 for Asterisk - the Open Source PBX by  doxygen 1.5.1