Asterisk - The Open Source Telephony Project GIT-master-67613d1
Data Structures | Macros | Functions | Variables
cdr_pgsql.c File Reference

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/cli.h"
#include "asterisk/module.h"
Include dependency graph for cdr_pgsql.c:

Go to the source code of this file.

Data Structures

struct  columns
 
struct  psql_columns
 

Macros

#define DATE_FORMAT   "'%Y-%m-%d %T'"
 
#define LENGTHEN_BUF(size, var_sql)
 
#define LENGTHEN_BUF1(size)    LENGTHEN_BUF(size, sql);
 
#define LENGTHEN_BUF2(size)    LENGTHEN_BUF(size, sql2);
 
#define PGSQL_MIN_VERSION_SCHEMA   70300
 

Functions

static void __reg_module (void)
 
static void __unreg_module (void)
 
struct ast_moduleAST_MODULE_SELF_SYM (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. More...
 
static int load_module (void)
 
static int pgsql_log (struct ast_cdr *cdr)
 
static void pgsql_reconnect (void)
 
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, .support_level = AST_MODULE_SUPPORT_EXTENDED, .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CDR_DRIVER, .requires = "cdr", }
 
static const struct ast_module_infoast_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
 
static int maxsize = 512
 
static int maxsize2 = 512
 
static const char name [] = "pgsql"
 
static char * pgappname
 
static char * pgdbname
 
static char * pgdbport
 
static char * pgdbuser
 
static char * pghostname
 
static char * pgpassword
 
static ast_mutex_t pgsql_lock = { PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP , NULL, {1, 0} }
 
static struct psql_columns psql_columns = { .first = NULL, .last = NULL, .lock = { PTHREAD_RWLOCK_INITIALIZER , NULL, {1, 0} } , }
 
static int records
 
static char * table
 
static int totalrecords = 0
 
static char * tz
 

Detailed Description

PostgreSQL CDR logger.

Author
Matthew D. Hardeman mhard.nosp@m.emn@.nosp@m.paper.nosp@m.soft.nosp@m..com PostgreSQL http://www.postgresql.org/

Definition in file cdr_pgsql.c.

Macro Definition Documentation

◆ DATE_FORMAT

#define DATE_FORMAT   "'%Y-%m-%d %T'"

Definition at line 56 of file cdr_pgsql.c.

◆ LENGTHEN_BUF

#define LENGTHEN_BUF (   size,
  var_sql 
)

Definition at line 100 of file cdr_pgsql.c.

◆ LENGTHEN_BUF1

#define LENGTHEN_BUF1 (   size)     LENGTHEN_BUF(size, sql);

Definition at line 115 of file cdr_pgsql.c.

◆ LENGTHEN_BUF2

#define LENGTHEN_BUF2 (   size)     LENGTHEN_BUF(size, sql2);

Definition at line 117 of file cdr_pgsql.c.

◆ PGSQL_MIN_VERSION_SCHEMA

#define PGSQL_MIN_VERSION_SCHEMA   70300

Definition at line 58 of file cdr_pgsql.c.

Function Documentation

◆ __reg_module()

static void __reg_module ( void  )
static

Definition at line 806 of file cdr_pgsql.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

Definition at line 806 of file cdr_pgsql.c.

◆ AST_MODULE_SELF_SYM()

struct ast_module * AST_MODULE_SELF_SYM ( void  )

Definition at line 806 of file cdr_pgsql.c.

◆ config_module()

static int config_module ( int  reload)
static

Definition at line 520 of file cdr_pgsql.c.

