cdr_mysql.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * James Sharp <jsharp@psychoses.org>
00005  *
00006  * Modified August 2003
00007  * Tilghman Lesher <asterisk__cdr__cdr_mysql__200308@the-tilghman.com>
00008  *
00009  * Modified August 6, 2005
00010  * Joseph Benden <joe@thrallingpenguin.com>
00011  * Added mysql connection timeout parameter
00012  * Added an automatic reconnect as to not lose a cdr record
00013  * Cleaned up the original code to match the coding guidelines
00014  *
00015  * Modified Juli 2006
00016  * Martin Portmann <map@infinitum.ch>
00017  * Added mysql ssl support
00018  *
00019  * See http://www.asterisk.org for more information about
00020  * the Asterisk project. Please do not directly contact
00021  * any of the maintainers of this project for assistance;
00022  * the project provides a web site, mailing lists and IRC
00023  * channels for your use.
00024  *
00025  * This program is free software, distributed under the terms of
00026  * the GNU General Public License Version 2. See the LICENSE file
00027  * at the top of the source tree.
00028  */
00029 
00030 /*!
00031  * \file
00032  * \brief MySQL CDR backend
00033  * \ingroup cdr_drivers
00034  */
00035 
00036 /*** MODULEINFO
00037    <depend>mysqlclient</depend>
00038    <defaultenabled>no</defaultenabled>
00039    <support_level>deprecated</support_level>
00040    <replacement>cdr_adaptive_odbc</replacement>
00041  ***/
00042 
00043 #include "asterisk.h"
00044 
00045 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
00046 
00047 #include <mysql/mysql.h>
00048 #include <mysql/errmsg.h>
00049 
00050 #include "asterisk/config.h"
00051 #include "asterisk/options.h"
00052 #include "asterisk/channel.h"
00053 #include "asterisk/cdr.h"
00054 #include "asterisk/module.h"
00055 #include "asterisk/logger.h"
00056 #include "asterisk/cli.h"
00057 #include "asterisk/strings.h"
00058 #include "asterisk/linkedlists.h"
00059 #include "asterisk/threadstorage.h"
00060 
00061 #define DATE_FORMAT "%Y-%m-%d %T"
00062 
00063 AST_THREADSTORAGE(sql1_buf);
00064 AST_THREADSTORAGE(sql2_buf);
00065 AST_THREADSTORAGE(escape_buf);
00066 
00067 static const char desc[] = "MySQL CDR Backend";
00068 static const char name[] = "mysql";
00069 static const char config[] = "cdr_mysql.conf";
00070 
00071 static struct ast_str *hostname = NULL, *dbname = NULL, *dbuser = NULL, *password = NULL, *dbsock = NULL, *dbtable = NULL, *dbcharset = NULL, *cdrzone = NULL;
00072 
00073 static struct ast_str *ssl_ca = NULL, *ssl_cert = NULL, *ssl_key = NULL;
00074 
00075 static int dbport = 0;
00076 static int connected = 0;
00077 static time_t connect_time = 0;
00078 static int records = 0;
00079 static int totalrecords = 0;
00080 static int timeout = 0;
00081 static int calldate_compat = 0;
00082 
00083 AST_MUTEX_DEFINE_STATIC(mysql_lock);
00084 
00085 struct unload_string {
00086    AST_LIST_ENTRY(unload_string) entry;
00087    struct ast_str *str;
00088 };
00089 
00090 static AST_LIST_HEAD_STATIC(unload_strings, unload_string);
00091 
00092 struct column {
00093    char *name;
00094    char *cdrname;
00095    char *staticvalue;
00096    char *type;
00097    AST_LIST_ENTRY(column) list;
00098 };
00099 
00100 /* Protected with mysql_lock */
00101 static AST_RWLIST_HEAD_STATIC(columns, column);
00102 
00103 static MYSQL mysql = { { NULL }, };
00104 
00105 static char *handle_cli_cdr_mysql_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00106 {
00107    switch (cmd) {
00108    case CLI_INIT:
00109       e->command = "cdr mysql status";
00110       e->usage =
00111          "Usage: cdr mysql status\n"
00112          "       Shows current connection status for cdr_mysql\n";
00113       return NULL;
00114    case CLI_GENERATE:
00115       return NULL;
00116    }
00117 
00118    if (a->argc != 3)
00119       return CLI_SHOWUSAGE;
00120 
00121    if (connected) {
00122       char status[256], status2[100] = "";
00123       int ctime = time(NULL) - connect_time;
00124       if (dbport)
00125          snprintf(status, 255, "Connected to %s@%s, port %d", ast_str_buffer(dbname), ast_str_buffer(hostname), dbport);
00126       else if (dbsock)
00127          snprintf(status, 255, "Connected to %s on socket file %s", ast_str_buffer(dbname), S_OR(ast_str_buffer(dbsock), "default"));
00128       else
00129          snprintf(status, 255, "Connected to %s@%s", ast_str_buffer(dbname), ast_str_buffer(hostname));
00130 
00131       if (!ast_strlen_zero(ast_str_buffer(dbuser)))
00132          snprintf(status2, 99, " with username %s", ast_str_buffer(dbuser));
00133       if (ast_str_strlen(dbtable))
00134          snprintf(status2, 99, " using table %s", ast_str_buffer(dbtable));
00135       if (ctime > 31536000) {
00136          ast_cli(a->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);
00137       } else if (ctime > 86400) {
00138          ast_cli(a->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);
00139       } else if (ctime > 3600) {
00140          ast_cli(a->fd, "%s%s for %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 3600, (ctime % 3600) / 60, ctime % 60);
00141       } else if (ctime > 60) {
00142          ast_cli(a->fd, "%s%s for %d minutes, %d seconds.\n", status, status2, ctime / 60, ctime % 60);
00143       } else {
00144          ast_cli(a->fd, "%s%s for %d seconds.\n", status, status2, ctime);
00145       }
00146       if (records == totalrecords)
00147          ast_cli(a->fd, "  Wrote %d records since last restart.\n", totalrecords);
00148       else
00149          ast_cli(a->fd, "  Wrote %d records since last restart and %d records since last reconnect.\n", totalrecords, records);
00150    } else {
00151       ast_cli(a->fd, "Not currently connected to a MySQL server.\n");
00152    }
00153 
00154    return CLI_SUCCESS;
00155 }
00156 
00157 static struct ast_cli_entry cdr_mysql_status_cli[] = {
00158    AST_CLI_DEFINE(handle_cli_cdr_mysql_status, "Show connection status of cdr_mysql"),
00159 };
00160 
00161 static int mysql_log(struct ast_cdr *cdr)
00162 {
00163    struct ast_str *sql1 = ast_str_thread_get(&sql1_buf, 1024), *sql2 = ast_str_thread_get(&sql2_buf, 1024);
00164    int retries = 5;
00165 #if MYSQL_VERSION_ID >= 50013
00166    my_bool my_bool_true = 1;
00167 #endif
00168 
00169    if (!sql1 || !sql2) {
00170       ast_log(LOG_ERROR, "Memory error\n");
00171       return -1;
00172    }
00173 
00174    ast_mutex_lock(&mysql_lock);
00175 
00176 db_reconnect:
00177    if ((!connected) && (hostname || dbsock) && dbuser && password && dbname && dbtable ) {
00178       /* Attempt to connect */
00179       mysql_init(&mysql);
00180       /* Add option to quickly timeout the connection */
00181       if (timeout && mysql_options(&mysql, MYSQL_OPT_CONNECT_TIMEOUT, (char *)&timeout) != 0) {
00182          ast_log(LOG_ERROR, "mysql_options returned (%d) %s\n", mysql_errno(&mysql), mysql_error(&mysql));
00183       }
00184 #if MYSQL_VERSION_ID >= 50013
00185       /* Add option for automatic reconnection */
00186       if (mysql_options(&mysql, MYSQL_OPT_RECONNECT, &my_bool_true) != 0) {
00187          ast_log(LOG_ERROR, "mysql_options returned (%d) %s\n", mysql_errno(&mysql), mysql_error(&mysql));
00188       }
00189 #endif
00190       if (ssl_ca || ssl_cert || ssl_key) {
00191          mysql_ssl_set(&mysql, ssl_key ? ast_str_buffer(ssl_key) : NULL, ssl_cert ? ast_str_buffer(ssl_cert) : NULL, ssl_ca ? ast_str_buffer(ssl_ca) : NULL, NULL, NULL);
00192       }
00193       if (mysql_real_connect(&mysql, ast_str_buffer(hostname), ast_str_buffer(dbuser), ast_str_buffer(password), ast_str_buffer(dbname), dbport, dbsock && ast_str_strlen(dbsock) ? ast_str_buffer(dbsock) : NULL, ssl_ca ? CLIENT_SSL : 0)) {
00194          connected = 1;
00195          connect_time = time(NULL);
00196          records = 0;
00197          if (dbcharset) {
00198             ast_str_set(&sql1, 0, "SET NAMES '%s'", ast_str_buffer(dbcharset));
00199             mysql_real_query(&mysql, ast_str_buffer(sql1), ast_str_strlen(sql1));
00200             ast_debug(1, "SQL command as follows: %s\n", ast_str_buffer(sql1));
00201          }
00202       } else {
00203          ast_log(LOG_ERROR, "Cannot connect to database server %s: (%d) %s\n", ast_str_buffer(hostname), mysql_errno(&mysql), mysql_error(&mysql));
00204          connected = 0;
00205       }
00206    } else {
00207       /* Long connection - ping the server */
00208       int error;
00209       if ((error = mysql_ping(&mysql))) {
00210          connected = 0;
00211          records = 0;
00212          switch (mysql_errno(&mysql)) {
00213             case CR_SERVER_GONE_ERROR:
00214             case CR_SERVER_LOST:
00215                ast_log(LOG_ERROR, "Server has gone away. Attempting to reconnect.\n");
00216                break;
00217             default:
00218                ast_log(LOG_ERROR, "Unknown connection error: (%d) %s\n", mysql_errno(&mysql), mysql_error(&mysql));
00219          }
00220          retries--;
00221          if (retries) {
00222             goto db_reconnect;
00223          } else {
00224             ast_log(LOG_ERROR, "Retried to connect five times, giving up.\n");
00225          }
00226       }
00227    }
00228 
00229    if (connected) {
00230       int column_count = 0;
00231       char *cdrname;
00232       char workspace[2048], *value = NULL;
00233       struct column *entry;
00234       struct ast_str *escape = ast_str_thread_get(&escape_buf, 16);
00235 
00236       ast_str_set(&sql1, 0, "INSERT INTO %s (", AS_OR(dbtable, "cdr"));
00237       ast_str_set(&sql2, 0, ") VALUES (");
00238 
00239       AST_RWLIST_RDLOCK(&columns);
00240       AST_RWLIST_TRAVERSE(&columns, entry, list) {
00241          if (!strcmp(entry->name, "calldate")) {
00242             /*!\note
00243              * For some dumb reason, "calldate" used to be formulated using
00244              * the datetime the record was posted, rather than the start
00245              * time of the call.  If someone really wants the old compatible
00246              * behavior, it's provided here.
00247              */
00248             if (calldate_compat) {
00249                struct timeval tv = ast_tvnow();
00250                struct ast_tm tm;
00251                char timestr[128];
00252                ast_localtime(&tv, &tm, ast_str_strlen(cdrzone) ? ast_str_buffer(cdrzone) : NULL);
00253                ast_strftime(timestr, sizeof(timestr), "%Y-%m-%d %T", &tm);
00254                value = ast_strdupa(timestr);
00255                cdrname = "calldate";
00256             } else {
00257                cdrname = "start";
00258             }
00259          } else {
00260             cdrname = entry->cdrname;
00261          }
00262 
00263          /* Construct SQL */
00264 
00265          /* Need the type and value to determine if we want the raw value or not */
00266          if (entry->staticvalue) {
00267             value = ast_strdupa(entry->staticvalue);
00268          } else if ((!strcmp(cdrname, "start") ||
00269              !strcmp(cdrname, "answer") ||
00270              !strcmp(cdrname, "end") ||
00271              !strcmp(cdrname, "disposition") ||
00272              !strcmp(cdrname, "amaflags")) &&
00273             (strstr(entry->type, "int") ||
00274              strstr(entry->type, "dec") ||
00275              strstr(entry->type, "float") ||
00276              strstr(entry->type, "double") ||
00277              strstr(entry->type, "real") ||
00278              strstr(entry->type, "numeric") ||
00279              strstr(entry->type, "fixed"))) {
00280             ast_cdr_format_var(cdr, cdrname, &value, workspace, sizeof(workspace), 1);
00281          } else if (!strcmp(cdrname, "calldate")) {
00282             /* Skip calldate - the value has already been dup'd */
00283          } else {
00284             ast_cdr_format_var(cdr, cdrname, &value, workspace, sizeof(workspace), 0);
00285          }
00286 
00287          if (value) {
00288             size_t valsz;
00289 
00290             if (column_count++) {
00291                ast_str_append(&sql1, 0, ",");
00292                ast_str_append(&sql2, 0, ",");
00293             }
00294 
00295             if (!strcasecmp(cdrname, "billsec") &&
00296                (strstr(entry->type, "float") ||
00297                strstr(entry->type, "double") ||
00298                strstr(entry->type, "decimal") ||
00299                strstr(entry->type, "numeric") ||
00300                strstr(entry->type, "real"))) {
00301 
00302                if (!ast_tvzero(cdr->answer)) {
00303                   snprintf(workspace, sizeof(workspace), "%lf",
00304                      (double) (ast_tvdiff_us(cdr->end, cdr->answer) / 1000000.0));
00305                } else {
00306                   ast_copy_string(workspace, "0", sizeof(workspace));
00307                }
00308 
00309                if (!ast_strlen_zero(workspace)) {
00310                   value = workspace;
00311                }
00312             }
00313 
00314             if (!strcasecmp(cdrname, "duration") &&
00315                (strstr(entry->type, "float") ||
00316                strstr(entry->type, "double") ||
00317                strstr(entry->type, "decimal") ||
00318                strstr(entry->type, "numeric") ||
00319                strstr(entry->type, "real"))) {
00320 
00321                snprintf(workspace, sizeof(workspace), "%lf",
00322                   (double) (ast_tvdiff_us(cdr->end, cdr->start) / 1000000.0));
00323 
00324                if (!ast_strlen_zero(workspace)) {
00325                   value = workspace;
00326                }
00327             }
00328 
00329             ast_str_make_space(&escape, (valsz = strlen(value)) * 2 + 1);
00330             mysql_real_escape_string(&mysql, ast_str_buffer(escape), value, valsz);
00331 
00332             ast_str_append(&sql1, 0, "`%s`", entry->name);
00333             ast_str_append(&sql2, 0, "'%s'", ast_str_buffer(escape));
00334          }
00335       }
00336       AST_RWLIST_UNLOCK(&columns);
00337 
00338       ast_debug(1, "Inserting a CDR record.\n");
00339       ast_str_append(&sql1, 0, "%s)", ast_str_buffer(sql2));
00340 
00341       ast_debug(1, "SQL command as follows: %s\n", ast_str_buffer(sql1));
00342 
00343       if (mysql_real_query(&mysql, ast_str_buffer(sql1), ast_str_strlen(sql1))) {
00344          ast_log(LOG_ERROR, "Failed to insert into database: (%d) %s\n", mysql_errno(&mysql), mysql_error(&mysql));
00345          mysql_close(&mysql);
00346          connected = 0;
00347       } else {
00348          records++;
00349          totalrecords++;
00350       }
00351    }
00352    ast_mutex_unlock(&mysql_lock);
00353    return 0;
00354 }
00355 
00356 static int my_unload_module(int reload)
00357 { 
00358    struct unload_string *us;
00359    struct column *entry;
00360 
00361    ast_cli_unregister_multiple(cdr_mysql_status_cli, sizeof(cdr_mysql_status_cli) / sizeof(struct ast_cli_entry));
00362 
00363    if (connected) {
00364       mysql_close(&mysql);
00365       connected = 0;
00366       records = 0;
00367    }
00368 
00369    AST_LIST_LOCK(&unload_strings);
00370    while ((us = AST_LIST_REMOVE_HEAD(&unload_strings, entry))) {
00371       ast_free(us->str);
00372       ast_free(us);
00373    }
00374    AST_LIST_UNLOCK(&unload_strings);
00375 
00376    if (!reload) {
00377       AST_RWLIST_WRLOCK(&columns);
00378    }
00379    while ((entry = AST_RWLIST_REMOVE_HEAD(&columns, list))) {
00380       ast_free(entry);
00381    }
00382    if (!reload) {
00383       AST_RWLIST_UNLOCK(&columns);
00384    }
00385 
00386    dbport = 0;
00387    if (reload) {
00388       return ast_cdr_backend_suspend(name);
00389    } else {
00390       return ast_cdr_unregister(name);
00391    }
00392 }
00393 
00394 static int my_load_config_string(struct ast_config *cfg, const char *category, const char *variable, struct ast_str **field, const char *def)
00395 {
00396    struct unload_string *us;
00397    const char *tmp;
00398 
00399    if (!(us = ast_calloc(1, sizeof(*us))))
00400       return -1;
00401 
00402    if (!(*field = ast_str_create(16))) {
00403       ast_free(us);
00404       return -1;
00405    }
00406 
00407    tmp = ast_variable_retrieve(cfg, category, variable);
00408 
00409    ast_str_set(field, 0, "%s", tmp ? tmp : def);
00410 
00411    us->str = *field;
00412 
00413    AST_LIST_LOCK(&unload_strings);
00414    AST_LIST_INSERT_HEAD(&unload_strings, us, entry);
00415    AST_LIST_UNLOCK(&unload_strings);
00416 
00417    return 0;
00418 }
00419 
00420 static int my_load_config_number(struct ast_config *cfg, const char *category, const char *variable, int *field, int def)
00421 {
00422    const char *tmp;
00423 
00424    tmp = ast_variable_retrieve(cfg, category, variable);
00425 
00426    if (!tmp || sscanf(tmp, "%30d", field) < 1)
00427       *field = def;
00428 
00429    return 0;
00430 }
00431 
00432 static int my_load_module(int reload)
00433 {
00434    int res;
00435    struct ast_config *cfg;
00436    struct ast_variable *var;
00437    /* CONFIG_STATUS_FILEUNCHANGED is impossible when config_flags is always 0,
00438     * and it has to be zero, so a reload can be sent to tell the driver to
00439     * rescan the table layout. */
00440    struct ast_flags config_flags = { 0 };
00441    struct column *entry;
00442    char *temp;
00443    struct ast_str *compat;
00444    MYSQL_ROW row;
00445    MYSQL_RES *result;
00446    char sqldesc[128];
00447 #if MYSQL_VERSION_ID >= 50013
00448    my_bool my_bool_true = 1;
00449 #endif
00450 
00451    /* Cannot use a conditionally different flag, because the table layout may
00452     * have changed, which is not detectable by config file change detection,
00453     * but should still cause the configuration to be re-parsed. */
00454    cfg = ast_config_load(config, config_flags);
00455    if (cfg == CONFIG_STATUS_FILEMISSING) {
00456       ast_log(LOG_WARNING, "Unable to load config for mysql CDR's: %s\n", config);
00457       return AST_MODULE_LOAD_SUCCESS;
00458    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
00459       ast_log(LOG_ERROR, "Unable to load configuration file '%s'\n", config);
00460       return AST_MODULE_LOAD_DECLINE;
00461    }
00462 
00463    if (reload) {
00464       AST_RWLIST_WRLOCK(&columns);
00465       my_unload_module(1);
00466    }
00467 
00468    var = ast_variable_browse(cfg, "global");
00469    if (!var) {
00470       /* nothing configured */
00471       if (reload) {
00472          AST_RWLIST_UNLOCK(&columns);
00473       }
00474       ast_config_destroy(cfg);
00475       return AST_MODULE_LOAD_SUCCESS;
00476    }
00477 
00478    res = 0;
00479 
00480    res |= my_load_config_string(cfg, "global", "hostname", &hostname, "localhost");
00481    res |= my_load_config_string(cfg, "global", "dbname", &dbname, "astriskcdrdb");
00482    res |= my_load_config_string(cfg, "global", "user", &dbuser, "root");
00483    res |= my_load_config_string(cfg, "global", "sock", &dbsock, "");
00484    res |= my_load_config_string(cfg, "global", "table", &dbtable, "cdr");
00485    res |= my_load_config_string(cfg, "global", "password", &password, "");
00486 
00487    res |= my_load_config_string(cfg, "global", "charset", &dbcharset, "");
00488 
00489    res |= my_load_config_string(cfg, "global", "ssl_ca", &ssl_ca, "");
00490    res |= my_load_config_string(cfg, "global", "ssl_cert", &ssl_cert, "");
00491    res |= my_load_config_string(cfg, "global", "ssl_key", &ssl_key, "");
00492 
00493    res |= my_load_config_number(cfg, "global", "port", &dbport, 0);
00494    res |= my_load_config_number(cfg, "global", "timeout", &timeout, 0);
00495    res |= my_load_config_string(cfg, "global", "compat", &compat, "no");
00496    res |= my_load_config_string(cfg, "global", "cdrzone", &cdrzone, "");
00497    if (ast_str_strlen(cdrzone) == 0) {
00498       for (; var; var = var->next) {
00499          if (!strcasecmp(var->name, "usegmtime") && ast_true(var->value)) {
00500             ast_str_set(&cdrzone, 0, "UTC");
00501          }
00502       }
00503    }
00504 
00505    if (ast_true(ast_str_buffer(compat))) {
00506       calldate_compat = 1;
00507    } else {
00508       calldate_compat = 0;
00509    }
00510 
00511    if (res < 0) {
00512       if (reload) {
00513          AST_RWLIST_UNLOCK(&columns);
00514       }
00515       ast_config_destroy(cfg);
00516       return AST_MODULE_LOAD_FAILURE;
00517    }
00518 
00519    /* Check for any aliases */
00520    if (!reload) {
00521       /* Lock, if not already */
00522       AST_RWLIST_WRLOCK(&columns);
00523    }
00524    while ((entry = AST_LIST_REMOVE_HEAD(&columns, list))) {
00525       ast_free(entry);
00526    }
00527 
00528    ast_debug(1, "Got hostname of %s\n", ast_str_buffer(hostname));
00529    ast_debug(1, "Got port of %d\n", dbport);
00530    ast_debug(1, "Got a timeout of %d\n", timeout);
00531    if (dbsock)
00532       ast_debug(1, "Got sock file of %s\n", ast_str_buffer(dbsock));
00533    ast_debug(1, "Got user of %s\n", ast_str_buffer(dbuser));
00534    ast_debug(1, "Got dbname of %s\n", ast_str_buffer(dbname));
00535    ast_debug(1, "Got password of %s\n", ast_str_buffer(password));
00536    ast_debug(1, "%sunning in calldate compatibility mode\n", calldate_compat ? "R" : "Not r");
00537    ast_debug(1, "Dates and times are localized to %s\n", S_OR(ast_str_buffer(cdrzone), "local timezone"));
00538 
00539    if (dbcharset) {
00540       ast_debug(1, "Got DB charset of %s\n", ast_str_buffer(dbcharset));
00541    }
00542 
00543    mysql_init(&mysql);
00544 
00545    if (timeout && mysql_options(&mysql, MYSQL_OPT_CONNECT_TIMEOUT, (char *)&timeout) != 0) {
00546       ast_log(LOG_ERROR, "cdr_mysql: mysql_options returned (%d) %s\n", mysql_errno(&mysql), mysql_error(&mysql));
00547    }
00548 
00549 #if MYSQL_VERSION_ID >= 50013
00550    /* Add option for automatic reconnection */
00551    if (mysql_options(&mysql, MYSQL_OPT_RECONNECT, &my_bool_true) != 0) {
00552       ast_log(LOG_ERROR, "cdr_mysql: mysql_options returned (%d) %s\n", mysql_errno(&mysql), mysql_error(&mysql));
00553    }
00554 #endif
00555 
00556    if ((ssl_ca && ast_str_strlen(ssl_ca)) || (ssl_cert && ast_str_strlen(ssl_cert)) || (ssl_key && ast_str_strlen(ssl_key))) {
00557       mysql_ssl_set(&mysql,
00558          ssl_key ? ast_str_buffer(ssl_key) : NULL,
00559          ssl_cert ? ast_str_buffer(ssl_cert) : NULL,
00560          ssl_ca ? ast_str_buffer(ssl_ca) : NULL,
00561          NULL, NULL);
00562    }
00563    temp = dbsock && ast_str_strlen(dbsock) ? ast_str_buffer(dbsock) : NULL;
00564    if (!mysql_real_connect(&mysql, ast_str_buffer(hostname), ast_str_buffer(dbuser), ast_str_buffer(password), ast_str_buffer(dbname), dbport, temp, ssl_ca && ast_str_strlen(ssl_ca) ? CLIENT_SSL : 0)) {
00565       ast_log(LOG_ERROR, "Failed to connect to mysql database %s on %s.\n", ast_str_buffer(dbname), ast_str_buffer(hostname));
00566       connected = 0;
00567       records = 0;
00568    } else {
00569       ast_debug(1, "Successfully connected to MySQL database.\n");
00570       connected = 1;
00571       records = 0;
00572       connect_time = time(NULL);
00573       if (dbcharset) {
00574          snprintf(sqldesc, sizeof(sqldesc), "SET NAMES '%s'", ast_str_buffer(dbcharset));
00575          mysql_real_query(&mysql, sqldesc, strlen(sqldesc));
00576          ast_debug(1, "SQL command as follows: %s\n", sqldesc);
00577       }
00578 
00579       /* Get table description */
00580       snprintf(sqldesc, sizeof(sqldesc), "DESC %s", dbtable ? ast_str_buffer(dbtable) : "cdr");
00581       if (mysql_query(&mysql, sqldesc)) {
00582          ast_log(LOG_ERROR, "Unable to query table description!!  Logging disabled.\n");
00583          mysql_close(&mysql);
00584          connected = 0;
00585          AST_RWLIST_UNLOCK(&columns);
00586          ast_config_destroy(cfg);
00587          return AST_MODULE_LOAD_FAILURE;
00588       }
00589 
00590       if (!(result = mysql_store_result(&mysql))) {
00591          ast_log(LOG_ERROR, "Unable to query table description!!  Logging disabled.\n");
00592          mysql_close(&mysql);
00593          connected = 0;
00594          AST_RWLIST_UNLOCK(&columns);
00595          ast_config_destroy(cfg);
00596          return AST_MODULE_LOAD_FAILURE;
00597       }
00598 
00599       while ((row = mysql_fetch_row(result))) {
00600          struct column *entry;
00601          char *cdrvar = "", *staticvalue = "";
00602 
00603          ast_debug(1, "Got a field '%s' of type '%s'\n", row[0], row[1]);
00604          /* Check for an alias or a static value */
00605          for (var = ast_variable_browse(cfg, "columns"); var; var = var->next) {
00606             if (strncmp(var->name, "alias", 5) == 0 && strcasecmp(var->value, row[0]) == 0 ) {
00607                char *alias = ast_strdupa(var->name + 5);
00608                cdrvar = ast_strip(alias);
00609                ast_verb(3, "Found alias %s for column %s\n", cdrvar, row[0]);
00610                break;
00611             } else if (strncmp(var->name, "static", 6) == 0 && strcasecmp(var->value, row[0]) == 0) {
00612                char *item = ast_strdupa(var->name + 6);
00613                item = ast_strip(item);
00614                if (item[0] == '"' && item[strlen(item) - 1] == '"') {
00615                   /* Remove surrounding quotes */
00616                   item[strlen(item) - 1] = '\0';
00617                   item++;
00618                }
00619                staticvalue = item;
00620             }
00621          }
00622 
00623          entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(row[0]) + 1 + strlen(cdrvar) + 1 + strlen(staticvalue) + 1 + strlen(row[1]) + 1);
00624          if (!entry) {
00625             ast_log(LOG_ERROR, "Out of memory creating entry for column '%s'\n", row[0]);
00626             res = -1;
00627             break;
00628          }
00629 
00630          entry->name = (char *)entry + sizeof(*entry);
00631          strcpy(entry->name, row[0]);
00632 
00633          if (!ast_strlen_zero(cdrvar)) {
00634             entry->cdrname = entry->name + strlen(row[0]) + 1;
00635             strcpy(entry->cdrname, cdrvar);
00636          } else { /* Point to same place as the column name */
00637             entry->cdrname = (char *)entry + sizeof(*entry);
00638          }
00639 
00640          if (!ast_strlen_zero(staticvalue)) {
00641             entry->staticvalue = entry->cdrname + strlen(entry->cdrname) + 1;
00642             strcpy(entry->staticvalue, staticvalue);
00643             ast_debug(1, "staticvalue length: %d\n", (int) strlen(staticvalue) );
00644             entry->type = entry->staticvalue + strlen(entry->staticvalue) + 1;
00645          } else {
00646             entry->type = entry->cdrname + strlen(entry->cdrname) + 1;
00647          }
00648          strcpy(entry->type, row[1]);
00649 
00650          ast_debug(1, "Entry name '%s'\n", entry->name);
00651          ast_debug(1, "   cdrname '%s'\n", entry->cdrname);
00652          ast_debug(1, "    static '%s'\n", entry->staticvalue);
00653          ast_debug(1, "      type '%s'\n", entry->type);
00654 
00655          AST_LIST_INSERT_TAIL(&columns, entry, list);
00656       }
00657       mysql_free_result(result);
00658    }
00659    AST_RWLIST_UNLOCK(&columns);
00660    ast_config_destroy(cfg);
00661    if (res < 0) {
00662       return AST_MODULE_LOAD_FAILURE;
00663    }
00664 
00665    if (!reload) {
00666       res = ast_cdr_register(name, desc, mysql_log);
00667    } else {
00668       res = ast_cdr_backend_unsuspend(name);
00669    }
00670    if (res) {
00671       ast_log(LOG_ERROR, "Unable to register MySQL CDR handling\n");
00672    } else {
00673       res = ast_cli_register_multiple(cdr_mysql_status_cli, sizeof(cdr_mysql_status_cli) / sizeof(struct ast_cli_entry));
00674    }
00675 
00676    return res;
00677 }
00678 
00679 static int load_module(void)
00680 {
00681    return my_load_module(0);
00682 }
00683 
00684 static int unload_module(void)
00685 {
00686    return my_unload_module(0);
00687 }
00688 
00689 static int reload(void)
00690 {
00691    int ret;
00692 
00693    ast_mutex_lock(&mysql_lock);
00694    ret = my_load_module(1);
00695    ast_mutex_unlock(&mysql_lock);
00696 
00697    return ret;
00698 }
00699 
00700 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "MySQL CDR Backend",
00701    .support_level = AST_MODULE_SUPPORT_DEPRECATED,
00702    .load = load_module,
00703    .unload = unload_module,
00704    .reload = reload,
00705 );
00706 

Generated on Thu Apr 16 06:27:17 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6