PostgreSQL CDR logger. More...
#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 |
Defines | |
#define | DATE_FORMAT "'%Y-%m-%d %T'" |
#define | LENGTHEN_BUF1(size) |
#define | LENGTHEN_BUF2(size) |
Functions | |
AST_MODULE_INFO (ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER,"PostgreSQL CDR Backend",.load=load_module,.unload=unload_module,.reload=reload,.load_pri=AST_MODPRI_CDR_DRIVER,) | |
AST_MUTEX_DEFINE_STATIC (pgsql_lock) | |
static | AST_RWLIST_HEAD_STATIC (psql_columns, columns) |
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 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 char * | table = NULL |
static char * | tz = NULL |
PostgreSQL CDR logger.
See also
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.
AST_MODULE_INFO | ( | ASTERISK_GPL_KEY | , | |
AST_MODFLAG_LOAD_ORDER | , | |||
"PostgreSQL CDR Backend" | , | |||
. | load = load_module , |
|||
. | unload = unload_module , |
|||
. | reload = reload , |
|||
. | load_pri = AST_MODPRI_CDR_DRIVER | |||
) |
AST_MUTEX_DEFINE_STATIC | ( | pgsql_lock | ) |
static AST_RWLIST_HEAD_STATIC | ( | psql_columns | , | |
columns | ||||
) | [static] |
static int config_module | ( | int | reload | ) | [static] |
Definition at line 368 of file cdr_pgsql.c.
References ast_alloca, 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::hasdefault, columns::len, LOG_ERROR, LOG_NOTICE, LOG_WARNING, columns::name, columns::notnull, option_debug, pgsql_lock, columns::type, 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 = ast_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 = ast_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, and AST_RWLIST_WRLOCK.
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, 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 }
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(), handle_request_invite(), handle_request_update(), handle_response_invite(), and sip_call().
char * encoding = NULL [static] |
Definition at line 56 of file cdr_pgsql.c.
Referenced by check_header(), and custom_prepare().
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.
Referenced by pgsql_log().
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.
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_unregister_indication_country(), ast_var_indications(), ast_var_indications_table(), complete_country(), complete_indications(), handle_cli_indication_add(), handle_cli_indication_remove(), and handle_cli_indication_show().