521{
522 char *pgerror;
523 struct columns *cur;
524 PGresult *result;
525 const char *tmp;
526 struct ast_config *cfg;
527 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
528
529 if ((cfg = ast_config_load(config, config_flags)) == NULL || cfg == CONFIG_STATUS_FILEINVALID) {
530 ast_log(LOG_WARNING, "Unable to load config for PostgreSQL CDR's: %s\n", config);
531 return -1;
532 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
533 return 0;
534 }
535
537
538 if (!ast_variable_browse(cfg, "global")) {
541 ast_log(LOG_NOTICE, "cdr_pgsql configuration contains no global section, skipping module %s.\n",
542 reload ? "reload" : "load");
543 return -1;
544 }
545
546 if (!(tmp = ast_variable_retrieve(cfg, "global", "hostname"))) {
547 ast_log(LOG_WARNING, "PostgreSQL server hostname not specified. Assuming unix socket connection\n");
548 tmp = ""; /* connect via UNIX-socket by default */
549 }
550
552 if (!(pghostname = ast_strdup(tmp))) {
555 return -1;
556 }
557
558 if (!(tmp = ast_variable_retrieve(cfg, "global", "dbname"))) {
559 ast_log(LOG_WARNING, "PostgreSQL database not specified. Assuming asterisk\n");
560 tmp = "asteriskcdrdb";
561 }
562
564 if (!(pgdbname = ast_strdup(tmp))) {
567 return -1;
568 }
569
570 if (!(tmp = ast_variable_retrieve(cfg, "global", "user"))) {
571 ast_log(LOG_WARNING, "PostgreSQL database user not specified. Assuming asterisk\n");
572 tmp = "asterisk";
573 }
574
576 if (!(pgdbuser = ast_strdup(tmp))) {
579 return -1;
580 }
581
582 if (!(tmp = ast_variable_retrieve(cfg, "global", "appname"))) {
583 tmp = "";
584 }
585
587 if (!(pgappname = ast_strdup(tmp))) {
590 return -1;
591 }
592
593
594 if (!(tmp = ast_variable_retrieve(cfg, "global", "password"))) {
595 ast_log(LOG_WARNING, "PostgreSQL database password not specified. Assuming blank\n");
596 tmp = "";
597 }
598
600 if (!(pgpassword = ast_strdup(tmp))) {
603 return -1;
604 }
605
606 if (!(tmp = ast_variable_retrieve(cfg, "global", "port"))) {
607 ast_log(LOG_WARNING, "PostgreSQL database port not specified. Using default 5432.\n");
608 tmp = "5432";
609 }
610
612 if (!(pgdbport = ast_strdup(tmp))) {
615 return -1;
616 }
617
618 if (!(tmp = ast_variable_retrieve(cfg, "global", "table"))) {
619 ast_log(LOG_WARNING, "CDR table not specified. Assuming cdr\n");
620 tmp = "cdr";
621 }
622
624 if (!(table = ast_strdup(tmp))) {
627 return -1;
628 }
629
630 if (!(tmp = ast_variable_retrieve(cfg, "global", "encoding"))) {
631 ast_log(LOG_WARNING, "Encoding not specified. Assuming LATIN9\n");
632 tmp = "LATIN9";
633 }
634
636 if (!(encoding = ast_strdup(tmp))) {
639 return -1;
640 }
641
642 if (!(tmp = ast_variable_retrieve(cfg, "global", "timezone"))) {
643 tmp = "";
644 }
645
646 ast_free(tz);
647 tz = NULL;
648
649 if (!ast_strlen_zero(tmp) && !(tz = ast_strdup(tmp))) {
652 return -1;
653 }
654
655 if (DEBUG_ATLEAST(1)) {
657 ast_log(LOG_DEBUG, "using default unix socket\n");
658 } else {
659 ast_log(LOG_DEBUG, "got hostname of %s\n", pghostname);
660 }
661 ast_log(LOG_DEBUG, "got port of %s\n", pgdbport);
662 ast_log(LOG_DEBUG, "got user of %s\n", pgdbuser);
663 ast_log(LOG_DEBUG, "got dbname of %s\n", pgdbname);
664 ast_log(LOG_DEBUG, "got password of %s\n", pgpassword);
665 ast_log(LOG_DEBUG, "got application name of %s\n", pgappname);
666 ast_log(LOG_DEBUG, "got sql table name of %s\n", table);
667 ast_log(LOG_DEBUG, "got encoding of %s\n", encoding);
668 ast_log(LOG_DEBUG, "got timezone of %s\n", tz);
669 }
670
672
673 if (PQstatus(conn) != CONNECTION_BAD) {
674 char sqlcmd[768];
675 char *fname, *ftype, *flen, *fnotnull, *fdef;
676 int i, rows, version;
677 ast_debug(1, "Successfully connected to PostgreSQL database.\n");
678 connected = 1;
679 connect_time = time(NULL);
680 records = 0;
681 if (PQsetClientEncoding(conn, encoding)) {
682#ifdef HAVE_PGSQL_pg_encoding_to_char
683 ast_log(LOG_WARNING, "Failed to set encoding to '%s'. Encoding set to default '%s'\n", encoding, pg_encoding_to_char(PQclientEncoding(conn)));
684#else
685 ast_log(LOG_WARNING, "Failed to set encoding to '%s'. Encoding set to default.\n", encoding);
686#endif
687 }
688 version = PQserverVersion(conn);
689
691 char *schemaname, *tablename, *tmp_schemaname, *tmp_tablename;
692 if (strchr(table, '.')) {
693 tmp_schemaname = ast_strdupa(table);
694 tmp_tablename = strchr(tmp_schemaname, '.');
695 *tmp_tablename++ = '\0';
696 } else {
697 tmp_schemaname = "";
698 tmp_tablename = table;
699 }
700 tablename = ast_alloca(strlen(tmp_tablename) * 2 + 1);
701 PQescapeStringConn(conn, tablename, tmp_tablename, strlen(tmp_tablename), NULL);
702
703 schemaname = ast_alloca(strlen(tmp_schemaname) * 2 + 1);
704 PQescapeStringConn(conn, schemaname, tmp_schemaname, strlen(tmp_schemaname), NULL);
705
706 snprintf(sqlcmd, sizeof(sqlcmd), "SELECT a.attname, t.typname, a.attlen, a.attnotnull, pg_catalog.pg_get_expr(d.adbin, d.adrelid) 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",
707 tablename,
708 ast_strlen_zero(schemaname) ? "" : "'", ast_strlen_zero(schemaname) ? "current_schema()" : schemaname, ast_strlen_zero(schemaname) ? "" : "'");
709 } else {
710 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);
711 }
712 /* Query the columns */
713 result = PQexec(conn, sqlcmd);
714 if (PQresultStatus(result) != PGRES_TUPLES_OK) {
715 pgerror = PQresultErrorMessage(result);
716 ast_log(LOG_ERROR, "Failed to query database columns: %s\n", pgerror);
717 PQclear(result);
721 }
722
723 rows = PQntuples(result);
724 if (rows == 0) {
725 ast_log(LOG_ERROR, "cdr_pgsql: Failed to query database columns. No columns found, does the table exist?\n");
726 PQclear(result);
730 }
731
732 /* Clear out the columns list. */
734
735 for (i = 0; i < rows; i++) {
736 fname = PQgetvalue(result, i, 0);
737 ftype = PQgetvalue(result, i, 1);
738 flen = PQgetvalue(result, i, 2);
739 fnotnull = PQgetvalue(result, i, 3);
740 fdef = PQgetvalue(result, i, 4);
741 if (atoi(flen) == -1) {
742 /* For varchar columns, the maximum length is encoded in a different field */
743 flen = PQgetvalue(result, i, 5);
744 }
745
746 cur = ast_calloc(1, sizeof(*cur) + strlen(fname) + strlen(ftype) + 2);
747 if (cur) {
748 sscanf(flen, "%30d", &cur->len);
749 cur->name = (char *)cur + sizeof(*cur);
750 cur->type = (char *)cur + sizeof(*cur) + strlen(fname) + 1;
751 strcpy(cur->name, fname);
752 strcpy(cur->type, ftype);
753 if (*fnotnull == 't') {
754 cur->notnull = 1;
755 } else {
756 cur->notnull = 0;
757 }
758 if (!ast_strlen_zero(fdef)) {
759 cur->hasdefault = 1;
760 } else {
761 cur->hasdefault = 0;
762 }
766 }
767 }
768 PQclear(result);
769 } else {
770 pgerror = PQerrorMessage(conn);
771 ast_log(LOG_ERROR, "Unable to connect to database server %s. CALLS WILL NOT BE LOGGED!!\n", pghostname);
772 ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
773 connected = 0;
774 PQfinish(conn);
775 conn = NULL;
776 }
777
779
781 return 0;
782}
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:288
#define ast_free(a)
Definition: astmm.h:180
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define ast_log
Definition: astobj2.c:42
static int tmp()
Definition: bt_open.c:389
static void empty_columns(void)
Definition: cdr_pgsql.c:482
static char * pgdbuser
Definition: cdr_pgsql.c:65
static void pgsql_reconnect(void)
Definition: cdr_pgsql.c:170
static char * pgappname
Definition: cdr_pgsql.c:67
static int connected
Definition: cdr_pgsql.c:73
#define PGSQL_MIN_VERSION_SCHEMA
Definition: cdr_pgsql.c:58
static const char config[]
Definition: cdr_pgsql.c:61
static ast_mutex_t pgsql_lock
Definition: cdr_pgsql.c:85
static char * pgdbport
Definition: cdr_pgsql.c:68
static char * pgdbname
Definition: cdr_pgsql.c:64
static int records
Definition: cdr_pgsql.c:78
static char * table
Definition: cdr_pgsql.c:69
static char * encoding
Definition: cdr_pgsql.c:70
static char * pghostname
Definition: cdr_pgsql.c:63
static time_t connect_time
Definition: cdr_pgsql.c:76
static char * pgpassword
Definition: cdr_pgsql.c:66
static int unload_module(void)
Definition: cdr_pgsql.c:493
static int reload(void)
Definition: cdr_pgsql.c:794
static PGconn * conn
Definition: cdr_pgsql.c:87
static char * tz
Definition: cdr_pgsql.c:71
static PGresult * result
Definition: cel_pgsql.c:84
static char version[AST_MAX_EXTENSION]
Definition: chan_ooh323.c:391
#define ast_config_load(filename, flags)
Load a config file.
#define CONFIG_STATUS_FILEUNCHANGED
#define CONFIG_STATUS_FILEINVALID
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
const char * ast_variable_retrieve(struct ast_config *config, const char *category, const char *variable)
Definition: main/config.c:783
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category_name)
Definition: extconf.c:1215
@ CONFIG_FLAG_FILEUNCHANGED
#define DEBUG_ATLEAST(level)
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_DEBUG
#define LOG_ERROR
#define LOG_NOTICE
#define LOG_WARNING
#define AST_RWLIST_WRLOCK(head)
Write locks a list.
Definition: linkedlists.h:52
#define AST_RWLIST_UNLOCK(head)
Attempts to unlock a read/write based list.
Definition: linkedlists.h:151
#define AST_RWLIST_INSERT_TAIL
Definition: linkedlists.h:741
#define ast_mutex_unlock(a)
Definition: lock.h:190
#define ast_mutex_lock(a)
Definition: lock.h:189
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
#define NULL
Definition: resample.c:96
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
Structure used to handle boolean flags.
Definition: utils.h:199
unsigned int hasdefault
Definition: cdr_pgsql.c:94
unsigned int notnull
Definition: cdr_pgsql.c:93

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(), config, CONFIG_FLAG_FILEUNCHANGED, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEUNCHANGED, conn, connect_time, connected, DEBUG_ATLEAST, empty_columns(), encoding, columns::hasdefault, columns::len, LOG_DEBUG, LOG_ERROR, LOG_NOTICE, LOG_WARNING, columns::name, columns::notnull, NULL, pgappname, pgdbname, pgdbport, pgdbuser, pghostname, pgpassword, pgsql_lock, PGSQL_MIN_VERSION_SCHEMA, pgsql_reconnect(), records, reload(), result, table, tmp(), columns::type, tz, unload_module(), and version.

