经典代码(三):asterisk

来源:互联网 发布:手机淘宝搭配套餐链接 编辑:程序博客网 时间:2024/05/01 21:11

/*
 * Asterisk -- A telephony toolkit for Linux.
 *
 * Copyright (C) 1999-2005, Digium, Inc.
 *
 * Mark Spencer <markster@digium.com>  - Asterisk Author
 * Matthew Boehm <mboehm@cytelcom.com> - MySQL RealTime Driver Author
 *
 * res_config_mysql.c <mysql plugin for RealTime configuration engine>
 *
 * v2.0   - (10-07-05) - mutex_lock fixes (bug #4973, comment #0034602)
 *
 * v1.9   - (08-19-05) - Added support to correctly honor the family database specified
 *                       in extconfig.conf (bug #4973)
 *
 * v1.8   - (04-21-05) - Modified return values of update_mysql to better indicate
 *                       what really happened.
 *
 * v1.7   - (01-28-05) - Fixed non-initialization of ast_category struct
 *                       in realtime_multi_mysql function which caused segfault.
 *
 * v1.6   - (00-00-00) - Skipped to bring comments into sync with version number in CVS.
 *
 * v1.5.1 - (01-26-05) - Added better(?) locking stuff
 *
 * 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.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)
 */

/*** MODULEINFO
    <depend>mysqlclient</depend>
 ***/

#include <asterisk.h>

#include <asterisk/channel.h>
#include <asterisk/logger.h>
#include <asterisk/config.h>
#include <asterisk/module.h>
#include <asterisk/lock.h>
#include <asterisk/options.h>
#include <asterisk/cli.h>
#include <asterisk/utils.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdio.h>
#include <mysql/mysql.h>
#include <mysql/mysql_version.h>
#include <mysql/errmsg.h>

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;

static int parse_config(void);
static int mysql_reconnect(const char *database);
static int realtime_mysql_status(int fd, int argc, char **argv);

static char cli_realtime_mysql_status_usage[] =
"Usage: realtime mysql status/n"
"       Shows connection information for the MySQL RealTime driver/n";

static struct ast_cli_entry cli_realtime_mysql_status = {
        { "realtime", "mysql", "status", NULL }, realtime_mysql_status,
        "Shows connection information for the MySQL RealTime driver", cli_realtime_mysql_status_usage, NULL };

static struct ast_variable *realtime_mysql(const char *database, const char *table, va_list ap)
{
    MYSQL_RES *result;
    MYSQL_ROW row;
    MYSQL_FIELD *fields;
    int numFields, i, valsz;
    char sql[512];
    char buf[511]; /* Keep this size uneven as it is 2n+1. */
    char *stringp;
    char *chunk;
    char *op;
    const char *newparam, *newval;
    struct ast_variable *var=NULL, *prev=NULL;

    if(!table) {
        ast_log(LOG_WARNING, "MySQL RealTime: 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");
        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);
        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 ((valsz = strlen (newval)) * 2 + 1 > sizeof(buf))
        valsz = (sizeof(buf) - 1) / 2;
    mysql_real_escape_string(&mysql, 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))
            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);
    }
    va_end(ap);

    ast_log(LOG_DEBUG, "MySQL RealTime: 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));
        ast_mutex_unlock(&mysql_lock);
        return NULL;
    }

    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 {                               
        ast_log(LOG_WARNING, "MySQL RealTime: Could not find any rows in table %s./n", table);
    }

    ast_mutex_unlock(&mysql_lock);
    mysql_free_result(result);

    return var;
}

static struct ast_variable *procedure_record_mysql(const char *database, const char *procedure, va_list ap)
{
    MYSQL_RES *result;
    MYSQL_ROW row;
    MYSQL_FIELD *fields;
    int numFields, i, valsz;
    char sql[512];
    char buf[511]; /* Keep this size uneven as it is 2n+1. */
    char *stringp;
    char *chunk;
    char *newparam;
    char *tail;
    struct ast_variable *var=NULL, *prev=NULL;

