#include "asterisk.h"
#include <libpq-fe.h>
#include "asterisk/config.h"
#include "asterisk/channel.h"
#include "asterisk/cdr.h"
#include "asterisk/module.h"
Go to the source code of this file.
Data Structures | |
struct | columns |
struct | psql_columns |
Defines | |
#define | DATE_FORMAT "'%Y-%m-%d %T'" |
#define | LENGTHEN_BUF1(size) |
#define | LENGTHEN_BUF2(size) |
Functions | |
static void | __reg_module (void) |
static void | __unreg_module (void) |
static int | config_module (int reload) |
static void | empty_columns (void) |
static int | load_module (void) |
static int | pgsql_log (struct ast_cdr *cdr) |
static int | reload (void) |
static int | unload_module (void) |
Variables | |
static struct ast_module_info | __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "PostgreSQL CDR Backend" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "ac1f6a56484a8820659555499174e588" , .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CDR_DRIVER, } |
static struct ast_module_info * | ast_module_info = &__mod_info |
static const char | config [] = "cdr_pgsql.conf" |
static PGconn * | conn = NULL |
static int | connected = 0 |
static char * | encoding = NULL |
static int | maxsize = 512 |
static int | maxsize2 = 512 |
static const char | name [] = "pgsql" |
static char * | pgdbname = NULL |
static char * | pgdbport = NULL |
static char * | pgdbuser = NULL |
static char * | pghostname = NULL |
static char * | pgpassword = NULL |
static ast_mutex_t | pgsql_lock = { PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP , NULL, 1 } |
static char * | table = NULL |
static char * | tz = NULL |
Definition in file cdr_pgsql.c.
#define DATE_FORMAT "'%Y-%m-%d %T'" |
Definition at line 52 of file cdr_pgsql.c.
#define LENGTHEN_BUF1 | ( | size | ) |
Definition at line 75 of file cdr_pgsql.c.
#define LENGTHEN_BUF2 | ( | size | ) |
Definition at line 90 of file cdr_pgsql.c.
static void __reg_module | ( | void | ) | [static] |
Definition at line 656 of file cdr_pgsql.c.
static void __unreg_module | ( | void | ) | [static] |
Definition at line 656 of file cdr_pgsql.c.
static int config_module | ( | int | reload | ) | [static] |
Definition at line 368 of file cdr_pgsql.c.
References ast_calloc, ast_config_destroy(), ast_config_load, ast_debug, ast_free, ast_log(), AST_MODULE_LOAD_DECLINE, ast_mutex_lock, ast_mutex_unlock, AST_RWLIST_INSERT_TAIL, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, ast_strdup, ast_strdupa, ast_strlen_zero(), ast_variable_browse(), ast_variable_retrieve(), ast_verb, CONFIG_FLAG_FILEUNCHANGED, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEUNCHANGED, empty_columns(), columns::list, LOG_ERROR, LOG_NOTICE, LOG_WARNING, option_debug, pgsql_lock, unload_module, var, and version.
Referenced by load_module(), and reload().
00369 { 00370 struct ast_variable *var; 00371 char *pgerror; 00372 struct columns *cur; 00373 PGresult *result; 00374 const char *tmp; 00375 struct ast_config *cfg; 00376 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; 00377 00378 if ((cfg = ast_config_load(config, config_flags)) == NULL || cfg == CONFIG_STATUS_FILEINVALID) { 00379 ast_log(LOG_WARNING, "Unable to load config for PostgreSQL CDR's: %s\n", config); 00380 return -1; 00381 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) { 00382 return 0; 00383 } 00384 00385 ast_mutex_lock(&pgsql_lock); 00386 00387 if (!(var = ast_variable_browse(cfg, "global"))) { 00388 ast_config_destroy(cfg); 00389 ast_mutex_unlock(&pgsql_lock); 00390 ast_log(LOG_NOTICE, "cdr_pgsql configuration contains no global section, skipping module %s.\n", 00391 reload ? "reload" : "load"); 00392 return -1; 00393 } 00394 00395 if (!(tmp = ast_variable_retrieve(cfg, "global", "hostname"))) { 00396 ast_log(LOG_WARNING, "PostgreSQL server hostname not specified. Assuming unix socket connection\n"); 00397 tmp = ""; /* connect via UNIX-socket by default */ 00398 } 00399 00400 ast_free(pghostname); 00401 if (!(pghostname = ast_strdup(tmp))) { 00402 ast_config_destroy(cfg); 00403 ast_mutex_unlock(&pgsql_lock); 00404 return -1; 00405 } 00406 00407 if (!(tmp = ast_variable_retrieve(cfg, "global", "dbname"))) { 00408 ast_log(LOG_WARNING, "PostgreSQL database not specified. Assuming asterisk\n"); 00409 tmp = "asteriskcdrdb"; 00410 } 00411 00412 ast_free(pgdbname); 00413 if (!(pgdbname = ast_strdup(tmp))) { 00414 ast_config_destroy(cfg); 00415 ast_mutex_unlock(&pgsql_lock); 00416 return -1; 00417 } 00418 00419 if (!(tmp = ast_variable_retrieve(cfg, "global", "user"))) { 00420 ast_log(LOG_WARNING, "PostgreSQL database user not specified. Assuming asterisk\n"); 00421 tmp = "asterisk"; 00422 } 00423 00424 ast_free(pgdbuser); 00425 if (!(pgdbuser = ast_strdup(tmp))) { 00426 ast_config_destroy(cfg); 00427 ast_mutex_unlock(&pgsql_lock); 00428 return -1; 00429 } 00430 00431 if (!(tmp = ast_variable_retrieve(cfg, "global", "password"))) { 00432 ast_log(LOG_WARNING, "PostgreSQL database password not specified. Assuming blank\n"); 00433 tmp = ""; 00434 } 00435 00436 ast_free(pgpassword); 00437 if (!(pgpassword = ast_strdup(tmp))) { 00438 ast_config_destroy(cfg); 00439 ast_mutex_unlock(&pgsql_lock); 00440 return -1; 00441 } 00442 00443 if (!(tmp = ast_variable_retrieve(cfg, "global", "port"))) { 00444 ast_log(LOG_WARNING, "PostgreSQL database port not specified. Using default 5432.\n"); 00445 tmp = "5432"; 00446 } 00447 00448 ast_free(pgdbport); 00449 if (!(pgdbport = ast_strdup(tmp))) { 00450 ast_config_destroy(cfg); 00451 ast_mutex_unlock(&pgsql_lock); 00452 return -1; 00453 } 00454 00455 if (!(tmp = ast_variable_retrieve(cfg, "global", "table"))) { 00456 ast_log(LOG_WARNING, "CDR table not specified. Assuming cdr\n"); 00457 tmp = "cdr"; 00458 } 00459 00460 ast_free(table); 00461 if (!(table = ast_strdup(tmp))) { 00462 ast_config_destroy(cfg); 00463 ast_mutex_unlock(&pgsql_lock); 00464 return -1; 00465 } 00466 00467 if (!(tmp = ast_variable_retrieve(cfg, "global", "encoding"))) { 00468 ast_log(LOG_WARNING, "Encoding not specified. Assuming LATIN9\n"); 00469 tmp = "LATIN9"; 00470 } 00471 00472 ast_free(encoding); 00473 if (!(encoding = ast_strdup(tmp))) { 00474 ast_config_destroy(cfg); 00475 ast_mutex_unlock(&pgsql_lock); 00476 return -1; 00477 } 00478 00479 if (!(tmp = ast_variable_retrieve(cfg, "global", "timezone"))) { 00480 tmp = ""; 00481 } 00482 00483 ast_free(tz); 00484 tz = NULL; 00485 00486 if (!ast_strlen_zero(tmp) && !(tz = ast_strdup(tmp))) { 00487 ast_config_destroy(cfg); 00488 ast_mutex_unlock(&pgsql_lock); 00489 return -1; 00490 } 00491 00492 if (option_debug) { 00493 if (ast_strlen_zero(pghostname)) { 00494 ast_debug(1, "using default unix socket\n"); 00495 } else { 00496 ast_debug(1, "got hostname of %s\n", pghostname); 00497 } 00498 ast_debug(1, "got port of %s\n", pgdbport); 00499 ast_debug(1, "got user of %s\n", pgdbuser); 00500 ast_debug(1, "got dbname of %s\n", pgdbname); 00501 ast_debug(1, "got password of %s\n", pgpassword); 00502 ast_debug(1, "got sql table name of %s\n", table); 00503 ast_debug(1, "got encoding of %s\n", encoding); 00504 ast_debug(1, "got timezone of %s\n", tz); 00505 } 00506 00507 conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword); 00508 if (PQstatus(conn) != CONNECTION_BAD) { 00509 char sqlcmd[768]; 00510 char *fname, *ftype, *flen, *fnotnull, *fdef; 00511 int i, rows, version; 00512 ast_debug(1, "Successfully connected to PostgreSQL database.\n"); 00513 connected = 1; 00514 if (PQsetClientEncoding(conn, encoding)) { 00515 #ifdef HAVE_PGSQL_pg_encoding_to_char 00516 ast_log(LOG_WARNING, "Failed to set encoding to '%s'. Encoding set to default '%s'\n", encoding, pg_encoding_to_char(PQclientEncoding(conn))); 00517 #else 00518 ast_log(LOG_WARNING, "Failed to set encoding to '%s'. Encoding set to default.\n", encoding); 00519 #endif 00520 } 00521 version = PQserverVersion(conn); 00522 00523 if (version >= 70300) { 00524 char *schemaname, *tablename; 00525 if (strchr(table, '.')) { 00526 schemaname = ast_strdupa(table); 00527 tablename = strchr(schemaname, '.'); 00528 *tablename++ = '\0'; 00529 } else { 00530 schemaname = ""; 00531 tablename = table; 00532 } 00533 00534 /* Escape special characters in schemaname */ 00535 if (strchr(schemaname, '\\') || strchr(schemaname, '\'')) { 00536 char *tmp = schemaname, *ptr; 00537 00538 ptr = schemaname = alloca(strlen(tmp) * 2 + 1); 00539 for (; *tmp; tmp++) { 00540 if (strchr("\\'", *tmp)) { 00541 *ptr++ = *tmp; 00542 } 00543 *ptr++ = *tmp; 00544 } 00545 *ptr = '\0'; 00546 } 00547 /* Escape special characters in tablename */ 00548 if (strchr(tablename, '\\') || strchr(tablename, '\'')) { 00549 char *tmp = tablename, *ptr; 00550 00551 ptr = tablename = alloca(strlen(tmp) * 2 + 1); 00552 for (; *tmp; tmp++) { 00553 if (strchr("\\'", *tmp)) { 00554 *ptr++ = *tmp; 00555 } 00556 *ptr++ = *tmp; 00557 } 00558 *ptr = '\0'; 00559 } 00560 00561 snprintf(sqlcmd, sizeof(sqlcmd), "SELECT a.attname, t.typname, a.attlen, a.attnotnull, d.adsrc, a.atttypmod FROM (((pg_catalog.pg_class c INNER JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace AND c.relname = '%s' AND n.nspname = %s%s%s) INNER JOIN pg_catalog.pg_attribute a ON (NOT a.attisdropped) AND a.attnum > 0 AND a.attrelid = c.oid) INNER JOIN pg_catalog.pg_type t ON t.oid = a.atttypid) LEFT OUTER JOIN pg_attrdef d ON a.atthasdef AND d.adrelid = a.attrelid AND d.adnum = a.attnum ORDER BY n.nspname, c.relname, attnum", 00562 tablename, 00563 ast_strlen_zero(schemaname) ? "" : "'", ast_strlen_zero(schemaname) ? "current_schema()" : schemaname, ast_strlen_zero(schemaname) ? "" : "'"); 00564 } else { 00565 snprintf(sqlcmd, sizeof(sqlcmd), "SELECT a.attname, t.typname, a.attlen, a.attnotnull, d.adsrc, a.atttypmod FROM pg_class c, pg_type t, pg_attribute a LEFT OUTER JOIN pg_attrdef d ON a.atthasdef AND d.adrelid = a.attrelid AND d.adnum = a.attnum WHERE c.oid = a.attrelid AND a.atttypid = t.oid AND (a.attnum > 0) AND c.relname = '%s' ORDER BY c.relname, attnum", table); 00566 } 00567 /* Query the columns */ 00568 result = PQexec(conn, sqlcmd); 00569 if (PQresultStatus(result) != PGRES_TUPLES_OK) { 00570 pgerror = PQresultErrorMessage(result); 00571 ast_log(LOG_ERROR, "Failed to query database columns: %s\n", pgerror); 00572 PQclear(result); 00573 unload_module(); 00574 ast_mutex_unlock(&pgsql_lock); 00575 return AST_MODULE_LOAD_DECLINE; 00576 } 00577 00578 rows = PQntuples(result); 00579 if (rows == 0) { 00580 ast_log(LOG_ERROR, "cdr_pgsql: Failed to query database columns. No columns found, does the table exist?\n"); 00581 PQclear(result); 00582 unload_module(); 00583 ast_mutex_unlock(&pgsql_lock); 00584 return AST_MODULE_LOAD_DECLINE; 00585 } 00586 00587 /* Clear out the columns list. */ 00588 empty_columns(); 00589 00590 for (i = 0; i < rows; i++) { 00591 fname = PQgetvalue(result, i, 0); 00592 ftype = PQgetvalue(result, i, 1); 00593 flen = PQgetvalue(result, i, 2); 00594 fnotnull = PQgetvalue(result, i, 3); 00595 fdef = PQgetvalue(result, i, 4); 00596 if (atoi(flen) == -1) { 00597 /* For varchar columns, the maximum length is encoded in a different field */ 00598 flen = PQgetvalue(result, i, 5); 00599 } 00600 ast_verb(4, "Found column '%s' of type '%s'\n", fname, ftype); 00601 cur = ast_calloc(1, sizeof(*cur) + strlen(fname) + strlen(ftype) + 2); 00602 if (cur) { 00603 sscanf(flen, "%30d", &cur->len); 00604 cur->name = (char *)cur + sizeof(*cur); 00605 cur->type = (char *)cur + sizeof(*cur) + strlen(fname) + 1; 00606 strcpy(cur->name, fname); 00607 strcpy(cur->type, ftype); 00608 if (*fnotnull == 't') { 00609 cur->notnull = 1; 00610 } else { 00611 cur->notnull = 0; 00612 } 00613 if (!ast_strlen_zero(fdef)) { 00614 cur->hasdefault = 1; 00615 } else { 00616 cur->hasdefault = 0; 00617 } 00618 AST_RWLIST_WRLOCK(&psql_columns); 00619 AST_RWLIST_INSERT_TAIL(&psql_columns, cur, list); 00620 AST_RWLIST_UNLOCK(&psql_columns); 00621 } 00622 } 00623 PQclear(result); 00624 } else { 00625 pgerror = PQerrorMessage(conn); 00626 ast_log(LOG_ERROR, "Unable to connect to database server %s. CALLS WILL NOT BE LOGGED!!\n", pghostname); 00627 ast_log(LOG_ERROR, "Reason: %s\n", pgerror); 00628 connected = 0; 00629 } 00630 00631 ast_config_destroy(cfg); 00632 00633 ast_mutex_unlock(&pgsql_lock); 00634 return 0; 00635 }
static void empty_columns | ( | void | ) | [static] |
Definition at line 337 of file cdr_pgsql.c.
References ast_free, AST_RWLIST_REMOVE_HEAD, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, and columns::list.
Referenced by config_module(), and unload_module().
00338 { 00339 struct columns *current; 00340 AST_RWLIST_WRLOCK(&psql_columns); 00341 while ((current = AST_RWLIST_REMOVE_HEAD(&psql_columns, list))) { 00342 ast_free(current); 00343 } 00344 AST_RWLIST_UNLOCK(&psql_columns); 00345 00346 }
static int load_module | ( | void | ) | [static] |
Definition at line 637 of file cdr_pgsql.c.
References ast_cdr_register(), AST_MODULE_LOAD_DECLINE, config_module(), and pgsql_log().
00638 { 00639 if (config_module(0)) { 00640 return AST_MODULE_LOAD_DECLINE; 00641 } 00642 return ast_cdr_register(name, ast_module_info->description, pgsql_log) 00643 ? AST_MODULE_LOAD_DECLINE : 0; 00644 }
static int pgsql_log | ( | struct ast_cdr * | cdr | ) | [static] |
Definition at line 104 of file cdr_pgsql.c.
References ast_cdr::answer, ast_cdr_getvar(), ast_debug, ast_free, ast_localtime(), ast_log(), ast_mutex_lock, ast_mutex_unlock, AST_RWLIST_RDLOCK, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, ast_str_append(), ast_str_buffer(), ast_str_create(), ast_str_set(), ast_str_strlen(), ast_strftime(), ast_tvdiff_us(), ast_tvzero(), ast_verb, DATE_FORMAT, ast_cdr::end, first, columns::hasdefault, LENGTHEN_BUF1, LENGTHEN_BUF2, columns::list, LOG_ERROR, LOG_WARNING, maxsize2, columns::name, columns::notnull, pgsql_lock, ast_cdr::start, columns::type, and value.
Referenced by load_module().
00105 { 00106 struct ast_tm tm; 00107 char *pgerror; 00108 PGresult *result; 00109 00110 ast_mutex_lock(&pgsql_lock); 00111 00112 if ((!connected) && pghostname && pgdbuser && pgpassword && pgdbname) { 00113 conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword); 00114 if (PQstatus(conn) != CONNECTION_BAD) { 00115 connected = 1; 00116 if (PQsetClientEncoding(conn, encoding)) { 00117 #ifdef HAVE_PGSQL_pg_encoding_to_char 00118 ast_log(LOG_WARNING, "Failed to set encoding to '%s'. Encoding set to default '%s'\n", encoding, pg_encoding_to_char(PQclientEncoding(conn))); 00119 #else 00120 ast_log(LOG_WARNING, "Failed to set encoding to '%s'. Encoding set to default.\n", encoding); 00121 #endif 00122 } 00123 } else { 00124 pgerror = PQerrorMessage(conn); 00125 ast_log(LOG_ERROR, "Unable to connect to database server %s. Calls will not be logged!\n", pghostname); 00126 ast_log(LOG_ERROR, "Reason: %s\n", pgerror); 00127 PQfinish(conn); 00128 conn = NULL; 00129 } 00130 } 00131 00132 if (connected) { 00133 struct columns *cur; 00134 struct ast_str *sql = ast_str_create(maxsize), *sql2 = ast_str_create(maxsize2); 00135 char buf[257], escapebuf[513], *value; 00136 int first = 1; 00137 00138 if (!sql || !sql2) { 00139 ast_free(sql); 00140 ast_free(sql2); 00141 return -1; 00142 } 00143 00144 ast_str_set(&sql, 0, "INSERT INTO %s (", table); 00145 ast_str_set(&sql2, 0, " VALUES ("); 00146 00147 AST_RWLIST_RDLOCK(&psql_columns); 00148 AST_RWLIST_TRAVERSE(&psql_columns, cur, list) { 00149 /* For fields not set, simply skip them */ 00150 ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0); 00151 if (strcmp(cur->name, "calldate") == 0 && !value) { 00152 ast_cdr_getvar(cdr, "start", &value, buf, sizeof(buf), 0, 0); 00153 } 00154 if (!value) { 00155 if (cur->notnull && !cur->hasdefault) { 00156 /* Field is NOT NULL (but no default), must include it anyway */ 00157 LENGTHEN_BUF1(strlen(cur->name) + 2); 00158 ast_str_append(&sql, 0, "%s\"%s\"", first ? "" : ",", cur->name); 00159 LENGTHEN_BUF2(3); 00160 ast_str_append(&sql2, 0, "%s''", first ? "" : ","); 00161 first = 0; 00162 } 00163 continue; 00164 } 00165 00166 LENGTHEN_BUF1(strlen(cur->name) + 2); 00167 ast_str_append(&sql, 0, "%s\"%s\"", first ? "" : ",", cur->name); 00168 00169 if (strcmp(cur->name, "start") == 0 || strcmp(cur->name, "calldate") == 0) { 00170 if (strncmp(cur->type, "int", 3) == 0) { 00171 LENGTHEN_BUF2(13); 00172 ast_str_append(&sql2, 0, "%s%ld", first ? "" : ",", (long) cdr->start.tv_sec); 00173 } else if (strncmp(cur->type, "float", 5) == 0) { 00174 LENGTHEN_BUF2(31); 00175 ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->start.tv_sec + (double)cdr->start.tv_usec / 1000000.0); 00176 } else { 00177 /* char, hopefully */ 00178 LENGTHEN_BUF2(31); 00179 ast_localtime(&cdr->start, &tm, tz); 00180 ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm); 00181 ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", buf); 00182 } 00183 } else if (strcmp(cur->name, "answer") == 0) { 00184 if (strncmp(cur->type, "int", 3) == 0) { 00185 LENGTHEN_BUF2(13); 00186 ast_str_append(&sql2, 0, "%s%ld", first ? "" : ",", (long) cdr->answer.tv_sec); 00187 } else if (strncmp(cur->type, "float", 5) == 0) { 00188 LENGTHEN_BUF2(31); 00189 ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->answer.tv_sec + (double)cdr->answer.tv_usec / 1000000.0); 00190 } else { 00191 /* char, hopefully */ 00192 LENGTHEN_BUF2(31); 00193 ast_localtime(&cdr->answer, &tm, tz); 00194 ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm); 00195 ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", buf); 00196 } 00197 } else if (strcmp(cur->name, "end") == 0) { 00198 if (strncmp(cur->type, "int", 3) == 0) { 00199 LENGTHEN_BUF2(13); 00200 ast_str_append(&sql2, 0, "%s%ld", first ? "" : ",", (long) cdr->end.tv_sec); 00201 } else if (strncmp(cur->type, "float", 5) == 0) { 00202 LENGTHEN_BUF2(31); 00203 ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->end.tv_sec + (double)cdr->end.tv_usec / 1000000.0); 00204 } else { 00205 /* char, hopefully */ 00206 LENGTHEN_BUF2(31); 00207 ast_localtime(&cdr->end, &tm, tz); 00208 ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm); 00209 ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", buf); 00210 } 00211 } else if (strcmp(cur->name, "duration") == 0 || strcmp(cur->name, "billsec") == 0) { 00212 if (cur->type[0] == 'i') { 00213 /* Get integer, no need to escape anything */ 00214 ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0); 00215 LENGTHEN_BUF2(13); 00216 ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", value); 00217 } else if (strncmp(cur->type, "float", 5) == 0) { 00218 struct timeval *when = cur->name[0] == 'd' ? &cdr->start : ast_tvzero(cdr->answer) ? &cdr->end : &cdr->answer; 00219 LENGTHEN_BUF2(31); 00220 ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double) (ast_tvdiff_us(cdr->end, *when) / 1000000.0)); 00221 } else { 00222 /* Char field, probably */ 00223 struct timeval *when = cur->name[0] == 'd' ? &cdr->start : ast_tvzero(cdr->answer) ? &cdr->end : &cdr->answer; 00224 LENGTHEN_BUF2(31); 00225 ast_str_append(&sql2, 0, "%s'%f'", first ? "" : ",", (double) (ast_tvdiff_us(cdr->end, *when) / 1000000.0)); 00226 } 00227 } else if (strcmp(cur->name, "disposition") == 0 || strcmp(cur->name, "amaflags") == 0) { 00228 if (strncmp(cur->type, "int", 3) == 0) { 00229 /* Integer, no need to escape anything */ 00230 ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 1); 00231 LENGTHEN_BUF2(13); 00232 ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", value); 00233 } else { 00234 /* Although this is a char field, there are no special characters in the values for these fields */ 00235 ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0); 00236 LENGTHEN_BUF2(31); 00237 ast_str_append(&sql2, 0, "%s'%s'", first ? "" : ",", value); 00238 } 00239 } else { 00240 /* Arbitrary field, could be anything */ 00241 ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0); 00242 if (strncmp(cur->type, "int", 3) == 0) { 00243 long long whatever; 00244 if (value && sscanf(value, "%30lld", &whatever) == 1) { 00245 LENGTHEN_BUF2(26); 00246 ast_str_append(&sql2, 0, "%s%lld", first ? "" : ",", whatever); 00247 } else { 00248 LENGTHEN_BUF2(2); 00249 ast_str_append(&sql2, 0, "%s0", first ? "" : ","); 00250 } 00251 } else if (strncmp(cur->type, "float", 5) == 0) { 00252 long double whatever; 00253 if (value && sscanf(value, "%30Lf", &whatever) == 1) { 00254 LENGTHEN_BUF2(51); 00255 ast_str_append(&sql2, 0, "%s%30Lf", first ? "" : ",", whatever); 00256 } else { 00257 LENGTHEN_BUF2(2); 00258 ast_str_append(&sql2, 0, "%s0", first ? "" : ","); 00259 } 00260 /* XXX Might want to handle dates, times, and other misc fields here XXX */ 00261 } else { 00262 if (value) 00263 PQescapeStringConn(conn, escapebuf, value, strlen(value), NULL); 00264 else 00265 escapebuf[0] = '\0'; 00266 LENGTHEN_BUF2(strlen(escapebuf) + 3); 00267 ast_str_append(&sql2, 0, "%s'%s'", first ? "" : ",", escapebuf); 00268 } 00269 } 00270 first = 0; 00271 } 00272 00273 LENGTHEN_BUF1(ast_str_strlen(sql2) + 2); 00274 AST_RWLIST_UNLOCK(&psql_columns); 00275 ast_str_append(&sql, 0, ")%s)", ast_str_buffer(sql2)); 00276 ast_verb(11, "[%s]\n", ast_str_buffer(sql)); 00277 00278 ast_debug(2, "inserting a CDR record.\n"); 00279 00280 /* Test to be sure we're still connected... */ 00281 /* If we're connected, and connection is working, good. */ 00282 /* Otherwise, attempt reconnect. If it fails... sorry... */ 00283 if (PQstatus(conn) == CONNECTION_OK) { 00284 connected = 1; 00285 } else { 00286 ast_log(LOG_ERROR, "Connection was lost... attempting to reconnect.\n"); 00287 PQreset(conn); 00288 if (PQstatus(conn) == CONNECTION_OK) { 00289 ast_log(LOG_ERROR, "Connection reestablished.\n"); 00290 connected = 1; 00291 } else { 00292 pgerror = PQerrorMessage(conn); 00293 ast_log(LOG_ERROR, "Unable to reconnect to database server %s. Calls will not be logged!\n", pghostname); 00294 ast_log(LOG_ERROR, "Reason: %s\n", pgerror); 00295 PQfinish(conn); 00296 conn = NULL; 00297 connected = 0; 00298 ast_mutex_unlock(&pgsql_lock); 00299 ast_free(sql); 00300 ast_free(sql2); 00301 return -1; 00302 } 00303 } 00304 result = PQexec(conn, ast_str_buffer(sql)); 00305 if (PQresultStatus(result) != PGRES_COMMAND_OK) { 00306 pgerror = PQresultErrorMessage(result); 00307 ast_log(LOG_ERROR, "Failed to insert call detail record into database!\n"); 00308 ast_log(LOG_ERROR, "Reason: %s\n", pgerror); 00309 ast_log(LOG_ERROR, "Connection may have been lost... attempting to reconnect.\n"); 00310 PQreset(conn); 00311 if (PQstatus(conn) == CONNECTION_OK) { 00312 ast_log(LOG_ERROR, "Connection reestablished.\n"); 00313 connected = 1; 00314 PQclear(result); 00315 result = PQexec(conn, ast_str_buffer(sql)); 00316 if (PQresultStatus(result) != PGRES_COMMAND_OK) { 00317 pgerror = PQresultErrorMessage(result); 00318 ast_log(LOG_ERROR, "HARD ERROR! Attempted reconnection failed. DROPPING CALL RECORD!\n"); 00319 ast_log(LOG_ERROR, "Reason: %s\n", pgerror); 00320 } 00321 } 00322 ast_mutex_unlock(&pgsql_lock); 00323 PQclear(result); 00324 ast_free(sql); 00325 ast_free(sql2); 00326 return -1; 00327 } 00328 PQclear(result); 00329 ast_free(sql); 00330 ast_free(sql2); 00331 } 00332 ast_mutex_unlock(&pgsql_lock); 00333 return 0; 00334 }
static int reload | ( | void | ) | [static] |
Definition at line 646 of file cdr_pgsql.c.
References config_module().
00647 { 00648 return config_module(1); 00649 }
static int unload_module | ( | void | ) | [static] |
Definition at line 348 of file cdr_pgsql.c.
References ast_cdr_unregister(), ast_free, and empty_columns().
00349 { 00350 ast_cdr_unregister(name); 00351 00352 PQfinish(conn); 00353 00354 ast_free(pghostname); 00355 ast_free(pgdbname); 00356 ast_free(pgdbuser); 00357 ast_free(pgpassword); 00358 ast_free(pgdbport); 00359 ast_free(table); 00360 ast_free(encoding); 00361 ast_free(tz); 00362 00363 empty_columns(); 00364 00365 return 0; 00366 }
struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "PostgreSQL CDR Backend" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "ac1f6a56484a8820659555499174e588" , .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CDR_DRIVER, } [static] |
Definition at line 656 of file cdr_pgsql.c.
struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 656 of file cdr_pgsql.c.
const char config[] = "cdr_pgsql.conf" [static] |
Definition at line 55 of file cdr_pgsql.c.
PGconn* conn = NULL [static] |
Definition at line 62 of file cdr_pgsql.c.
int connected = 0 [static] |
Definition at line 57 of file cdr_pgsql.c.
Referenced by aji_initialize(), ast_channel_connected_line_macro(), ast_channel_queue_connected_line_update(), ast_channel_set_connected_line(), ast_channel_update_connected_line(), ast_connected_line_build_data(), ast_connected_line_parse_data(), connectedline_write(), dial_exec_full(), do_forward(), feature_request_and_dial(), handle_request_invite(), handle_request_update(), handle_response_invite(), misdn_queue_connected_line_update(), parked_call_exec(), sip_call(), socket_process(), and wait_for_winner().
char * encoding = NULL [static] |
int maxsize = 512 [static] |
Definition at line 58 of file cdr_pgsql.c.
int maxsize2 = 512 [static] |
Definition at line 58 of file cdr_pgsql.c.
const char name[] = "pgsql" [static] |
Definition at line 54 of file cdr_pgsql.c.
char * pgdbname = NULL [static] |
Definition at line 56 of file cdr_pgsql.c.
char * pgdbport = NULL [static] |
Definition at line 56 of file cdr_pgsql.c.
char * pgdbuser = NULL [static] |
Definition at line 56 of file cdr_pgsql.c.
char* pghostname = NULL [static] |
Definition at line 56 of file cdr_pgsql.c.
char * pgpassword = NULL [static] |
Definition at line 56 of file cdr_pgsql.c.
ast_mutex_t pgsql_lock = { PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP , NULL, 1 } [static] |
Definition at line 60 of file cdr_pgsql.c.
Referenced by config_module(), config_pgsql(), destroy_pgsql(), find_table(), parse_config(), pgsql_log(), realtime_multi_pgsql(), realtime_pgsql(), require_pgsql(), store_pgsql(), unload_module(), update2_pgsql(), and update_pgsql().
char * table = NULL [static] |
Definition at line 56 of file cdr_pgsql.c.
char * tz = NULL [static] |
Definition at line 56 of file cdr_pgsql.c.
Referenced by ast_get_indication_zone(), ast_tone_zone_ref(), ast_tone_zone_unref(), ast_unregister_indication_country(), ast_var_indications(), ast_var_indications_table(), complete_country(), complete_indications(), handle_cli_indication_add(), handle_cli_indication_remove(), handle_cli_indication_show(), and in_band_indication().