Referenced by load_module(), and reload().

◆ empty_columns()

static void empty_columns ( void  )
static

Definition at line 482 of file cdr_pgsql.c.

483{
484 struct columns *current;
488 }
490
491}
#define AST_RWLIST_REMOVE_HEAD
Definition: linkedlists.h:844
size_t current
Definition: main/cli.c:113
struct columns::@4 list

References ast_free, AST_RWLIST_REMOVE_HEAD, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, current, and columns::list.

Referenced by config_module(), and unload_module().

◆ handle_cdr_pgsql_status()

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 121 of file cdr_pgsql.c.

122{
123 switch (cmd) {
124 case CLI_INIT:
125 e->command = "cdr show pgsql status";
126 e->usage =
127 "Usage: cdr show pgsql status\n"
128 " Shows current connection status for cdr_pgsql\n";
129 return NULL;
130 case CLI_GENERATE:
131 return NULL;
132 }
133
134 if (a->argc != e->args)
135 return CLI_SHOWUSAGE;
136
137 if (connected) {
138 char status[256];
139 char status2[100] = "";
140 char buf[362]; /* 256+100+" for "+NULL */
141 int ctime = time(NULL) - connect_time;
142
143 if (pgdbport) {
144 snprintf(status, 255, "Connected to %s@%s, port %s", pgdbname, pghostname, pgdbport);
145 } else {
146 snprintf(status, 255, "Connected to %s@%s", pgdbname, pghostname);
147 }
148
149 if (pgdbuser && *pgdbuser) {
150 snprintf(status2, 99, " with username %s", pgdbuser);
151 }
152 if (table && *table) {
153 snprintf(status2, 99, " using table %s", table);
154 }
155
156 snprintf(buf, sizeof(buf), "%s%s for ", status, status2);
158
159 if (records == totalrecords) {
160 ast_cli(a->fd, " Wrote %d records since last restart.\n", totalrecords);
161 } else {
162 ast_cli(a->fd, " Wrote %d records since last restart and %d records since last reconnect.\n", totalrecords, records);
163 }
164 } else {
165 ast_cli(a->fd, "Not currently connected to a PgSQL server.\n");
166 }
167 return CLI_SUCCESS;
168}
jack_status_t status
Definition: app_jack.c:146
static int totalrecords
Definition: cdr_pgsql.c:77
#define CLI_SHOWUSAGE
Definition: cli.h:45
void ast_cli_print_timestr_fromseconds(int fd, int seconds, const char *prefix)
Print on cli a duration in seconds in format s year(s), s week(s), s day(s), s hour(s),...
Definition: main/cli.c:3053
#define CLI_SUCCESS
Definition: cli.h:44
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
@ CLI_INIT
Definition: cli.h:152
@ CLI_GENERATE
Definition: cli.h:153
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
int args
This gets set in ast_cli_register()
Definition: cli.h:185
char * command
Definition: cli.h:186
const char * usage
Definition: cli.h:177
static struct test_val a