    if(!procedure) {
        ast_log(LOG_WARNING, "MySQL RealTime: No procedure specified./n");
        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);
        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 */

    snprintf(sql, sizeof(sql), "call %s.%s(", database, procedure);

    while((newparam = va_arg(ap, const char *)))
    {
        snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s, ", newparam);
    }
    ast_verbose( "sql=%s/n", sql );
    tail = sql + strlen(sql) - 1;
    /*This is for no paramter*/
    if ( *tail == '(' )
    {
        *++tail = ')';
        *++tail = '/0';
    }
    else
    {
        tail = sql + strlen(sql);
        tail = sql + strlen(sql) - 2;
        *tail++ = ')';
        *tail = '/0';
    }
    ast_verbose( "*************sql=%s/n", sql );
    va_end(ap);

    ast_log(LOG_DEBUG, "MySQL RealTime: Retrieve SQL: %s/n", sql);

    /* Execution. */
    /*This is for test*/
    //mysql_query( &mysql, "use ipbx" );
    /*ast_verbose( "Here is ok**********247/n" );
    mysql_query( &mysql, "call p2(1)" );
    ast_verbose( "Here is ok**********249/n" );*/

    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));
        ast_mutex_unlock(&mysql_lock);
        return NULL;
    }

    if((result = mysql_store_result(&mysql)))
    {
        numFields = mysql_num_fields(result);
        fields = mysql_fetch_fields(result);
        ast_verbose( "numFields = %d/n", numFields );
        //ast_verbose( "row = %s/n", mysql_fetch_row(result) );//this can't be inputed why?
        while((row = mysql_fetch_row(result)))
        {
            for(i = 0; i < numFields; i++)
            {
                stringp = row[i];
                ast_verbose( "row[ %d ] = %s/n", i, row[i] );
                while(stringp)
                {
                    chunk = strsep(&stringp, ";");
                    ast_verbose( "---chunk = %s/n", chunk );
                    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);
                            ast_verbose( "-else--fields[i].name = %s/n", fields[i].name );
                            ast_verbose( "-else--chunk = %s/n", chunk );
                        }
                    }
                }
            }
        }
    } else {                               
        ast_log(LOG_WARNING, "MySQL RealTime: Could not find this procedure %s./n", procedure);
    }

    ast_mutex_unlock(&mysql_lock);
    mysql_free_result(result);

    return var;
}

static struct ast_variable *procedure_val_mysql(const char *database, const char *procedure, va_list ap)
{
    MYSQL_RES *result;
    MYSQL_ROW row;
    MYSQL_FIELD *fields;
    int numFields, i, valsz;
    char sql[512];
    char buf[511]; /* Keep this size uneven as it is 2n+1. */
    char *stringp;
    char *chunk;
    char *newparam;
    char *tail;
    int x;
    struct ast_variable *var=NULL, *prev=NULL;

    if(!procedure) {
        ast_log(LOG_WARNING, "MySQL RealTime: No procedure specified./n");
        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);
        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 */
   
    mysql_query( &mysql, "set @x=x" );
    snprintf(sql, sizeof(sql), "call %s.%s(", database, procedure);

    while((newparam = va_arg(ap, const char *)))
    {
        snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s, ", newparam);
    }
   
