Asterisk - The Open Source Telephony Project GIT-master-a63eec2
Loading...
Searching...
No Matches
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.
 
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 = ASTERISK_GPL_KEY , .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 = AST_MUTEX_INIT_VALUE
 
static struct psql_columns psql_columns = AST_RWLIST_HEAD_INIT_VALUE
 
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.

101 { \
102 /* Lengthen buffer, if necessary */ \
103 if (ast_str_strlen(var_sql) + size + 1 > ast_str_size(var_sql)) { \
104 if (ast_str_make_space(&var_sql, ((ast_str_size(var_sql) + size + 3) / 512 + 1) * 512) != 0) { \
105 ast_log(LOG_ERROR, "Unable to allocate sufficient memory. Insert CDR '%s:%s' failed.\n", pghostname, table); \
106 ast_free(sql); \
107 ast_free(sql2); \
108 AST_RWLIST_UNLOCK(&psql_columns); \
109 ast_mutex_unlock(&pgsql_lock); \
110 return -1; \
111 } \
112 } \
113 } while (0)
static ast_mutex_t pgsql_lock
Definition cdr_pgsql.c:85
static char * table
Definition cdr_pgsql.c:69
static char * pghostname
Definition cdr_pgsql.c:63
#define LOG_ERROR
size_t attribute_pure ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition strings.h:730
#define ast_str_make_space(buf, new_len)
Definition strings.h:828
size_t attribute_pure ast_str_size(const struct ast_str *buf)
Returns the current maximum length (without reallocation) of the current buffer.
Definition strings.h:742

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

◆ __unreg_module()

static void __unreg_module ( void  )
static

Definition at line 817 of file cdr_pgsql.c.

◆ AST_MODULE_SELF_SYM()

struct ast_module * AST_MODULE_SELF_SYM ( void  )

Definition at line 817 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 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 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 * encoding
Definition cdr_pgsql.c:70
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:805
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]
#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:1287
@ CONFIG_FLAG_FILEUNCHANGED
const char * ast_variable_retrieve(struct ast_config *config, const char *category, const char *variable)
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category_name)
Definition extconf.c:1213
#define DEBUG_ATLEAST(level)
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_DEBUG
#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.
#define AST_RWLIST_INSERT_TAIL
#define ast_mutex_unlock(a)
Definition lock.h:197
#define ast_mutex_lock(a)
Definition lock.h:196
@ 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:217
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, 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}
size_t current
#define AST_RWLIST_REMOVE_HEAD
struct columns::@5 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:149
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:3032
#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{
786 int res;
787
788 if (config_module(0)) {
794 } else {
796 }
797
798 if (res != AST_MODULE_LOAD_SUCCESS) {
800 }
801
802 return res;
803}
int ast_cdr_register(const char *name, const char *desc, ast_cdrbe be)
Register a CDR handling engine.
Definition cdr.c:3076
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
@ AST_MODULE_LOAD_SUCCESS
Definition module.h:70
const char * description
Definition module.h:366
#define ARRAY_LEN(a)
Definition utils.h:703

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

◆ 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 * adequately 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:3183
#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
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
#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
char *attribute_pure ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition strings.h:761
struct timeval answer
Definition cdr.h:301
struct timeval start
Definition cdr.h:299
struct timeval end
Definition cdr.h:303
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 _pgsql_exec(), config_module(), destroy_pgsql(), parse_config(), pgsql_log(), realtime_multi_pgsql(), realtime_pgsql(), store_pgsql(), update2_pgsql(), and update_pgsql().

◆ reload()

static int reload ( void  )
static

Definition at line 805 of file cdr_pgsql.c.

806{
807 return config_module(1);
808}

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}
void ast_cli_unregister_multiple(void)
Definition ael_main.c:408
int ast_cdr_unregister(const char *name)
Unregister a CDR handling engine.
Definition cdr.c:3121

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(), and load_module().

Variable Documentation

◆ __mod_info

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "PostgreSQL CDR Backend" , .key = ASTERISK_GPL_KEY , .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 817 of file cdr_pgsql.c.

◆ ast_module_info

const struct ast_module_info* ast_module_info = &__mod_info
static

Definition at line 817 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.

81 {
82 AST_CLI_DEFINE(handle_cdr_pgsql_status, "Show connection status of the PostgreSQL CDR driver (cdr_pgsql)"),
83};
#define AST_CLI_DEFINE(fn, txt,...)
Definition cli.h:197

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

◆ 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 = AST_MUTEX_INIT_VALUE
static

Definition at line 85 of file cdr_pgsql.c.

Referenced by config_module(), and pgsql_log().

◆ psql_columns

◆ 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