References a, ast_cli_entry::args, ast_cli(), ast_cli_print_timestr_fromseconds(), buf, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, connect_time, connected, NULL, pgdbname, pgdbport, pgdbuser, pghostname, records, status, table, totalrecords, and ast_cli_entry::usage.

◆ load_module()

static int load_module ( void  )
static

Definition at line 784 of file cdr_pgsql.c.

785{
787 if (config_module(0)) {
789 }
792}
int ast_cdr_register(const char *name, const char *desc, ast_cdrbe be)
Register a CDR handling engine.
Definition: cdr.c:3005
static int config_module(int reload)
Definition: cdr_pgsql.c:520
static const char name[]
Definition: cdr_pgsql.c:60
static int pgsql_log(struct ast_cdr *cdr)
Definition: cdr_pgsql.c:210
static struct ast_cli_entry cdr_pgsql_status_cli[]
Definition: cdr_pgsql.c:81
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
descriptor for a cli entry.
Definition: cli.h:171
const char * description
Definition: module.h:352

References ast_cdr_register(), ast_cli_register_multiple, AST_MODULE_LOAD_DECLINE, cdr_pgsql_status_cli, config_module(), ast_module_info::description, name, and pgsql_log().

◆ pgsql_log()

static int pgsql_log ( struct ast_cdr cdr)
static

Definition at line 210 of file cdr_pgsql.c.