    tail = sql + strlen(sql) - 1;
    //This is for no paramter
    if ( *tail == '(' )
    {
        *++tail = ')';
        *++tail = '/0';
    }
    else
    {
        snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "@x");
        tail = sql + strlen(sql);
        *tail++ = ')';
        *tail = '/0';
    }
    ast_verbose( "*************sql=%s/n", sql );
    va_end(ap);

    ast_log(LOG_DEBUG, "MySQL RealTime: Retrieve SQL: %s/n", sql);


    /* Execution. */
    /*This is for test*/
    /*mysql_query( &mysql, "set @x=x" );
    ast_verbose( "Here is ok**********247/n" );
    mysql_query( &mysql, "call atobilling.Auth(448, @x)" );
    ast_verbose( "Here is ok**********249/n" );
    mysql_query( &mysql, "select @x" );
    ast_verbose( "Here is ok**********362/n" );*/
    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));
        ast_mutex_unlock(&mysql_lock);
        return NULL;
    }

    mysql_query( &mysql, "select @x" );
    if((result = mysql_store_result(&mysql)))
    {
        numFields = mysql_num_fields(result);
        fields = mysql_fetch_fields(result);
        ast_verbose( "numFields = %d/n", numFields );
        //ast_verbose( "row = %s/n", mysql_fetch_row(result) );//this can't be inputed why?
        while((row = mysql_fetch_row(result)))
        {
            for(i = 0; i < numFields; i++)
            {
                stringp = row[i];
                ast_verbose( "row[ %d ] = %s/n", i, row[i] );
                while(stringp)
                {
                    chunk = strsep(&stringp, ";");
                    ast_verbose( "---chunk = %s/n", chunk );
                    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);
                            ast_verbose( "-else--fields[i].name = %s/n", fields[i].name );
                            ast_verbose( "-else--chunk = %s/n", chunk );
                        }
                    }
                }
            }
        }
    } else {                               
        ast_log(LOG_WARNING, "MySQL RealTime: Could not find this procedure %s./n", procedure);
    }

    ast_mutex_unlock(&mysql_lock);
    mysql_free_result(result);

    return var;
}

static struct ast_config *realtime_multi_mysql(const char *database, const char *table, va_list ap)
{
    MYSQL_RES *result;
    MYSQL_ROW row;
    MYSQL_FIELD *fields;
    int numFields, i, valsz;
    char sql[512];
    char buf[511]; /* Keep this size uneven as it is 2n+1. */
    const char *initfield = NULL;
    char *stringp;
    char *chunk;
    char *op;
    const char *newparam, *newval;
    struct ast_realloca ra;
    struct ast_variable *var=NULL;
    struct ast_config *cfg = NULL;
    struct ast_category *cat = NULL;

    if(!table) {
        ast_log(LOG_WARNING, "MySQL RealTime: No table specified./n");
        return NULL;
    }
   
    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 *);
    if(!newparam || !newval)  {
        ast_log(LOG_WARNING, "MySQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on./n");
        ast_config_destroy(cfg);
        return NULL;
    }

    initfield = ast_strdupa(newparam);
    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)) {
        ast_mutex_unlock(&mysql_lock);
        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 ((valsz = strlen (newval)) * 2 + 1 > sizeof(buf))
        valsz = (sizeof(buf) - 1) / 2;
    mysql_real_escape_string(&mysql, 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))
            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) {
        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);

    /* 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));
        ast_mutex_unlock(&mysql_lock);
        ast_config_destroy(cfg);
        return NULL;
    }

    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");
                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)) {
                            ast_category_rename(cat, chunk);
                        }
                        var = ast_variable_new(fields[i].name, chunk);
                        ast_variable_append(cat, var);
                    }
                }
            }
            ast_category_append(cfg, cat);
        }
    } else {
        ast_log(LOG_WARNING, "MySQL RealTime: Could not find any rows in table %s./n", table);
    }

    ast_mutex_unlock(&mysql_lock);
    mysql_free_result(result);

    return cfg;
}

static int update_mysql(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
{
    my_ulonglong numrows;
    char sql[512];
    char buf[511]; /* Keep this size uneven as it is 2n+1. */
    int valsz;
    const char *newparam, *newval;

    if(!table) {
        ast_log(LOG_WARNING, "MySQL RealTime: 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");
               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);
        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))
        valsz = (sizeof(buf) - 1) / 2;
    mysql_real_escape_string(&mysql, 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))
            valsz = (sizeof(buf) - 1) / 2;
        mysql_real_escape_string(&mysql, 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))
        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: 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));
        ast_mutex_unlock(&mysql_lock);
        return -1;
    }

    numrows = mysql_affected_rows(&mysql);
    ast_mutex_unlock(&mysql_lock);

    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
     * 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(numrows >= 0)
        return (int)numrows;

    return -1;
}

