#include "asterisk.h"
#include <libpq-fe.h>
#include "asterisk/config.h"
#include "asterisk/channel.h"
#include "asterisk/cdr.h"
#include "asterisk/cli.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 | __fini_psql_columns (void) |
| static void | __init_psql_columns (void) |
| static void | __reg_module (void) |
| static void | __unreg_module (void) |
| static int | config_module (int reload) |
| static void | empty_columns (void) |
| static char * | handle_cdr_pgsql_status (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
| Handle the CLI command cdr show pgsql status. | |
| 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 = AST_BUILDOPT_SUM, .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 struct ast_cli_entry | cdr_pgsql_status_cli [] |
| static const char | config [] = "cdr_pgsql.conf" |
| static PGconn * | conn = NULL |
| static time_t | connect_time = 0 |
| 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_MUTEX_INITIALIZER , NULL, 1 } |
| static int | records |
| static char * | table = NULL |
| static int | totalrecords = 0 |
| static char * | tz = NULL |
Definition in file cdr_pgsql.c.
| #define DATE_FORMAT "'%Y-%m-%d %T'" |
Definition at line 53 of file cdr_pgsql.c.
| #define LENGTHEN_BUF1 | ( | size | ) |
Definition at line 84 of file cdr_pgsql.c.
| #define LENGTHEN_BUF2 | ( | size | ) |
Definition at line 99 of file cdr_pgsql.c.
| static void __fini_psql_columns | ( | void | ) | [static] |
| static void __init_psql_columns | ( | void | ) | [static] |
| static void __reg_module | ( | void | ) | [static] |
Definition at line 739 of file cdr_pgsql.c.
| static void __unreg_module | ( | void | ) | [static] |
Definition at line 739 of file cdr_pgsql.c.
| static int config_module | ( | int | reload | ) | [static] |
Definition at line 449 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, and version.
Referenced by load_module(), and reload().
00450 { 00451 char *pgerror; 00452 struct columns *cur; 00453 PGresult *result; 00454 const char *tmp; 00455 struct ast_config *cfg; 00456 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; 00457 00458 if ((cfg = ast_config_load(config, config_flags)) == NULL || cfg == CONFIG_STATUS_FILEINVALID) { 00459 ast_log(LOG_WARNING, "Unable to load config for PostgreSQL CDR's: %s\n", config); 00460 return -1; 00461 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) { 00462 return 0; 00463 } 00464 00465 ast_mutex_lock(&pgsql_lock); 00466 00467 if (!ast_variable_browse(cfg, "global")) { 00468 ast_config_destroy(cfg); 00469 ast_mutex_unlock(&pgsql_lock); 00470 ast_log(LOG_NOTICE, "cdr_pgsql configuration contains no global section, skipping module %s.\n", 00471 reload ? "reload" : "load"); 00472 return -1; 00473 } 00474 00475 if (!(tmp = ast_variable_retrieve(cfg, "global", "hostname"))) { 00476 ast_log(LOG_WARNING, "PostgreSQL server hostname not specified. Assuming unix socket connection\n"); 00477 tmp = ""; /* connect via UNIX-socket by default */ 00478 } 00479 00480 ast_free(pghostname); 00481 if (!(pghostname = ast_strdup(tmp))) { 00482 ast_config_destroy(cfg); 00483 ast_mutex_unlock(&pgsql_lock); 00484 return -1; 00485 } 00486 00487 if (!(tmp = ast_variable_retrieve(cfg, "global", "dbname"))) { 00488 ast_log(LOG_WARNING, "PostgreSQL database not specified. Assuming asterisk\n"); 00489 tmp = "asteriskcdrdb"; 00490 } 00491 00492 ast_free(pgdbname); 00493 if (!(pgdbname = ast_strdup(tmp))) { 00494 ast_config_destroy(cfg); 00495 ast_mutex_unlock(&pgsql_lock); 00496 return -1; 00497 } 00498 00499 if (!(tmp = ast_variable_retrieve(cfg, "global", "user"))) { 00500 ast_log(LOG_WARNING, "PostgreSQL database user not specified. Assuming asterisk\n"); 00501 tmp = "asterisk"; 00502 } 00503 00504 ast_free(pgdbuser); 00505 if (!(pgdbuser = ast_strdup(tmp))) { 00506 ast_config_destroy(cfg); 00507 ast_mutex_unlock(&pgsql_lock); 00508 return -1; 00509 } 00510 00511 if (!(tmp = ast_variable_retrieve(cfg, "global", "password"))) { 00512 ast_log(LOG_WARNING, "PostgreSQL database password not specified. Assuming blank\n"); 00513 tmp = ""; 00514 } 00515 00516 ast_free(pgpassword); 00517 if (!(pgpassword = ast_strdup(tmp))) { 00518 ast_config_destroy(cfg); 00519 ast_mutex_unlock(&pgsql_lock); 00520 return -1; 00521 } 00522 00523 if (!(tmp = ast_variable_retrieve(cfg, "global", "port"))) { 00524 ast_log(LOG_WARNING, "PostgreSQL database port not specified. Using default 5432.\n"); 00525 tmp = "5432"; 00526 } 00527 00528 ast_free(pgdbport); 00529 if (!(pgdbport = ast_strdup(tmp))) { 00530 ast_config_destroy(cfg); 00531 ast_mutex_unlock(&pgsql_lock); 00532 return -1; 00533 } 00534 00535 if (!(tmp = ast_variable_retrieve(cfg, "global", "table"))) { 00536 ast_log(LOG_WARNING, "CDR table not specified. Assuming cdr\n"); 00537 tmp = "cdr"; 00538 } 00539 00540 ast_free(table); 00541 if (!(table = ast_strdup(tmp))) { 00542 ast_config_destroy(cfg); 00543 ast_mutex_unlock(&pgsql_lock); 00544 return -1; 00545 } 00546 00547 if (!(tmp = ast_variable_retrieve(cfg, "global", "encoding"))) { 00548 ast_log(LOG_WARNING, "Encoding not specified. Assuming LATIN9\n"); 00549 tmp = "LATIN9"; 00550 } 00551 00552 ast_free(encoding); 00553 if (!(encoding = ast_strdup(tmp))) { 00554 ast_config_destroy(cfg); 00555 ast_mutex_unlock(&pgsql_lock); 00556 return -1; 00557 } 00558 00559 if (!(tmp = ast_variable_retrieve(cfg, "global", "timezone"))) { 00560 tmp = ""; 00561 } 00562 00563 ast_free(tz); 00564 tz = NULL; 00565 00566 if (!ast_strlen_zero(tmp) && !(tz = ast_strdup(tmp))) { 00567 ast_config_destroy(cfg); 00568 ast_mutex_unlock(&pgsql_lock); 00569 return -1; 00570 } 00571 00572 if (option_debug) { 00573 if (ast_strlen_zero(pghostname)) { 00574 ast_debug(1, "using default unix socket\n"); 00575 } else { 00576 ast_debug(1, "got hostname of %s\n", pghostname); 00577 } 00578 ast_debug(1, "got port of %s\n", pgdbport); 00579 ast_debug(1, "got user of %s\n", pgdbuser); 00580 ast_debug(1, "got dbname of %s\n", pgdbname); 00581 ast_debug(1, "got password of %s\n", pgpassword); 00582 ast_debug(1, "got sql table name of %s\n", table); 00583 ast_debug(1, "got encoding of %s\n", encoding); 00584 ast_debug(1, "got timezone of %s\n", tz); 00585 } 00586 00587 conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword); 00588 if (PQstatus(conn) != CONNECTION_BAD) { 00589 char sqlcmd[768]; 00590 char *fname, *ftype, *flen, *fnotnull, *fdef; 00591 int i, rows, version; 00592 ast_debug(1, "Successfully connected to PostgreSQL database.\n"); 00593 connected = 1; 00594 connect_time = time(NULL); 00595 records = 0; 00596 if (PQsetClientEncoding(conn, encoding)) { 00597 #ifdef HAVE_PGSQL_pg_encoding_to_char 00598 ast_log(LOG_WARNING, "Failed to set encoding to '%s'. Encoding set to default '%s'\n", encoding, pg_encoding_to_char(PQclientEncoding(conn))); 00599 #else 00600 ast_log(LOG_WARNING, "Failed to set encoding to '%s'. Encoding set to default.\n", encoding); 00601 #endif 00602 } 00603 version = PQserverVersion(conn); 00604 00605 if (version >= 70300) { 00606 char *schemaname, *tablename; 00607 if (strchr(table, '.')) { 00608 schemaname = ast_strdupa(table); 00609 tablename = strchr(schemaname, '.'); 00610 *tablename++ = '\0'; 00611 } else { 00612 schemaname = ""; 00613 tablename = table; 00614 } 00615 00616 /* Escape special characters in schemaname */ 00617 if (strchr(schemaname, '\\') || strchr(schemaname, '\'')) { 00618 char *tmp = schemaname, *ptr; 00619 00620 ptr = schemaname = ast_alloca(strlen(tmp) * 2 + 1); 00621 for (; *tmp; tmp++) { 00622 if (strchr("\\'", *tmp)) { 00623 *ptr++ = *tmp; 00624 } 00625 *ptr++ = *tmp; 00626 } 00627 *ptr = '\0'; 00628 } 00629 /* Escape special characters in tablename */ 00630 if (strchr(tablename, '\\') || strchr(tablename, '\'')) { 00631 char *tmp = tablename, *ptr; 00632 00633 ptr = tablename = ast_alloca(strlen(tmp) * 2 + 1); 00634 for (; *tmp; tmp++) { 00635 if (strchr("\\'", *tmp)) { 00636 *ptr++ = *tmp; 00637 } 00638 *ptr++ = *tmp; 00639 } 00640 *ptr = '\0'; 00641 } 00642 00643 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", 00644 tablename, 00645 ast_strlen_zero(schemaname) ? "" : "'", ast_strlen_zero(schemaname) ? "current_schema()" : schemaname, ast_strlen_zero(schemaname) ? "" : "'"); 00646 } else { 00647 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); 00648 } 00649 /* Query the columns */ 00650 result = PQexec(conn, sqlcmd); 00651 if (PQresultStatus(result) != PGRES_TUPLES_OK) { 00652 pgerror = PQresultErrorMessage(result); 00653 ast_log(LOG_ERROR, "Failed to query database columns: %s\n", pgerror); 00654 PQclear(result); 00655 unload_module(); 00656 ast_mutex_unlock(&pgsql_lock); 00657 return AST_MODULE_LOAD_DECLINE; 00658 } 00659 00660 rows = PQntuples(result); 00661 if (rows == 0) { 00662 ast_log(LOG_ERROR, "cdr_pgsql: Failed to query database columns. No columns found, does the table exist?\n"); 00663 PQclear(result); 00664 unload_module(); 00665 ast_mutex_unlock(&pgsql_lock); 00666 return AST_MODULE_LOAD_DECLINE; 00667 } 00668 00669 /* Clear out the columns list. */ 00670 empty_columns(); 00671 00672 for (i = 0; i < rows; i++) { 00673 fname = PQgetvalue(result, i, 0); 00674 ftype = PQgetvalue(result, i, 1); 00675 flen = PQgetvalue(result, i, 2); 00676 fnotnull = PQgetvalue(result, i, 3); 00677 fdef = PQgetvalue(result, i, 4); 00678 if (atoi(flen) == -1) { 00679 /* For varchar columns, the maximum length is encoded in a different field */ 00680 flen = PQgetvalue(result, i, 5); 00681 } 00682 ast_verb(4, "Found column '%s' of type '%s'\n", fname, ftype); 00683 cur = ast_calloc(1, sizeof(*cur) + strlen(fname) + strlen(ftype) + 2); 00684 if (cur) { 00685 sscanf(flen, "%30d", &cur->len); 00686 cur->name = (char *)cur + sizeof(*cur); 00687 cur->type = (char *)cur + sizeof(*cur) + strlen(fname) + 1; 00688 strcpy(cur->name, fname); 00689 strcpy(cur->type, ftype); 00690 if (*fnotnull == 't') { 00691 cur->notnull = 1; 00692 } else { 00693 cur->notnull = 0; 00694 } 00695 if (!ast_strlen_zero(fdef)) { 00696 cur->hasdefault = 1; 00697 } else { 00698 cur->hasdefault = 0; 00699 } 00700 AST_RWLIST_WRLOCK(&psql_columns); 00701 AST_RWLIST_INSERT_TAIL(&psql_columns, cur, list); 00702 AST_RWLIST_UNLOCK(&psql_columns); 00703 } 00704 } 00705 PQclear(result); 00706 } else { 00707 pgerror = PQerrorMessage(conn); 00708 ast_log(LOG_ERROR, "Unable to connect to database server %s. CALLS WILL NOT BE LOGGED!!\n", pghostname); 00709 ast_log(LOG_ERROR, "Reason: %s\n", pgerror); 00710 connected = 0; 00711 } 00712 00713 ast_config_destroy(cfg); 00714 00715 ast_mutex_unlock(&pgsql_lock); 00716 return 0; 00717 }
| static void empty_columns | ( | void | ) | [static] |
Definition at line 417 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().
00418 { 00419 struct columns *current; 00420 AST_RWLIST_WRLOCK(&psql_columns); 00421 while ((current = AST_RWLIST_REMOVE_HEAD(&psql_columns, list))) { 00422 ast_free(current); 00423 } 00424 AST_RWLIST_UNLOCK(&psql_columns); 00425 00426 }
| static char * handle_cdr_pgsql_status | ( | struct ast_cli_entry * | e, | |
| int | cmd, | |||
| struct ast_cli_args * | a | |||
| ) | [static] |
Handle the CLI command cdr show pgsql status.
Definition at line 114 of file cdr_pgsql.c.
References ast_cli_args::argc, ast_cli(), CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, ast_cli_args::fd, status, and ast_cli_entry::usage.
00115 { 00116 switch (cmd) { 00117 case CLI_INIT: 00118 e->command = "cdr show pgsql status"; 00119 e->usage = 00120 "Usage: cdr show pgsql status\n" 00121 " Shows current connection status for cdr_pgsql\n"; 00122 return NULL; 00123 case CLI_GENERATE: 00124 return NULL; 00125 } 00126 00127 if (a->argc != 3) 00128 return CLI_SHOWUSAGE; 00129 00130 if (connected) { 00131 char status[256], status2[100] = ""; 00132 int ctime = time(NULL) - connect_time; 00133 00134 if (pgdbport) { 00135 snprintf(status, 255, "Connected to %s@%s, port %s", pgdbname, pghostname, pgdbport); 00136 } else { 00137 snprintf(status, 255, "Connected to %s@%s", pgdbname, pghostname); 00138 } 00139 00140 if (pgdbuser && *pgdbuser) { 00141 snprintf(status2, 99, " with username %s", pgdbuser); 00142 } 00143 if (table && *table) { 00144 snprintf(status2, 99, " using table %s", table); 00145 } 00146 if (ctime > 31536000) { 00147 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); 00148 } else if (ctime > 86400) { 00149 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); 00150 } else if (ctime > 3600) { 00151 ast_cli(a->fd, "%s%s for %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 3600, (ctime % 3600) / 60, ctime % 60); 00152 } else if (ctime > 60) { 00153 ast_cli(a->fd, "%s%s for %d minutes, %d seconds.\n", status, status2, ctime / 60, ctime % 60); 00154 } else { 00155 ast_cli(a->fd, "%s%s for %d seconds.\n", status, status2, ctime); 00156 } 00157 if (records == totalrecords) { 00158 ast_cli(a->fd, " Wrote %d records since last restart.\n", totalrecords); 00159 } else { 00160 ast_cli(a->fd, " Wrote %d records since last restart and %d records since last reconnect.\n", totalrecords, records); 00161 } 00162 } else { 00163 ast_cli(a->fd, "Not currently connected to a PgSQL server.\n"); 00164 } 00165 return CLI_SUCCESS; 00166 }
| static int load_module | ( | void | ) | [static] |
Definition at line 719 of file cdr_pgsql.c.
References ast_cdr_register(), ast_cli_register_multiple(), AST_MODULE_LOAD_DECLINE, config_module(), and pgsql_log().
00720 { 00721 ast_cli_register_multiple(cdr_pgsql_status_cli, sizeof(cdr_pgsql_status_cli) / sizeof(struct ast_cli_entry)); 00722 if (config_module(0)) { 00723 return AST_MODULE_LOAD_DECLINE; 00724 } 00725 return ast_cdr_register(name, ast_module_info->description, pgsql_log) 00726 ? AST_MODULE_LOAD_DECLINE : 0; 00727 }
| static int pgsql_log | ( | struct ast_cdr * | cdr | ) | [static] |
Definition at line 168 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().
00169 { 00170 struct ast_tm tm; 00171 char *pgerror; 00172 PGresult *result; 00173 00174 ast_mutex_lock(&pgsql_lock); 00175 00176 if ((!connected) && pghostname && pgdbuser && pgpassword && pgdbname) { 00177 conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword); 00178 if (PQstatus(conn) != CONNECTION_BAD) { 00179 connected = 1; 00180 connect_time = time(NULL); 00181 records = 0; 00182 if (PQsetClientEncoding(conn, encoding)) { 00183 #ifdef HAVE_PGSQL_pg_encoding_to_char 00184 ast_log(LOG_WARNING, "Failed to set encoding to '%s'. Encoding set to default '%s'\n", encoding, pg_encoding_to_char(PQclientEncoding(conn))); 00185 #else 00186 ast_log(LOG_WARNING, "Failed to set encoding to '%s'. Encoding set to default.\n", encoding); 00187 #endif 00188 } 00189 } else { 00190 pgerror = PQerrorMessage(conn); 00191 ast_log(LOG_ERROR, "Unable to connect to database server %s. Calls will not be logged!\n", pghostname); 00192 ast_log(LOG_ERROR, "Reason: %s\n", pgerror); 00193 PQfinish(conn); 00194 conn = NULL; 00195 } 00196 } 00197 00198 if (connected) { 00199 struct columns *cur; 00200 struct ast_str *sql = ast_str_create(maxsize), *sql2 = ast_str_create(maxsize2); 00201 char buf[257], escapebuf[513], *value; 00202 int first = 1; 00203 00204 if (!sql || !sql2) { 00205 ast_free(sql); 00206 ast_free(sql2); 00207 return -1; 00208 } 00209 00210 ast_str_set(&sql, 0, "INSERT INTO %s (", table); 00211 ast_str_set(&sql2, 0, " VALUES ("); 00212 00213 AST_RWLIST_RDLOCK(&psql_columns); 00214 AST_RWLIST_TRAVERSE(&psql_columns, cur, list) { 00215 /* For fields not set, simply skip them */ 00216 ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0); 00217 if (strcmp(cur->name, "calldate") == 0 && !value) { 00218 ast_cdr_getvar(cdr, "start", &value, buf, sizeof(buf), 0, 0); 00219 } 00220 if (!value) { 00221 if (cur->notnull && !cur->hasdefault) { 00222 /* Field is NOT NULL (but no default), must include it anyway */ 00223 LENGTHEN_BUF1(strlen(cur->name) + 2); 00224 ast_str_append(&sql, 0, "%s\"%s\"", first ? "" : ",", cur->name); 00225 LENGTHEN_BUF2(3); 00226 ast_str_append(&sql2, 0, "%s''", first ? "" : ","); 00227 first = 0; 00228 } 00229 continue; 00230 } 00231 00232 LENGTHEN_BUF1(strlen(cur->name) + 2); 00233 ast_str_append(&sql, 0, "%s\"%s\"", first ? "" : ",", cur->name); 00234 00235 if (strcmp(cur->name, "start") == 0 || strcmp(cur->name, "calldate") == 0) { 00236 if (strncmp(cur->type, "int", 3) == 0) { 00237 LENGTHEN_BUF2(13); 00238 ast_str_append(&sql2, 0, "%s%ld", first ? "" : ",", (long) cdr->start.tv_sec); 00239 } else if (strncmp(cur->type, "float", 5) == 0) { 00240 LENGTHEN_BUF2(31); 00241 ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->start.tv_sec + (double)cdr->start.tv_usec / 1000000.0); 00242 } else { 00243 /* char, hopefully */ 00244 LENGTHEN_BUF2(31); 00245 ast_localtime(&cdr->start, &tm, tz); 00246 ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm); 00247 ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", buf); 00248 } 00249 } else if (strcmp(cur->name, "answer") == 0) { 00250 if (strncmp(cur->type, "int", 3) == 0) { 00251 LENGTHEN_BUF2(13); 00252 ast_str_append(&sql2, 0, "%s%ld", first ? "" : ",", (long) cdr->answer.tv_sec); 00253 } else if (strncmp(cur->type, "float", 5) == 0) { 00254 LENGTHEN_BUF2(31); 00255 ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->answer.tv_sec + (double)cdr->answer.tv_usec / 1000000.0); 00256 } else { 00257 /* char, hopefully */ 00258 LENGTHEN_BUF2(31); 00259 ast_localtime(&cdr->answer, &tm, tz); 00260 ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm); 00261 ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", buf); 00262 } 00263 } else if (strcmp(cur->name, "end") == 0) { 00264 if (strncmp(cur->type, "int", 3) == 0) { 00265 LENGTHEN_BUF2(13); 00266 ast_str_append(&sql2, 0, "%s%ld", first ? "" : ",", (long) cdr->end.tv_sec); 00267 } else if (strncmp(cur->type, "float", 5) == 0) { 00268 LENGTHEN_BUF2(31); 00269 ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->end.tv_sec + (double)cdr->end.tv_usec / 1000000.0); 00270 } else { 00271 /* char, hopefully */ 00272 LENGTHEN_BUF2(31); 00273 ast_localtime(&cdr->end, &tm, tz); 00274 ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm); 00275 ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", buf); 00276 } 00277 } else if (strcmp(cur->name, "duration") == 0 || strcmp(cur->name, "billsec") == 0) { 00278 if (cur->type[0] == 'i') { 00279 /* Get integer, no need to escape anything */ 00280 ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0); 00281 LENGTHEN_BUF2(13); 00282 ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", value); 00283 } else if (strncmp(cur->type, "float", 5) == 0) { 00284 struct timeval *when = cur->name[0] == 'd' ? &cdr->start : ast_tvzero(cdr->answer) ? &cdr->end : &cdr->answer; 00285 LENGTHEN_BUF2(31); 00286 ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double) (ast_tvdiff_us(cdr->end, *when) / 1000000.0)); 00287 } else { 00288 /* Char field, probably */ 00289 struct timeval *when = cur->name[0] == 'd' ? &cdr->start : ast_tvzero(cdr->answer) ? &cdr->end : &cdr->answer; 00290 LENGTHEN_BUF2(31); 00291 ast_str_append(&sql2, 0, "%s'%f'", first ? "" : ",", (double) (ast_tvdiff_us(cdr->end, *when) / 1000000.0)); 00292 } 00293 } else if (strcmp(cur->name, "disposition") == 0 || strcmp(cur->name, "amaflags") == 0) { 00294 if (strncmp(cur->type, "int", 3) == 0) { 00295 /* Integer, no need to escape anything */ 00296 ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 1); 00297 LENGTHEN_BUF2(13); 00298 ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", value); 00299 } else { 00300 /* Although this is a char field, there are no special characters in the values for these fields */ 00301 ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0); 00302 LENGTHEN_BUF2(31); 00303 ast_str_append(&sql2, 0, "%s'%s'", first ? "" : ",", value); 00304 } 00305 } else { 00306 /* Arbitrary field, could be anything */ 00307 ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0); 00308 if (strncmp(cur->type, "int", 3) == 0) { 00309 long long whatever; 00310 if (value && sscanf(value, "%30lld", &whatever) == 1) { 00311 LENGTHEN_BUF2(26); 00312 ast_str_append(&sql2, 0, "%s%lld", first ? "" : ",", whatever); 00313 } else { 00314 LENGTHEN_BUF2(2); 00315 ast_str_append(&sql2, 0, "%s0", first ? "" : ","); 00316 } 00317 } else if (strncmp(cur->type, "float", 5) == 0) { 00318 long double whatever; 00319 if (value && sscanf(value, "%30Lf", &whatever) == 1) { 00320 LENGTHEN_BUF2(51); 00321 ast_str_append(&sql2, 0, "%s%30Lf", first ? "" : ",", whatever); 00322 } else { 00323 LENGTHEN_BUF2(2); 00324 ast_str_append(&sql2, 0, "%s0", first ? "" : ","); 00325 } 00326 /* XXX Might want to handle dates, times, and other misc fields here XXX */ 00327 } else { 00328 if (value) 00329 PQescapeStringConn(conn, escapebuf, value, strlen(value), NULL); 00330 else 00331 escapebuf[0] = '\0'; 00332 LENGTHEN_BUF2(strlen(escapebuf) + 3); 00333 ast_str_append(&sql2, 0, "%s'%s'", first ? "" : ",", escapebuf); 00334 } 00335 } 00336 first = 0; 00337 } 00338 00339 LENGTHEN_BUF1(ast_str_strlen(sql2) + 2); 00340 AST_RWLIST_UNLOCK(&psql_columns); 00341 ast_str_append(&sql, 0, ")%s)", ast_str_buffer(sql2)); 00342 ast_verb(11, "[%s]\n", ast_str_buffer(sql)); 00343 00344 ast_debug(2, "inserting a CDR record.\n"); 00345 00346 /* Test to be sure we're still connected... */ 00347 /* If we're connected, and connection is working, good. */ 00348 /* Otherwise, attempt reconnect. If it fails... sorry... */ 00349 if (PQstatus(conn) == CONNECTION_OK) { 00350 connected = 1; 00351 } else { 00352 ast_log(LOG_ERROR, "Connection was lost... attempting to reconnect.\n"); 00353 PQreset(conn); 00354 if (PQstatus(conn) == CONNECTION_OK) { 00355 ast_log(LOG_ERROR, "Connection reestablished.\n"); 00356 connected = 1; 00357 connect_time = time(NULL); 00358 records = 0; 00359 } else { 00360 pgerror = PQerrorMessage(conn); 00361 ast_log(LOG_ERROR, "Unable to reconnect to database server %s. Calls will not be logged!\n", pghostname); 00362 ast_log(LOG_ERROR, "Reason: %s\n", pgerror); 00363 PQfinish(conn); 00364 conn = NULL; 00365 connected = 0; 00366 ast_mutex_unlock(&pgsql_lock); 00367 ast_free(sql); 00368 ast_free(sql2); 00369 return -1; 00370 } 00371 } 00372 result = PQexec(conn, ast_str_buffer(sql)); 00373 if (PQresultStatus(result) != PGRES_COMMAND_OK) { 00374 pgerror = PQresultErrorMessage(result); 00375 ast_log(LOG_ERROR, "Failed to insert call detail record into database!\n"); 00376 ast_log(LOG_ERROR, "Reason: %s\n", pgerror); 00377 ast_log(LOG_ERROR, "Connection may have been lost... attempting to reconnect.\n"); 00378 PQreset(conn); 00379 if (PQstatus(conn) == CONNECTION_OK) { 00380 ast_log(LOG_ERROR, "Connection reestablished.\n"); 00381 connected = 1; 00382 connect_time = time(NULL); 00383 records = 0; 00384 PQclear(result); 00385 result = PQexec(conn, ast_str_buffer(sql)); 00386 if (PQresultStatus(result) != PGRES_COMMAND_OK) { 00387 pgerror = PQresultErrorMessage(result); 00388 ast_log(LOG_ERROR, "HARD ERROR! Attempted reconnection failed. DROPPING CALL RECORD!\n"); 00389 ast_log(LOG_ERROR, "Reason: %s\n", pgerror); 00390 } else { 00391 /* Second try worked out ok */ 00392 totalrecords++; 00393 records++; 00394 ast_mutex_unlock(&pgsql_lock); 00395 PQclear(result); 00396 return 0; 00397 } 00398 } 00399 ast_mutex_unlock(&pgsql_lock); 00400 PQclear(result); 00401 ast_free(sql); 00402 ast_free(sql2); 00403 return -1; 00404 } else { 00405 totalrecords++; 00406 records++; 00407 } 00408 PQclear(result); 00409 ast_free(sql); 00410 ast_free(sql2); 00411 } 00412 ast_mutex_unlock(&pgsql_lock); 00413 return 0; 00414 }
| static int reload | ( | void | ) | [static] |
Definition at line 729 of file cdr_pgsql.c.
References config_module().
00730 { 00731 return config_module(1); 00732 }
| static int unload_module | ( | void | ) | [static] |
Definition at line 428 of file cdr_pgsql.c.
References ARRAY_LEN, ast_cdr_unregister(), ast_cli_unregister_multiple(), ast_free, and empty_columns().
00429 { 00430 ast_cdr_unregister(name); 00431 ast_cli_unregister_multiple(cdr_pgsql_status_cli, ARRAY_LEN(cdr_pgsql_status_cli)); 00432 00433 PQfinish(conn); 00434 00435 ast_free(pghostname); 00436 ast_free(pgdbname); 00437 ast_free(pgdbuser); 00438 ast_free(pgpassword); 00439 ast_free(pgdbport); 00440 ast_free(table); 00441 ast_free(encoding); 00442 ast_free(tz); 00443 00444 empty_columns(); 00445 00446 return 0; 00447 }
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 = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CDR_DRIVER, } [static] |
Definition at line 739 of file cdr_pgsql.c.
struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 739 of file cdr_pgsql.c.
struct ast_cli_entry cdr_pgsql_status_cli[] [static] |
Initial value:
{
AST_CLI_DEFINE(handle_cdr_pgsql_status, "Show connection status of the PostgreSQL CDR driver (cdr_pgsql)"),
}
Definition at line 65 of file cdr_pgsql.c.
const char config[] = "cdr_pgsql.conf" [static] |
Definition at line 56 of file cdr_pgsql.c.
PGconn* conn = NULL [static] |
Definition at line 71 of file cdr_pgsql.c.
time_t connect_time = 0 [static] |
Definition at line 60 of file cdr_pgsql.c.
Referenced by handle_cli_realtime_pgsql_status(), and pgsql_reconnect().
int connected = 0 [static] |
Definition at line 58 of file cdr_pgsql.c.
Referenced by aji_initialize(), handle_request_invite(), handle_request_update(), handle_response_invite(), launch_monitor_thread(), and sip_call().
char * encoding = NULL [static] |
int maxsize = 512 [static] |
Definition at line 59 of file cdr_pgsql.c.
int maxsize2 = 512 [static] |
Definition at line 59 of file cdr_pgsql.c.
const char name[] = "pgsql" [static] |
Definition at line 55 of file cdr_pgsql.c.
char * pgdbname = NULL [static] |
Definition at line 57 of file cdr_pgsql.c.
char * pgdbport = NULL [static] |
Definition at line 57 of file cdr_pgsql.c.
char * pgdbuser = NULL [static] |
Definition at line 57 of file cdr_pgsql.c.
char* pghostname = NULL [static] |
Definition at line 57 of file cdr_pgsql.c.
char * pgpassword = NULL [static] |
Definition at line 57 of file cdr_pgsql.c.
ast_mutex_t pgsql_lock = { PTHREAD_MUTEX_INITIALIZER , NULL, 1 } [static] |
Definition at line 69 of file cdr_pgsql.c.
Referenced by config_module(), config_pgsql(), destroy_pgsql(), parse_config(), pgsql_log(), realtime_multi_pgsql(), realtime_pgsql(), require_pgsql(), store_pgsql(), unload_module(), update2_pgsql(), and update_pgsql().
int records [static] |
Definition at line 62 of file cdr_pgsql.c.
char * table = NULL [static] |
Definition at line 57 of file cdr_pgsql.c.
int totalrecords = 0 [static] |
Definition at line 61 of file cdr_pgsql.c.
char * tz = NULL [static] |
Definition at line 57 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().
1.5.6