211{
212 struct ast_tm tm;
213 char *pgerror;
214 PGresult *result;
215 int res = -1;
216
218
219 if ((!connected) && pghostname && pgdbuser && pgpassword && pgdbname) {
221
222 if (PQstatus(conn) != CONNECTION_BAD) {
223 connected = 1;
224 connect_time = time(NULL);
225 records = 0;
226 if (PQsetClientEncoding(conn, encoding)) {
227#ifdef HAVE_PGSQL_pg_encoding_to_char
228 ast_log(LOG_WARNING, "Failed to set encoding to '%s'. Encoding set to default '%s'\n", encoding, pg_encoding_to_char(PQclientEncoding(conn)));
229#else
230 ast_log(LOG_WARNING, "Failed to set encoding to '%s'. Encoding set to default.\n", encoding);
231#endif
232 }
233 } else {
234 pgerror = PQerrorMessage(conn);
235 ast_log(LOG_ERROR, "Unable to connect to database server %s. Calls will not be logged!\n", pghostname);
236 ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
237 PQfinish(conn);
238 conn = NULL;
239 }
240 }
241
242 if (connected) {
243 struct columns *cur;
244 struct ast_str *sql = ast_str_create(maxsize), *sql2 = ast_str_create(maxsize2);
245 char buf[257];
246 char *escapebuf = NULL, *value;
247 char *separator = "";
248 size_t bufsize = 513;
249
250 escapebuf = ast_malloc(bufsize);
251 if (!escapebuf || !sql || !sql2) {
252 goto ast_log_cleanup;
253 }
254
255 ast_str_set(&sql, 0, "INSERT INTO %s (", table);
256 ast_str_set(&sql2, 0, " VALUES (");
257
259 AST_RWLIST_TRAVERSE(&psql_columns, cur, list) {
260 /* For fields not set, simply skip them */
261 ast_cdr_format_var(cdr, cur->name, &value, buf, sizeof(buf), 0);
262 if (strcmp(cur->name, "calldate") == 0 && !value) {
263 ast_cdr_format_var(cdr, "start", &value, buf, sizeof(buf), 0);
264 }
265 if (!value) {
266 if (cur->notnull && !cur->hasdefault) {
267 /* Field is NOT NULL (but no default), must include it anyway */
268 LENGTHEN_BUF1(strlen(cur->name) + 2);
269 ast_str_append(&sql, 0, "%s\"%s\"", separator, cur->name);
270 LENGTHEN_BUF2(3);
271 ast_str_append(&sql2, 0, "%s''", separator);
272 separator = ", ";
273 }
274 continue;
275 }
276
277 LENGTHEN_BUF1(strlen(cur->name) + 2);
278 ast_str_append(&sql, 0, "%s\"%s\"", separator, cur->name);
279
280 if (strcmp(cur->name, "start") == 0 || strcmp(cur->name, "calldate") == 0) {
281 if (strncmp(cur->type, "int", 3) == 0) {
282 LENGTHEN_BUF2(13);
283 ast_str_append(&sql2, 0, "%s%ld", separator, (long) cdr->start.tv_sec);
284 } else if (strncmp(cur->type, "float", 5) == 0) {
285 LENGTHEN_BUF2(31);
286 ast_str_append(&sql2, 0, "%s%f", separator, (double)cdr->start.tv_sec + (double)cdr->start.tv_usec / 1000000.0);
287 } else {
288 /* char, hopefully */
289 LENGTHEN_BUF2(31);
290 ast_localtime(&cdr->start, &tm, tz);
291 ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm);
292 ast_str_append(&sql2, 0, "%s%s", separator, buf);
293 }
294 } else if (strcmp(cur->name, "answer") == 0) {
295 if (strncmp(cur->type, "int", 3) == 0) {
296 LENGTHEN_BUF2(13);
297 ast_str_append(&sql2, 0, "%s%ld", separator, (long) cdr->answer.tv_sec);
298 } else if (strncmp(cur->type, "float", 5) == 0) {
299 LENGTHEN_BUF2(31);
300 ast_str_append(&sql2, 0, "%s%f", separator, (double)cdr->answer.tv_sec + (double)cdr->answer.tv_usec / 1000000.0);
301 } else {
302 /* char, hopefully */
303 LENGTHEN_BUF2(31);
304 ast_localtime(&cdr->answer, &tm, tz);
305 ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm);
306 ast_str_append(&sql2, 0, "%s%s", separator, buf);
307 }
308 } else if (strcmp(cur->name, "end") == 0) {
309 if (strncmp(cur->type, "int", 3) == 0) {
310 LENGTHEN_BUF2(13);
311 ast_str_append(&sql2, 0, "%s%ld", separator, (long) cdr->end.tv_sec);
312 } else if (strncmp(cur->type, "float", 5) == 0) {
313 LENGTHEN_BUF2(31);
314 ast_str_append(&sql2, 0, "%s%f", separator, (double)cdr->end.tv_sec + (double)cdr->end.tv_usec / 1000000.0);
315 } else {
316 /* char, hopefully */
317 LENGTHEN_BUF2(31);
318 ast_localtime(&cdr->end, &tm, tz);
319 ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm);
320 ast_str_append(&sql2, 0, "%s%s", separator, buf);
321 }
322 } else if (strcmp(cur->name, "duration") == 0 || strcmp(cur->name, "billsec") == 0) {
323 if (cur->type[0] == 'i') {
324 /* Get integer, no need to escape anything */
325 ast_cdr_format_var(cdr, cur->name, &value, buf, sizeof(buf), 0);
326 LENGTHEN_BUF2(13);
327 ast_str_append(&sql2, 0, "%s%s", separator, value);
328 } else if (strncmp(cur->type, "float", 5) == 0) {
329 struct timeval *when = cur->name[0] == 'd' ? &cdr->start : ast_tvzero(cdr->answer) ? &cdr->end : &cdr->answer;
330 LENGTHEN_BUF2(31);
331 ast_str_append(&sql2, 0, "%s%f", separator, (double) (ast_tvdiff_us(cdr->end, *when) / 1000000.0));
332 } else {
333 /* Char field, probably */
334 struct timeval *when = cur->name[0] == 'd' ? &cdr->start : ast_tvzero(cdr->answer) ? &cdr->end : &cdr->answer;
335 LENGTHEN_BUF2(31);
336 ast_str_append(&sql2, 0, "%s'%f'", separator, (double) (ast_tvdiff_us(cdr->end, *when) / 1000000.0));
337 }
338 } else if (strcmp(cur->name, "disposition") == 0 || strcmp(cur->name, "amaflags") == 0) {
339 if (strncmp(cur->type, "int", 3) == 0) {
340 /* Integer, no need to escape anything */
341 ast_cdr_format_var(cdr, cur->name, &value, buf, sizeof(buf), 1);
342 LENGTHEN_BUF2(13);
343 ast_str_append(&sql2, 0, "%s%s", separator, value);
344 } else {
345 /* Although this is a char field, there are no special characters in the values for these fields */
346 ast_cdr_format_var(cdr, cur->name, &value, buf, sizeof(buf), 0);
347 LENGTHEN_BUF2(31);
348 ast_str_append(&sql2, 0, "%s'%s'", separator, value);
349 }
350 } else {
351 /* Arbitrary field, could be anything */
352 ast_cdr_format_var(cdr, cur->name, &value, buf, sizeof(buf), 0);
353 if (strncmp(cur->type, "int", 3) == 0) {
354 long long whatever;
355 if (value && sscanf(value, "%30lld", &whatever) == 1) {
356 LENGTHEN_BUF2(26);
357 ast_str_append(&sql2, 0, "%s%lld", separator, whatever);
358 } else {
359 LENGTHEN_BUF2(2);
360 ast_str_append(&sql2, 0, "%s0", separator);
361 }
362 } else if (strncmp(cur->type, "float", 5) == 0) {
363 long double whatever;
364 if (value && sscanf(value, "%30Lf", &whatever) == 1) {
365 LENGTHEN_BUF2(51);
366 ast_str_append(&sql2, 0, "%s%30Lf", separator, whatever);
367 } else {
368 LENGTHEN_BUF2(2);
369 ast_str_append(&sql2, 0, "%s0", separator);
370 }
371 /* XXX Might want to handle dates, times, and other misc fields here XXX */
372 } else {
373 if (value) {
374 size_t required_size = strlen(value) * 2 + 1;
375
376 /* If our argument size exceeds our buffer, grow it,
377 * as PQescapeStringConn() expects the buffer to be
378 * adequitely sized and does *NOT* do size checking.
379 */
380 if (required_size > bufsize) {
381 char *tmpbuf = ast_realloc(escapebuf, required_size);
382
383 if (!tmpbuf) {
385 goto ast_log_cleanup;
386 }
387
388 escapebuf = tmpbuf;
389 bufsize = required_size;
390 }
391 PQescapeStringConn(conn, escapebuf, value, strlen(value), NULL);
392 } else {
393 escapebuf[0] = '\0';
394 }
395 LENGTHEN_BUF2(strlen(escapebuf) + 3);
396 ast_str_append(&sql2, 0, "%s'%s'", separator, escapebuf);
397 }
398 }
399 separator = ", ";
400 }
401
402 LENGTHEN_BUF1(ast_str_strlen(sql2) + 2);
404 ast_str_append(&sql, 0, ")%s)", ast_str_buffer(sql2));
405
406 ast_debug(3, "Inserting a CDR record: [%s]\n", ast_str_buffer(sql));
407
408 /* Test to be sure we're still connected... */
409 /* If we're connected, and connection is working, good. */
410 /* Otherwise, attempt reconnect. If it fails... sorry... */
411 if (PQstatus(conn) == CONNECTION_OK) {
412 connected = 1;
413 } else {
414 ast_log(LOG_ERROR, "Connection was lost... attempting to reconnect.\n");
415 PQreset(conn);
416 if (PQstatus(conn) == CONNECTION_OK) {
417 ast_log(LOG_ERROR, "Connection reestablished.\n");
418 connected = 1;
419 connect_time = time(NULL);
420 records = 0;
421 } else {
422 pgerror = PQerrorMessage(conn);
423 ast_log(LOG_ERROR, "Unable to reconnect to database server %s. Calls will not be logged!\n", pghostname);
424 ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
425 PQfinish(conn);
426 conn = NULL;
427 connected = 0;
428 goto ast_log_cleanup;
429 }
430 }
431 result = PQexec(conn, ast_str_buffer(sql));
432 if (PQresultStatus(result) != PGRES_COMMAND_OK) {
433 pgerror = PQresultErrorMessage(result);
434 ast_log(LOG_ERROR, "Failed to insert call detail record into database!\n");
435 ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
436 ast_log(LOG_ERROR, "Connection may have been lost... attempting to reconnect.\n");
437 PQreset(conn);
438 if (PQstatus(conn) == CONNECTION_OK) {
439 ast_log(LOG_ERROR, "Connection reestablished.\n");
440 connected = 1;
441 connect_time = time(NULL);
442 records = 0;
443 PQclear(result);
444 result = PQexec(conn, ast_str_buffer(sql));
445 if (PQresultStatus(result) != PGRES_COMMAND_OK) {
446 pgerror = PQresultErrorMessage(result);
447 ast_log(LOG_ERROR, "HARD ERROR! Attempted reconnection failed. DROPPING CALL RECORD!\n");
448 ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
449 } else {
450 /* Second try worked out ok */
451 totalrecords++;
452 records++;
453 res = 0;
454 }
455 }
456 } else {
457 totalrecords++;
458 records++;
459 res = 0;
460 }
461 PQclear(result);
462
463 /* Next time, just allocate buffers that are that big to start with. */
464 if (ast_str_strlen(sql) > maxsize) {
465 maxsize = ast_str_strlen(sql);
466 }
467 if (ast_str_strlen(sql2) > maxsize2) {
468 maxsize2 = ast_str_strlen(sql2);
469 }
470
471ast_log_cleanup:
472 ast_free(escapebuf);
473 ast_free(sql);
474 ast_free(sql2);
475 }
476
478 return res;
479}
#define ast_realloc(p, len)
A wrapper for realloc()
Definition: astmm.h:226
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:191
void ast_cdr_format_var(struct ast_cdr *cdr, const char *name, char **ret, char *workspace, int workspacelen, int raw)
Format a CDR variable from an already posted CDR.
Definition: cdr.c:3112
#define LENGTHEN_BUF2(size)
Definition: cdr_pgsql.c:117
static int maxsize2
Definition: cdr_pgsql.c:75
static int maxsize
Definition: cdr_pgsql.c:75
#define DATE_FORMAT
Definition: cdr_pgsql.c:56
#define LENGTHEN_BUF1(size)
Definition: cdr_pgsql.c:115
#define AST_RWLIST_RDLOCK(head)
Read locks a list.
Definition: linkedlists.h:78
#define AST_RWLIST_TRAVERSE
Definition: linkedlists.h:494
struct ast_tm * ast_localtime(const struct timeval *timep, struct ast_tm *p_tm, const char *zone)
Timezone-independent version of localtime_r(3).
Definition: localtime.c:1739
int ast_strftime(char *buf, size_t len, const char *format, const struct ast_tm *tm)
Special version of strftime(3) that handles fractions of a second. Takes the same arguments as strfti...
Definition: localtime.c:2524
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1139
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1113
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:730
struct timeval answer
Definition: cdr.h:299
struct timeval start
Definition: cdr.h:297
struct timeval end
Definition: cdr.h:301
Support for dynamic strings.
Definition: strings.h:623
int value
Definition: syslog.c:37
int64_t ast_tvdiff_us(struct timeval end, struct timeval start)
Computes the difference (in microseconds) between two struct timeval instances.
Definition: time.h:87
int ast_tvzero(const struct timeval t)
Returns true if the argument is 0,0.
Definition: time.h:117