static struct ast_config *config_mysql(const char *database, const char *table, const char *file, struct ast_config *cfg, int withcomments)
{
    MYSQL_RES *result;
    MYSQL_ROW row;
    my_ulonglong num_rows;
    struct ast_variable *new_v;
    struct ast_category *cur_cat;
    char sql[250] = "";
    char last[80] = "";
    int last_cat_metric = 0;

    last[0] = '/0';

    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);

    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);
        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));
        ast_mutex_unlock(&mysql_lock);
        return NULL;
    }

    if((result = mysql_store_result(&mysql))) {
        num_rows = mysql_num_rows(result);
        ast_log(LOG_DEBUG, "MySQL RealTime: Found %llu rows./n", num_rows);

        /* 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")) {
                if (!ast_config_internal_load(row[2], cfg, 0)) {
                    mysql_free_result(result);
                    ast_mutex_unlock(&mysql_lock);
                    return NULL;
                }
                continue;
            }

            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);
        }
    } 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 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,
    .procedure_record_func = procedure_record_mysql,
    .procedure_val_func = procedure_val_mysql
};

static int load_module(void)
{
    parse_config();

    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));
    }

    ast_config_engine_register(&mysql_engine);

    if(option_verbose) {
        ast_verbose("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");
    }

    ast_module_user_hangup_all();

    /* Unlock so something else can destroy the lock. */
    ast_mutex_unlock(&mysql_lock);

    return 0;
}

static int reload(void)
{
    /* Aquire control before doing anything to the module itself. */
    ast_mutex_lock(&mysql_lock);

    mysql_close(&mysql);
    connected = 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));
    }

    ast_verbose(VERBOSE_PREFIX_2 "MySQL RealTime reloaded./n");

    /* Done reloading. Release lock so others can now use driver. */
    ast_mutex_unlock(&mysql_lock);

    return 0;
}

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);
        }

        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(!(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);
                }

        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);
                }

        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(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);
                }
    }
    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);

    return 1;
}

static int mysql_reconnect(const char *database)
{
    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));
    //ast_verbose( "my_database=%s/n", my_database );
    /* mutex lock should have been locked before calling this function. */
    //ast_verbose( "connected=%s,dbhost=%s,dbsock=%s,dbuser=%s,dbpass=%s,my_database=%s/n",connected, dbhost, dbsock, dbuser, dbpass, my_database);
reconnect_tryagain:
    if((!connected) && (dbhost || dbsock) && dbuser && dbpass && my_database)
    {
        //ast_verbose( "*************here is ok************737/n" );
        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(mysql_real_connect(&mysql, dbhost, dbuser, dbpass, my_database, dbport, dbsock, 131072))
        {
#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);
            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;
            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;
        }

        connected = 1;
        connect_time = time(NULL);

        if(mysql_select_db(&mysql, my_database) != 0)
        {
            ast_log(LOG_WARNING, "MySQL RealTime: Unable to select database: %s. Still Connected (%u)./n", my_database, mysql_errno(&mysql));
            ast_log(LOG_DEBUG, "MySQL RealTime: Database Select Failed (%u): %s/n", mysql_errno(&mysql), mysql_error(&mysql));
            return 0;
        }

        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] = "";
    int ctime = time(NULL) - 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(dbuser && *dbuser) {
            snprintf(status2, 99, " with username %s", dbuser);
        }

        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 {
            ast_cli(fd, "%s%s for %d seconds./n", status, status2, ctime);
        }

        return RESULT_SUCCESS;
    } else {
        return RESULT_FAILURE;
    }
}

AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "MySQL RealTime Configuration Driver");