#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 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 = "8586c2a7d357cb591cc3a6607a8f62d1" , .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 51 of file cdr_pgsql.c.
#define LENGTHEN_BUF1 | ( | size | ) |
Definition at line 74 of file cdr_pgsql.c.
#define LENGTHEN_BUF2 | ( | size | ) |
Definition at line 88 of file cdr_pgsql.c.
static void __reg_module | ( | void | ) | [static] |
Definition at line 649 of file cdr_pgsql.c.
static void __unreg_module | ( | void | ) | [static] |
Definition at line 649 of file cdr_pgsql.c.
static int config_module | ( | int | reload | ) | [static] |
Definition at line 378 of file cdr_pgsql.c.
References ast_calloc, ast_cdr_register(), ast_config_destroy(), ast_config_load, ast_debug, ast_free, ast_log(), AST_MODULE_LOAD_DECLINE, AST_RWLIST_INSERT_TAIL, ast_strdup, ast_strdupa, ast_strlen_zero(), ast_variable_browse(), ast_variable_retrieve(), ast_verb, CONFIG_FLAG_FILEUNCHANGED, config_flags, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEUNCHANGED, columns::list, LOG_ERROR, LOG_WARNING, option_debug, pgsql_log(), unload_module, var, and version.
Referenced by load_module(), and reload().
00379 { 00380 struct ast_variable *var; 00381 char *pgerror; 00382 struct columns *cur; 00383 PGresult *result; 00384 const char *tmp; 00385 struct ast_config *cfg; 00386 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; 00387 00388 if ((cfg = ast_config_load(config, config_flags)) == NULL || cfg == CONFIG_STATUS_FILEINVALID) { 00389 ast_log(LOG_WARNING, "Unable to load config for PostgreSQL CDR's: %s\n", config); 00390 return -1; 00391 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) 00392 return 0; 00393 00394 if (!(var = ast_variable_browse(cfg, "global"))) { 00395 ast_config_destroy(cfg); 00396 return 0; 00397 } 00398 00399 if (!(tmp = ast_variable_retrieve(cfg, "global", "hostname"))) { 00400 ast_log(LOG_WARNING, "PostgreSQL server hostname not specified. Assuming unix socket connection\n"); 00401 tmp = ""; /* connect via UNIX-socket by default */ 00402 } 00403 00404 if (pghostname) 00405 ast_free(pghostname); 00406 if (!(pghostname = ast_strdup(tmp))) { 00407 ast_config_destroy(cfg); 00408 return -1; 00409 } 00410 00411 if (!(tmp = ast_variable_retrieve(cfg, "global", "dbname"))) { 00412 ast_log(LOG_WARNING, "PostgreSQL database not specified. Assuming asterisk\n"); 00413 tmp = "asteriskcdrdb"; 00414 } 00415 00416 if (pgdbname) 00417 ast_free(pgdbname); 00418 if (!(pgdbname = ast_strdup(tmp))) { 00419 ast_config_destroy(cfg); 00420 return -1; 00421 } 00422 00423 if (!(tmp = ast_variable_retrieve(cfg, "global", "user"))) { 00424 ast_log(LOG_WARNING, "PostgreSQL database user not specified. Assuming asterisk\n"); 00425 tmp = "asterisk"; 00426 } 00427 00428 if (pgdbuser) 00429 ast_free(pgdbuser); 00430 if (!(pgdbuser = ast_strdup(tmp))) { 00431 ast_config_destroy(cfg); 00432 return -1; 00433 } 00434 00435 if (!(tmp = ast_variable_retrieve(cfg, "global", "password"))) { 00436 ast_log(LOG_WARNING, "PostgreSQL database password not specified. Assuming blank\n"); 00437 tmp = ""; 00438 } 00439 00440 if (pgpassword) 00441 ast_free(pgpassword); 00442 if (!(pgpassword = ast_strdup(tmp))) { 00443 ast_config_destroy(cfg); 00444 return -1; 00445 } 00446 00447 if (!(tmp = ast_variable_retrieve(cfg, "global", "port"))) { 00448 ast_log(LOG_WARNING, "PostgreSQL database port not specified. Using default 5432.\n"); 00449 tmp = "5432"; 00450 } 00451 00452 if (pgdbport) 00453 ast_free(pgdbport); 00454 if (!(pgdbport = ast_strdup(tmp))) { 00455 ast_config_destroy(cfg); 00456 return -1; 00457 } 00458 00459 if (!(tmp = ast_variable_retrieve(cfg, "global", "table"))) { 00460 ast_log(LOG_WARNING, "CDR table not specified. Assuming cdr\n"); 00461 tmp = "cdr"; 00462 } 00463 00464 if (table) 00465 ast_free(table); 00466 if (!(table = ast_strdup(tmp))) { 00467 ast_config_destroy(cfg); 00468 return -1; 00469 } 00470 00471 if (!(tmp = ast_variable_retrieve(cfg, "global", "encoding"))) { 00472 ast_log(LOG_WARNING, "Encoding not specified. Assuming LATIN9\n"); 00473 tmp = "LATIN9"; 00474 } 00475 00476 if (encoding) { 00477 ast_free(encoding); 00478 } 00479 if (!(encoding = ast_strdup(tmp))) { 00480 ast_config_destroy(cfg); 00481 return -1; 00482 } 00483 00484 if (!(tmp = ast_variable_retrieve(cfg, "global", "timezone"))) { 00485 tmp = ""; 00486 } 00487 00488 if (tz) { 00489 ast_free(tz); 00490 tz = NULL; 00491 } 00492 if (!ast_strlen_zero(tmp) && !(tz = ast_strdup(tmp))) { 00493 ast_config_destroy(cfg); 00494 return -1; 00495 } 00496 00497 if (option_debug) { 00498 if (ast_strlen_zero(pghostname)) { 00499 ast_debug(1, "using default unix socket\n"); 00500 } else { 00501 ast_debug(1, "got hostname of %s\n", pghostname); 00502 } 00503 ast_debug(1, "got port of %s\n", pgdbport); 00504 ast_debug(1, "got user of %s\n", pgdbuser); 00505 ast_debug(1, "got dbname of %s\n", pgdbname); 00506 ast_debug(1, "got password of %s\n", pgpassword); 00507 ast_debug(1, "got sql table name of %s\n", table); 00508 ast_debug(1, "got encoding of %s\n", encoding); 00509 ast_debug(1, "got timezone of %s\n", tz); 00510 } 00511 00512 conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword); 00513 if (PQstatus(conn) != CONNECTION_BAD) { 00514 char sqlcmd[768]; 00515 char *fname, *ftype, *flen, *fnotnull, *fdef; 00516 int i, rows, version; 00517 ast_debug(1, "Successfully connected to PostgreSQL database.\n"); 00518 connected = 1; 00519 if (PQsetClientEncoding(conn, encoding)) { 00520 #ifdef HAVE_PGSQL_pg_encoding_to_char 00521 ast_log(LOG_WARNING, "Failed to set encoding to '%s'. Encoding set to default '%s'\n", encoding, pg_encoding_to_char(PQclientEncoding(conn))); 00522 #else 00523 ast_log(LOG_WARNING, "Failed to set encoding to '%s'. Encoding set to default.\n", encoding); 00524 #endif 00525 } 00526 version = PQserverVersion(conn); 00527 00528 if (version >= 70300) { 00529 char *schemaname, *tablename; 00530 if (strchr(table, '.')) { 00531 schemaname = ast_strdupa(table); 00532 tablename = strchr(schemaname, '.'); 00533 *tablename++ = '\0'; 00534 } else { 00535 schemaname = ""; 00536 tablename = table; 00537 } 00538 00539 /* Escape special characters in schemaname */ 00540 if (strchr(schemaname, '\\') || strchr(schemaname, '\'')) { 00541 char *tmp = schemaname, *ptr; 00542 00543 ptr = schemaname = alloca(strlen(tmp) * 2 + 1); 00544 for (; *tmp; tmp++) { 00545 if (strchr("\\'", *tmp)) { 00546 *ptr++ = *tmp; 00547 } 00548 *ptr++ = *tmp; 00549 } 00550 *ptr = '\0'; 00551 } 00552 /* Escape special characters in tablename */ 00553 if (strchr(tablename, '\\') || strchr(tablename, '\'')) { 00554 char *tmp = tablename, *ptr; 00555 00556 ptr = tablename = alloca(strlen(tmp) * 2 + 1); 00557 for (; *tmp; tmp++) { 00558 if (strchr("\\'", *tmp)) { 00559 *ptr++ = *tmp; 00560 } 00561 *ptr++ = *tmp; 00562 } 00563 *ptr = '\0'; 00564 } 00565 00566 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", 00567 tablename, 00568 ast_strlen_zero(schemaname) ? "" : "'", ast_strlen_zero(schemaname) ? "current_schema()" : schemaname, ast_strlen_zero(schemaname) ? "" : "'"); 00569 } else { 00570 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); 00571 } 00572 /* Query the columns */ 00573 result = PQexec(conn, sqlcmd); 00574 if (PQresultStatus(result) != PGRES_TUPLES_OK) { 00575 pgerror = PQresultErrorMessage(result); 00576 ast_log(LOG_ERROR, "Failed to query database columns: %s\n", pgerror); 00577 PQclear(result); 00578 unload_module(); 00579 return AST_MODULE_LOAD_DECLINE; 00580 } 00581 00582 rows = PQntuples(result); 00583 if (rows == 0) { 00584 ast_log(LOG_ERROR, "cdr_pgsql: Failed to query database columns. No columns found, does the table exist?\n"); 00585 PQclear(result); 00586 unload_module(); 00587 return AST_MODULE_LOAD_DECLINE; 00588 } 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_INSERT_TAIL(&psql_columns, cur, list); 00619 } 00620 } 00621 PQclear(result); 00622 } else { 00623 pgerror = PQerrorMessage(conn); 00624 ast_log(LOG_ERROR, "Unable to connect to database server %s. CALLS WILL NOT BE LOGGED!!\n", pghostname); 00625 ast_log(LOG_ERROR, "Reason: %s\n", pgerror); 00626 connected = 0; 00627 } 00628 00629 ast_config_destroy(cfg); 00630 00631 return ast_cdr_register(name, ast_module_info->description, pgsql_log); 00632 }
static int load_module | ( | void | ) | [static] |
Definition at line 634 of file cdr_pgsql.c.
References AST_MODULE_LOAD_DECLINE, and config_module().
00635 { 00636 return config_module(0) ? AST_MODULE_LOAD_DECLINE : 0; 00637 }
static int pgsql_log | ( | struct ast_cdr * | cdr | ) | [static] |
Definition at line 101 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 config_module().
00102 { 00103 struct ast_tm tm; 00104 char *pgerror; 00105 PGresult *result; 00106 00107 ast_mutex_lock(&pgsql_lock); 00108 00109 if ((!connected) && pghostname && pgdbuser && pgpassword && pgdbname) { 00110 conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword); 00111 if (PQstatus(conn) != CONNECTION_BAD) { 00112 connected = 1; 00113 if (PQsetClientEncoding(conn, encoding)) { 00114 #ifdef HAVE_PGSQL_pg_encoding_to_char 00115 ast_log(LOG_WARNING, "Failed to set encoding to '%s'. Encoding set to default '%s'\n", encoding, pg_encoding_to_char(PQclientEncoding(conn))); 00116 #else 00117 ast_log(LOG_WARNING, "Failed to set encoding to '%s'. Encoding set to default.\n", encoding); 00118 #endif 00119 } 00120 } else { 00121 pgerror = PQerrorMessage(conn); 00122 ast_log(LOG_ERROR, "Unable to connect to database server %s. Calls will not be logged!\n", pghostname); 00123 ast_log(LOG_ERROR, "Reason: %s\n", pgerror); 00124 PQfinish(conn); 00125 conn = NULL; 00126 } 00127 } 00128 00129 if (connected) { 00130 struct columns *cur; 00131 struct ast_str *sql = ast_str_create(maxsize), *sql2 = ast_str_create(maxsize2); 00132 char buf[257], escapebuf[513], *value; 00133 int first = 1; 00134 00135 if (!sql || !sql2) { 00136 if (sql) { 00137 ast_free(sql); 00138 } 00139 if (sql2) { 00140 ast_free(sql2); 00141 } 00142 return -1; 00143 } 00144 00145 ast_str_set(&sql, 0, "INSERT INTO %s (", table); 00146 ast_str_set(&sql2, 0, " VALUES ("); 00147 00148 AST_RWLIST_RDLOCK(&psql_columns); 00149 AST_RWLIST_TRAVERSE(&psql_columns, cur, list) { 00150 /* For fields not set, simply skip them */ 00151 ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0); 00152 if (strcmp(cur->name, "calldate") == 0 && !value) { 00153 ast_cdr_getvar(cdr, "start", &value, buf, sizeof(buf), 0, 0); 00154 } 00155 if (!value) { 00156 if (cur->notnull && !cur->hasdefault) { 00157 /* Field is NOT NULL (but no default), must include it anyway */ 00158 LENGTHEN_BUF1(strlen(cur->name) + 2); 00159 ast_str_append(&sql, 0, "%s\"%s\"", first ? "" : ",", cur->name); 00160 LENGTHEN_BUF2(3); 00161 ast_str_append(&sql2, 0, "%s''", first ? "" : ","); 00162 first = 0; 00163 } 00164 continue; 00165 } 00166 00167 LENGTHEN_BUF1(strlen(cur->name) + 2); 00168 ast_str_append(&sql, 0, "%s\"%s\"", first ? "" : ",", cur->name); 00169 00170 if (strcmp(cur->name, "start") == 0 || strcmp(cur->name, "calldate") == 0) { 00171 if (strncmp(cur->type, "int", 3) == 0) { 00172 LENGTHEN_BUF2(13); 00173 ast_str_append(&sql2, 0, "%s%ld", first ? "" : ",", (long) cdr->start.tv_sec); 00174 } else if (strncmp(cur->type, "float", 5) == 0) { 00175 LENGTHEN_BUF2(31); 00176 ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->start.tv_sec + (double)cdr->start.tv_usec / 1000000.0); 00177 } else { 00178 /* char, hopefully */ 00179 LENGTHEN_BUF2(31); 00180 ast_localtime(&cdr->start, &tm, tz); 00181 ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm); 00182 ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", buf); 00183 } 00184 } else if (strcmp(cur->name, "answer") == 0) { 00185 if (strncmp(cur->type, "int", 3) == 0) { 00186 LENGTHEN_BUF2(13); 00187 ast_str_append(&sql2, 0, "%s%ld", first ? "" : ",", (long) cdr->answer.tv_sec); 00188 } else if (strncmp(cur->type, "float", 5) == 0) { 00189 LENGTHEN_BUF2(31); 00190 ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->answer.tv_sec + (double)cdr->answer.tv_usec / 1000000.0); 00191 } else { 00192 /* char, hopefully */ 00193 LENGTHEN_BUF2(31); 00194 ast_localtime(&cdr->answer, &tm, tz); 00195 ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm); 00196 ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", buf); 00197 } 00198 } else if (strcmp(cur->name, "end") == 0) { 00199 if (strncmp(cur->type, "int", 3) == 0) { 00200 LENGTHEN_BUF2(13); 00201 ast_str_append(&sql2, 0, "%s%ld", first ? "" : ",", (long) cdr->end.tv_sec); 00202 } else if (strncmp(cur->type, "float", 5) == 0) { 00203 LENGTHEN_BUF2(31); 00204 ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->end.tv_sec + (double)cdr->end.tv_usec / 1000000.0); 00205 } else { 00206 /* char, hopefully */ 00207 LENGTHEN_BUF2(31); 00208 ast_localtime(&cdr->end, &tm, tz); 00209 ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm); 00210 ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", buf); 00211 } 00212 } else if (strcmp(cur->name, "duration") == 0 || strcmp(cur->name, "billsec") == 0) { 00213 if (cur->type[0] == 'i') { 00214 /* Get integer, no need to escape anything */ 00215 ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0); 00216 LENGTHEN_BUF2(13); 00217 ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", value); 00218 } else if (strncmp(cur->type, "float", 5) == 0) { 00219 struct timeval *when = cur->name[0] == 'd' ? &cdr->start : ast_tvzero(cdr->answer) ? &cdr->end : &cdr->answer; 00220 LENGTHEN_BUF2(31); 00221 ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double) (ast_tvdiff_us(cdr->end, *when) / 1000000.0)); 00222 } else { 00223 /* Char field, probably */ 00224 struct timeval *when = cur->name[0] == 'd' ? &cdr->start : ast_tvzero(cdr->answer) ? &cdr->end : &cdr->answer; 00225 LENGTHEN_BUF2(31); 00226 ast_str_append(&sql2, 0, "%s'%f'", first ? "" : ",", (double) (ast_tvdiff_us(cdr->end, *when) / 1000000.0)); 00227 } 00228 } else if (strcmp(cur->name, "disposition") == 0 || strcmp(cur->name, "amaflags") == 0) { 00229 if (strncmp(cur->type, "int", 3) == 0) { 00230 /* Integer, no need to escape anything */ 00231 ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 1); 00232 LENGTHEN_BUF2(13); 00233 ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", value); 00234 } else { 00235 /* Although this is a char field, there are no special characters in the values for these fields */ 00236 ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0); 00237 LENGTHEN_BUF2(31); 00238 ast_str_append(&sql2, 0, "%s'%s'", first ? "" : ",", value); 00239 } 00240 } else { 00241 /* Arbitrary field, could be anything */ 00242 ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0); 00243 if (strncmp(cur->type, "int", 3) == 0) { 00244 long long whatever; 00245 if (value && sscanf(value, "%30lld", &whatever) == 1) { 00246 LENGTHEN_BUF2(26); 00247 ast_str_append(&sql2, 0, "%s%lld", first ? "" : ",", whatever); 00248 } else { 00249 LENGTHEN_BUF2(2); 00250 ast_str_append(&sql2, 0, "%s0", first ? "" : ","); 00251 } 00252 } else if (strncmp(cur->type, "float", 5) == 0) { 00253 long double whatever; 00254 if (value && sscanf(value, "%30Lf", &whatever) == 1) { 00255 LENGTHEN_BUF2(51); 00256 ast_str_append(&sql2, 0, "%s%30Lf", first ? "" : ",", whatever); 00257 } else { 00258 LENGTHEN_BUF2(2); 00259 ast_str_append(&sql2, 0, "%s0", first ? "" : ","); 00260 } 00261 /* XXX Might want to handle dates, times, and other misc fields here XXX */ 00262 } else { 00263 if (value) 00264 PQescapeStringConn(conn, escapebuf, value, strlen(value), NULL); 00265 else 00266 escapebuf[0] = '\0'; 00267 LENGTHEN_BUF2(strlen(escapebuf) + 3); 00268 ast_str_append(&sql2, 0, "%s'%s'", first ? "" : ",", escapebuf); 00269 } 00270 } 00271 first = 0; 00272 } 00273 AST_RWLIST_UNLOCK(&psql_columns); 00274 LENGTHEN_BUF1(ast_str_strlen(sql2) + 2); 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 639 of file cdr_pgsql.c.
References config_module().
00640 { 00641 return config_module(1); 00642 }
static int unload_module | ( | void | ) | [static] |
Definition at line 336 of file cdr_pgsql.c.
References ast_cdr_unregister(), ast_free, AST_RWLIST_REMOVE_HEAD, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, and columns::list.
00337 { 00338 struct columns *current; 00339 00340 ast_cdr_unregister(name); 00341 00342 PQfinish(conn); 00343 00344 if (pghostname) { 00345 ast_free(pghostname); 00346 } 00347 if (pgdbname) { 00348 ast_free(pgdbname); 00349 } 00350 if (pgdbuser) { 00351 ast_free(pgdbuser); 00352 } 00353 if (pgpassword) { 00354 ast_free(pgpassword); 00355 } 00356 if (pgdbport) { 00357 ast_free(pgdbport); 00358 } 00359 if (table) { 00360 ast_free(table); 00361 } 00362 if (encoding) { 00363 ast_free(encoding); 00364 } 00365 if (tz) { 00366 ast_free(tz); 00367 } 00368 00369 AST_RWLIST_WRLOCK(&psql_columns); 00370 while ((current = AST_RWLIST_REMOVE_HEAD(&psql_columns, list))) { 00371 ast_free(current); 00372 } 00373 AST_RWLIST_UNLOCK(&psql_columns); 00374 00375 return 0; 00376 }
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 = "8586c2a7d357cb591cc3a6607a8f62d1" , .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CDR_DRIVER, } [static] |
Definition at line 649 of file cdr_pgsql.c.
struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 649 of file cdr_pgsql.c.
const char config[] = "cdr_pgsql.conf" [static] |
Definition at line 54 of file cdr_pgsql.c.
PGconn* conn = NULL [static] |
Definition at line 61 of file cdr_pgsql.c.
int connected = 0 [static] |
Definition at line 56 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(), feature_request_and_dial(), handle_request_invite(), handle_request_update(), handle_response_invite(), misdn_queue_connected_line_update(), and socket_process().
char * encoding = NULL [static] |
int maxsize = 512 [static] |
Definition at line 57 of file cdr_pgsql.c.
int maxsize2 = 512 [static] |
Definition at line 57 of file cdr_pgsql.c.
const char name[] = "pgsql" [static] |
Definition at line 53 of file cdr_pgsql.c.
char * pgdbname = NULL [static] |
Definition at line 55 of file cdr_pgsql.c.
char * pgdbport = NULL [static] |
Definition at line 55 of file cdr_pgsql.c.
char * pgdbuser = NULL [static] |
Definition at line 55 of file cdr_pgsql.c.
char* pghostname = NULL [static] |
Definition at line 55 of file cdr_pgsql.c.
char * pgpassword = NULL [static] |
Definition at line 55 of file cdr_pgsql.c.
ast_mutex_t pgsql_lock = { PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP , NULL, 1 } [static] |
Definition at line 59 of file cdr_pgsql.c.
Referenced by config_pgsql(), destroy_pgsql(), 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 55 of file cdr_pgsql.c.
char * tz = NULL [static] |
Definition at line 55 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().