References ast_cdr::answer, ast_cdr_format_var(), ast_debug, ast_free, ast_localtime(), ast_log, ast_malloc, ast_mutex_lock, ast_mutex_unlock, ast_realloc, 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(), buf, conn, connect_time, connected, DATE_FORMAT, encoding, ast_cdr::end, columns::hasdefault, LENGTHEN_BUF1, LENGTHEN_BUF2, LOG_ERROR, LOG_WARNING, maxsize, maxsize2, columns::name, columns::notnull, NULL, pgdbname, pgdbuser, pghostname, pgpassword, pgsql_lock, pgsql_reconnect(), records, result, ast_cdr::start, table, totalrecords, columns::type, tz, and value.

Referenced by load_module().

◆ pgsql_reconnect()

static void pgsql_reconnect ( void  )
static

Definition at line 170 of file cdr_pgsql.c.

171{
172 struct ast_str *conn_info = ast_str_create(128);
173 if (!conn_info) {
174 ast_log(LOG_ERROR, "Failed to allocate memory for connection string.\n");
175 return;
176 }
177
178 if (conn) {
179 PQfinish(conn);
180 conn = NULL;
181 }
182
184 ast_str_append(&conn_info, 0, "host=%s ", pghostname);
185 }
187 ast_str_append(&conn_info, 0, "port=%s ", pgdbport);
188 }
190 ast_str_append(&conn_info, 0, "dbname=%s ", pgdbname);
191 }
193 ast_str_append(&conn_info, 0, "user=%s ", pgdbuser);
194 }
196 ast_str_append(&conn_info, 0, "application_name=%s ", pgappname);
197 }
199 ast_str_append(&conn_info, 0, "password=%s", pgpassword);
200 }
201 if (ast_str_strlen(conn_info) == 0) {
202 ast_log(LOG_ERROR, "Connection string is blank.\n");
203 return;
204 }
205
206 conn = PQconnectdb(ast_str_buffer(conn_info));
207 ast_free(conn_info);
208}

References ast_free, ast_log, ast_str_append(), ast_str_buffer(), ast_str_create, ast_str_strlen(), ast_strlen_zero(), conn, LOG_ERROR, NULL, pgappname, pgdbname, pgdbport, pgdbuser, pghostname, and pgpassword.

Referenced by config_module(), and pgsql_log().

◆ reload()

static int reload ( void  )
static

Definition at line 794 of file cdr_pgsql.c.

795{
796 return config_module(1);
797}

References config_module().

Referenced by config_module().

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 493 of file cdr_pgsql.c.

494{
496 return -1;
497 }
498
500
501 if (conn) {
502 PQfinish(conn);
503 conn = NULL;
504 }
513 ast_free(tz);
514
516
517 return 0;
518}
int ast_cdr_unregister(const char *name)
Unregister a CDR handling engine.
Definition: cdr.c:3050
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
#define ARRAY_LEN(a)
Definition: utils.h:666

References ARRAY_LEN, ast_cdr_unregister(), ast_cli_unregister_multiple(), ast_free, cdr_pgsql_status_cli, conn, empty_columns(), encoding, name, NULL, pgappname, pgdbname, pgdbport, pgdbuser, pghostname, pgpassword, table, and tz.

Referenced by config_module().

Variable Documentation

◆ __mod_info

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, .support_level = AST_MODULE_SUPPORT_EXTENDED, .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CDR_DRIVER, .requires = "cdr", }
static

Definition at line 806 of file cdr_pgsql.c.

◆ ast_module_info

const struct ast_module_info* ast_module_info = &__mod_info
static

Definition at line 806 of file cdr_pgsql.c.

◆ cdr_pgsql_status_cli

struct ast_cli_entry cdr_pgsql_status_cli[]
static
Initial value:
= {
{ .handler = handle_cdr_pgsql_status , .summary = "Show connection status of the PostgreSQL CDR driver (cdr_pgsql)" ,},
}
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.
Definition: cdr_pgsql.c:121

Definition at line 81 of file cdr_pgsql.c.

Referenced by load_module(), and unload_module().

◆ config

const char config[] = "cdr_pgsql.conf"
static

Definition at line 61 of file cdr_pgsql.c.

Referenced by config_module().

◆ conn

PGconn* conn = NULL
static

◆ connect_time

time_t connect_time = 0
static

Definition at line 76 of file cdr_pgsql.c.

Referenced by config_module(), handle_cdr_pgsql_status(), and pgsql_log().

◆ connected

int connected = 0
static

Definition at line 73 of file cdr_pgsql.c.

Referenced by config_module(), handle_cdr_pgsql_status(), and pgsql_log().

◆ encoding

char* encoding
static

Definition at line 70 of file cdr_pgsql.c.

Referenced by check_header(), config_module(), custom_prepare(), pgsql_log(), and unload_module().

◆ maxsize

int maxsize = 512
static

Definition at line 75 of file cdr_pgsql.c.

Referenced by pgsql_log().

◆ maxsize2

int maxsize2 = 512
static

Definition at line 75 of file cdr_pgsql.c.

Referenced by pgsql_log().

◆ name

const char name[] = "pgsql"
static

Definition at line 60 of file cdr_pgsql.c.

Referenced by load_module(), and unload_module().

◆ pgappname

char* pgappname
static

Definition at line 67 of file cdr_pgsql.c.

Referenced by config_module(), pgsql_reconnect(), and unload_module().

◆ pgdbname

char* pgdbname
static

◆ pgdbport

char* pgdbport
static

Definition at line 68 of file cdr_pgsql.c.

Referenced by config_module(), handle_cdr_pgsql_status(), pgsql_reconnect(), and unload_module().

◆ pgdbuser

char* pgdbuser
static

◆ pghostname

char* pghostname
static

◆ pgpassword

char* pgpassword
static

Definition at line 66 of file cdr_pgsql.c.

Referenced by config_module(), pgsql_log(), pgsql_reconnect(), and unload_module().

◆ pgsql_lock

ast_mutex_t pgsql_lock = { PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP , NULL, {1, 0} }
static

Definition at line 85 of file cdr_pgsql.c.

Referenced by config_module(), and pgsql_log().

◆ psql_columns

struct psql_columns psql_columns = { .first = NULL, .last = NULL, .lock = { PTHREAD_RWLOCK_INITIALIZER , NULL, {1, 0} } , }
static

◆ records

int records
static

◆ table

char* table
static

Definition at line 69 of file cdr_pgsql.c.

Referenced by config_module(), handle_cdr_pgsql_status(), pgsql_log(), and unload_module().

◆ totalrecords

int totalrecords = 0
static

Definition at line 77 of file cdr_pgsql.c.

Referenced by handle_cdr_pgsql_status(), and pgsql_log().

◆ tz

char* tz
static