Wed Jan 8 2020 09:49:59

Asterisk developer's documentation


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/module.h"

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_BUF1(size)
 
#define LENGTHEN_BUF2(size)
 

Functions

static void __reg_module (void)
 
static void __unreg_module (void)
 
static int config_module (int reload)
 
static void empty_columns (void)
 
static int load_module (void)
 
static int pgsql_log (struct ast_cdr *cdr)
 
static int reload (void)
 
static int unload_module (void)
 

Variables

static 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 = "ac1f6a56484a8820659555499174e588" , .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CDR_DRIVER, }
 
static struct ast_module_infoast_module_info = &__mod_info
 
static const char config [] = "cdr_pgsql.conf"
 
static PGconn * conn = NULL
 
static int connected = 0
 
static char * encoding = NULL
 
static int maxsize = 512
 
static int maxsize2 = 512
 
static const char name [] = "pgsql"
 
static char * pgdbname = NULL
 
static char * pgdbport = NULL
 
static char * pgdbuser = NULL
 
static char * pghostname = NULL
 
static char * pgpassword = NULL
 
static ast_mutex_t pgsql_lock = { PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP , NULL, 1 }
 
static struct psql_columns psql_columns = { .first = NULL, .last = NULL, .lock = { PTHREAD_RWLOCK_INITIALIZER , NULL, 1 } , }
 
static char * table = NULL
 
static char * tz = NULL
 

Detailed Description

PostgreSQL CDR logger.

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

See also

Definition in file cdr_pgsql.c.

Macro Definition Documentation

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

Definition at line 52 of file cdr_pgsql.c.

Referenced by pgsql_log().

#define LENGTHEN_BUF1 (   size)

Definition at line 75 of file cdr_pgsql.c.

Referenced by pgsql_log().

#define LENGTHEN_BUF2 (   size)

Definition at line 90 of file cdr_pgsql.c.

Referenced by pgsql_log().

Function Documentation

static void __reg_module ( void  )
static

Definition at line 656 of file cdr_pgsql.c.

static void __unreg_module ( void  )
static

Definition at line 656 of file cdr_pgsql.c.

static int config_module ( int  reload)
static

Definition at line 368 of file cdr_pgsql.c.

References ast_alloca, ast_calloc, ast_config_destroy(), ast_config_load, ast_debug, ast_free, ast_log(), AST_MODULE_LOAD_DECLINE, ast_mutex_lock, ast_mutex_unlock, AST_RWLIST_INSERT_TAIL, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, ast_strdup, ast_strdupa, ast_strlen_zero(), ast_variable_browse(), ast_variable_retrieve(), ast_verb, CONFIG_FLAG_FILEUNCHANGED, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEUNCHANGED, empty_columns(), columns::hasdefault, columns::len, LOG_ERROR, LOG_NOTICE, LOG_WARNING, columns::name, columns::notnull, option_debug, pgsql_lock, table, columns::type, unload_module(), var, and version.

Referenced by load_module(), and reload().

369 {
370  struct ast_variable *var;
371  char *pgerror;
372  struct columns *cur;
373  PGresult *result;
374  const char *tmp;
375  struct ast_config *cfg;
376  struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
377 
378  if ((cfg = ast_config_load(config, config_flags)) == NULL || cfg == CONFIG_STATUS_FILEINVALID) {
379  ast_log(LOG_WARNING, "Unable to load config for PostgreSQL CDR's: %s\n", config);
380  return -1;
381  } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
382  return 0;
383  }
384 
386 
387  if (!(var = ast_variable_browse(cfg, "global"))) {
388  ast_config_destroy(cfg);
390  ast_log(LOG_NOTICE, "cdr_pgsql configuration contains no global section, skipping module %s.\n",
391  reload ? "reload" : "load");
392  return -1;
393  }
394 
395  if (!(tmp = ast_variable_retrieve(cfg, "global", "hostname"))) {
396  ast_log(LOG_WARNING, "PostgreSQL server hostname not specified. Assuming unix socket connection\n");
397  tmp = ""; /* connect via UNIX-socket by default */
398  }
399 
401  if (!(pghostname = ast_strdup(tmp))) {
402  ast_config_destroy(cfg);
404  return -1;
405  }
406 
407  if (!(tmp = ast_variable_retrieve(cfg, "global", "dbname"))) {
408  ast_log(LOG_WARNING, "PostgreSQL database not specified. Assuming asterisk\n");
409  tmp = "asteriskcdrdb";
410  }
411 
413  if (!(pgdbname = ast_strdup(tmp))) {
414  ast_config_destroy(cfg);
416  return -1;
417  }
418 
419  if (!(tmp = ast_variable_retrieve(cfg, "global", "user"))) {
420  ast_log(LOG_WARNING, "PostgreSQL database user not specified. Assuming asterisk\n");
421  tmp = "asterisk";
422  }
423 
425  if (!(pgdbuser = ast_strdup(tmp))) {
426  ast_config_destroy(cfg);
428  return -1;
429  }
430 
431  if (!(tmp = ast_variable_retrieve(cfg, "global", "password"))) {
432  ast_log(LOG_WARNING, "PostgreSQL database password not specified. Assuming blank\n");
433  tmp = "";
434  }
435 
437  if (!(pgpassword = ast_strdup(tmp))) {
438  ast_config_destroy(cfg);
440  return -1;
441  }
442 
443  if (!(tmp = ast_variable_retrieve(cfg, "global", "port"))) {
444  ast_log(LOG_WARNING, "PostgreSQL database port not specified. Using default 5432.\n");
445  tmp = "5432";
446  }
447 
449  if (!(pgdbport = ast_strdup(tmp))) {
450  ast_config_destroy(cfg);
452  return -1;
453  }
454 
455  if (!(tmp = ast_variable_retrieve(cfg, "global", "table"))) {
456  ast_log(LOG_WARNING, "CDR table not specified. Assuming cdr\n");
457  tmp = "cdr";
458  }
459 
460  ast_free(table);
461  if (!(table = ast_strdup(tmp))) {
462  ast_config_destroy(cfg);
464  return -1;
465  }
466 
467  if (!(tmp = ast_variable_retrieve(cfg, "global", "encoding"))) {
468  ast_log(LOG_WARNING, "Encoding not specified. Assuming LATIN9\n");
469  tmp = "LATIN9";
470  }
471 
473  if (!(encoding = ast_strdup(tmp))) {
474  ast_config_destroy(cfg);
476  return -1;
477  }
478 
479  if (!(tmp = ast_variable_retrieve(cfg, "global", "timezone"))) {
480  tmp = "";
481  }
482 
483  ast_free(tz);
484  tz = NULL;
485 
486  if (!ast_strlen_zero(tmp) && !(tz = ast_strdup(tmp))) {
487  ast_config_destroy(cfg);
489  return -1;
490  }
491 
492  if (option_debug) {
494  ast_debug(1, "using default unix socket\n");
495  } else {
496  ast_debug(1, "got hostname of %s\n", pghostname);
497  }
498  ast_debug(1, "got port of %s\n", pgdbport);
499  ast_debug(1, "got user of %s\n", pgdbuser);
500  ast_debug(1, "got dbname of %s\n", pgdbname);
501  ast_debug(1, "got password of %s\n", pgpassword);
502  ast_debug(1, "got sql table name of %s\n", table);
503  ast_debug(1, "got encoding of %s\n", encoding);
504  ast_debug(1, "got timezone of %s\n", tz);
505  }
506 
507  conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword);
508  if (PQstatus(conn) != CONNECTION_BAD) {
509  char sqlcmd[768];
510  char *fname, *ftype, *flen, *fnotnull, *fdef;
511  int i, rows, version;
512  ast_debug(1, "Successfully connected to PostgreSQL database.\n");
513  connected = 1;
514  if (PQsetClientEncoding(conn, encoding)) {
515 #ifdef HAVE_PGSQL_pg_encoding_to_char
516  ast_log(LOG_WARNING, "Failed to set encoding to '%s'. Encoding set to default '%s'\n", encoding, pg_encoding_to_char(PQclientEncoding(conn)));
517 #else
518  ast_log(LOG_WARNING, "Failed to set encoding to '%s'. Encoding set to default.\n", encoding);
519 #endif
520  }
521  version = PQserverVersion(conn);
522 
523  if (version >= 70300) {
524  char *schemaname, *tablename;
525  if (strchr(table, '.')) {
526  schemaname = ast_strdupa(table);
527  tablename = strchr(schemaname, '.');
528  *tablename++ = '\0';
529  } else {
530  schemaname = "";
531  tablename = table;
532  }
533 
534  /* Escape special characters in schemaname */
535  if (strchr(schemaname, '\\') || strchr(schemaname, '\'')) {
536  char *tmp = schemaname, *ptr;
537 
538  ptr = schemaname = ast_alloca(strlen(tmp) * 2 + 1);
539  for (; *tmp; tmp++) {
540  if (strchr("\\'", *tmp)) {
541  *ptr++ = *tmp;
542  }
543  *ptr++ = *tmp;
544  }
545  *ptr = '\0';
546  }
547  /* Escape special characters in tablename */
548  if (strchr(tablename, '\\') || strchr(tablename, '\'')) {
549  char *tmp = tablename, *ptr;
550 
551  ptr = tablename = ast_alloca(strlen(tmp) * 2 + 1);
552  for (; *tmp; tmp++) {
553  if (strchr("\\'", *tmp)) {
554  *ptr++ = *tmp;
555  }
556  *ptr++ = *tmp;
557  }
558  *ptr = '\0';
559  }
560 
561  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",
562  tablename,
563  ast_strlen_zero(schemaname) ? "" : "'", ast_strlen_zero(schemaname) ? "current_schema()" : schemaname, ast_strlen_zero(schemaname) ? "" : "'");
564  } else {
565  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);
566  }
567  /* Query the columns */
568  result = PQexec(conn, sqlcmd);
569  if (PQresultStatus(result) != PGRES_TUPLES_OK) {
570  pgerror = PQresultErrorMessage(result);
571  ast_log(LOG_ERROR, "Failed to query database columns: %s\n", pgerror);
572  PQclear(result);
573  unload_module();
576  }
577 
578  rows = PQntuples(result);
579  if (rows == 0) {
580  ast_log(LOG_ERROR, "cdr_pgsql: Failed to query database columns. No columns found, does the table exist?\n");
581  PQclear(result);
582  unload_module();
585  }
586 
587  /* Clear out the columns list. */
588  empty_columns();
589 
590  for (i = 0; i < rows; i++) {
591  fname = PQgetvalue(result, i, 0);
592  ftype = PQgetvalue(result, i, 1);
593  flen = PQgetvalue(result, i, 2);
594  fnotnull = PQgetvalue(result, i, 3);
595  fdef = PQgetvalue(result, i, 4);
596  if (atoi(flen) == -1) {
597  /* For varchar columns, the maximum length is encoded in a different field */
598  flen = PQgetvalue(result, i, 5);
599  }
600  ast_verb(4, "Found column '%s' of type '%s'\n", fname, ftype);
601  cur = ast_calloc(1, sizeof(*cur) + strlen(fname) + strlen(ftype) + 2);
602  if (cur) {
603  sscanf(flen, "%30d", &cur->len);
604  cur->name = (char *)cur + sizeof(*cur);
605  cur->type = (char *)cur + sizeof(*cur) + strlen(fname) + 1;
606  strcpy(cur->name, fname);
607  strcpy(cur->type, ftype);
608  if (*fnotnull == 't') {
609  cur->notnull = 1;
610  } else {
611  cur->notnull = 0;
612  }
613  if (!ast_strlen_zero(fdef)) {
614  cur->hasdefault = 1;
615  } else {
616  cur->hasdefault = 0;
617  }
621  }
622  }
623  PQclear(result);
624  } else {
625  pgerror = PQerrorMessage(conn);
626  ast_log(LOG_ERROR, "Unable to connect to database server %s. CALLS WILL NOT BE LOGGED!!\n", pghostname);
627  ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
628  connected = 0;
629  }
630 
631  ast_config_destroy(cfg);
632 
634  return 0;
635 }
unsigned int hasdefault
Definition: cdr_pgsql.c:69
static PGconn * conn
Definition: cdr_pgsql.c:62
uint32_t version
static char * encoding
Definition: cdr_pgsql.c:56
const char * ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable)
Gets a variable.
Definition: config.c:625
static const char config[]
Definition: cdr_pgsql.c:55
#define ast_strdup(a)
Definition: astmm.h:109
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: utils.h:653
static char * pgdbuser
Definition: cdr_pgsql.c:56
int option_debug
Definition: asterisk.c:182
static ast_mutex_t pgsql_lock
Definition: cdr_pgsql.c:60
#define AST_RWLIST_WRLOCK(head)
Write locks a list.
Definition: linkedlists.h:51
static char * pgdbport
Definition: cdr_pgsql.c:56
#define LOG_WARNING
Definition: logger.h:144
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category)
Goes through variables.
Definition: config.c:597
#define AST_RWLIST_UNLOCK(head)
Attempts to unlock a read/write based list.
Definition: linkedlists.h:150
Structure for variables, used for configurations and for channel variables.
Definition: config.h:75
#define var
Definition: ast_expr2f.c:606
#define ast_mutex_lock(a)
Definition: lock.h:155
static char * tz
Definition: cdr_pgsql.c:56
#define ast_verb(level,...)
Definition: logger.h:243
void ast_config_destroy(struct ast_config *config)
Destroys a config.
Definition: config.c:1037
SQLSMALLINT type
static char * table
Definition: cdr_pgsql.c:56
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:236
static char * pgdbname
Definition: cdr_pgsql.c:56
static char * pgpassword
Definition: cdr_pgsql.c:56
#define ast_config_load(filename, flags)
Load a config file.
Definition: config.h:170
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:63
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: utils.h:663
#define LOG_ERROR
Definition: logger.h:155
static int reload(void)
Definition: cdr_pgsql.c:646
void ast_log(int level, const char *file, int line, const char *function, const char *fmt,...)
Used for sending a log message This is the standard logger function. Probably the only way you will i...
Definition: logger.c:1207
#define LOG_NOTICE
Definition: logger.h:133
#define ast_free(a)
Definition: astmm.h:97
Structure used to handle boolean flags.
Definition: utils.h:200
static char * pghostname
Definition: cdr_pgsql.c:56
#define AST_RWLIST_INSERT_TAIL
Definition: linkedlists.h:726
unsigned int notnull
Definition: cdr_pgsql.c:68
int len
Definition: cdr_pgsql.c:67
#define ast_calloc(a, b)
Definition: astmm.h:82
static int connected
Definition: cdr_pgsql.c:57
static int unload_module(void)
Definition: cdr_pgsql.c:348
#define CONFIG_STATUS_FILEINVALID
Definition: config.h:52
static void empty_columns(void)
Definition: cdr_pgsql.c:337
#define CONFIG_STATUS_FILEUNCHANGED
Definition: config.h:51
#define ast_mutex_unlock(a)
Definition: lock.h:156
static void empty_columns ( void  )
static

Definition at line 337 of file cdr_pgsql.c.

References ast_free, AST_RWLIST_REMOVE_HEAD, AST_RWLIST_UNLOCK, and AST_RWLIST_WRLOCK.

Referenced by config_module(), and unload_module().

338 {
339  struct columns *current;
341  while ((current = AST_RWLIST_REMOVE_HEAD(&psql_columns, list))) {
342  ast_free(current);
343  }
345 
346 }
#define AST_RWLIST_WRLOCK(head)
Write locks a list.
Definition: linkedlists.h:51
#define AST_RWLIST_UNLOCK(head)
Attempts to unlock a read/write based list.
Definition: linkedlists.h:150
struct columns::@71 list
#define ast_free(a)
Definition: astmm.h:97
#define AST_RWLIST_REMOVE_HEAD
Definition: linkedlists.h:829
static int load_module ( void  )
static

Definition at line 637 of file cdr_pgsql.c.

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

638 {
639  if (config_module(0)) {
641  }
644 }
const char * description
Definition: module.h:234
int ast_cdr_register(const char *name, const char *desc, ast_cdrbe be)
Register a CDR handling engine.
Definition: cdr.c:130
static const char name[]
static int pgsql_log(struct ast_cdr *cdr)
Definition: cdr_pgsql.c:104
static int config_module(int reload)
Definition: cdr_pgsql.c:368
static int pgsql_log ( struct ast_cdr cdr)
static

Definition at line 104 of file cdr_pgsql.c.

References ast_cdr::answer, ast_cdr_getvar(), ast_debug, ast_free, ast_localtime(), ast_log(), ast_mutex_lock, ast_mutex_unlock, AST_RWLIST_RDLOCK, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, ast_str_append(), ast_str_buffer(), ast_str_create(), ast_str_set(), ast_str_strlen(), ast_strftime(), ast_tvdiff_us(), ast_tvzero(), ast_verb, DATE_FORMAT, ast_cdr::end, first, columns::hasdefault, LENGTHEN_BUF1, LENGTHEN_BUF2, LOG_ERROR, LOG_WARNING, maxsize2, columns::name, columns::notnull, pgsql_lock, ast_cdr::start, columns::type, and value.

Referenced by load_module().

105 {
106  struct ast_tm tm;
107  char *pgerror;
108  PGresult *result;
109 
111 
112  if ((!connected) && pghostname && pgdbuser && pgpassword && pgdbname) {
113  conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword);
114  if (PQstatus(conn) != CONNECTION_BAD) {
115  connected = 1;
116  if (PQsetClientEncoding(conn, encoding)) {
117 #ifdef HAVE_PGSQL_pg_encoding_to_char
118  ast_log(LOG_WARNING, "Failed to set encoding to '%s'. Encoding set to default '%s'\n", encoding, pg_encoding_to_char(PQclientEncoding(conn)));
119 #else
120  ast_log(LOG_WARNING, "Failed to set encoding to '%s'. Encoding set to default.\n", encoding);
121 #endif
122  }
123  } else {
124  pgerror = PQerrorMessage(conn);
125  ast_log(LOG_ERROR, "Unable to connect to database server %s. Calls will not be logged!\n", pghostname);
126  ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
127  PQfinish(conn);
128  conn = NULL;
129  }
130  }
131 
132  if (connected) {
133  struct columns *cur;
134  struct ast_str *sql = ast_str_create(maxsize), *sql2 = ast_str_create(maxsize2);
135  char buf[257], escapebuf[513], *value;
136  int first = 1;
137 
138  if (!sql || !sql2) {
139  ast_free(sql);
140  ast_free(sql2);
141  return -1;
142  }
143 
144  ast_str_set(&sql, 0, "INSERT INTO %s (", table);
145  ast_str_set(&sql2, 0, " VALUES (");
146 
148  AST_RWLIST_TRAVERSE(&psql_columns, cur, list) {
149  /* For fields not set, simply skip them */
150  ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0);
151  if (strcmp(cur->name, "calldate") == 0 && !value) {
152  ast_cdr_getvar(cdr, "start", &value, buf, sizeof(buf), 0, 0);
153  }
154  if (!value) {
155  if (cur->notnull && !cur->hasdefault) {
156  /* Field is NOT NULL (but no default), must include it anyway */
157  LENGTHEN_BUF1(strlen(cur->name) + 2);
158  ast_str_append(&sql, 0, "%s\"%s\"", first ? "" : ",", cur->name);
159  LENGTHEN_BUF2(3);
160  ast_str_append(&sql2, 0, "%s''", first ? "" : ",");
161  first = 0;
162  }
163  continue;
164  }
165 
166  LENGTHEN_BUF1(strlen(cur->name) + 2);
167  ast_str_append(&sql, 0, "%s\"%s\"", first ? "" : ",", cur->name);
168 
169  if (strcmp(cur->name, "start") == 0 || strcmp(cur->name, "calldate") == 0) {
170  if (strncmp(cur->type, "int", 3) == 0) {
171  LENGTHEN_BUF2(13);
172  ast_str_append(&sql2, 0, "%s%ld", first ? "" : ",", (long) cdr->start.tv_sec);
173  } else if (strncmp(cur->type, "float", 5) == 0) {
174  LENGTHEN_BUF2(31);
175  ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->start.tv_sec + (double)cdr->start.tv_usec / 1000000.0);
176  } else {
177  /* char, hopefully */
178  LENGTHEN_BUF2(31);
179  ast_localtime(&cdr->start, &tm, tz);
180  ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm);
181  ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", buf);
182  }
183  } else if (strcmp(cur->name, "answer") == 0) {
184  if (strncmp(cur->type, "int", 3) == 0) {
185  LENGTHEN_BUF2(13);
186  ast_str_append(&sql2, 0, "%s%ld", first ? "" : ",", (long) cdr->answer.tv_sec);
187  } else if (strncmp(cur->type, "float", 5) == 0) {
188  LENGTHEN_BUF2(31);
189  ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->answer.tv_sec + (double)cdr->answer.tv_usec / 1000000.0);
190  } else {
191  /* char, hopefully */
192  LENGTHEN_BUF2(31);
193  ast_localtime(&cdr->answer, &tm, tz);
194  ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm);
195  ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", buf);
196  }
197  } else if (strcmp(cur->name, "end") == 0) {
198  if (strncmp(cur->type, "int", 3) == 0) {
199  LENGTHEN_BUF2(13);
200  ast_str_append(&sql2, 0, "%s%ld", first ? "" : ",", (long) cdr->end.tv_sec);
201  } else if (strncmp(cur->type, "float", 5) == 0) {
202  LENGTHEN_BUF2(31);
203  ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->end.tv_sec + (double)cdr->end.tv_usec / 1000000.0);
204  } else {
205  /* char, hopefully */
206  LENGTHEN_BUF2(31);
207  ast_localtime(&cdr->end, &tm, tz);
208  ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm);
209  ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", buf);
210  }
211  } else if (strcmp(cur->name, "duration") == 0 || strcmp(cur->name, "billsec") == 0) {
212  if (cur->type[0] == 'i') {
213  /* Get integer, no need to escape anything */
214  ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0);
215  LENGTHEN_BUF2(13);
216  ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", value);
217  } else if (strncmp(cur->type, "float", 5) == 0) {
218  struct timeval *when = cur->name[0] == 'd' ? &cdr->start : ast_tvzero(cdr->answer) ? &cdr->end : &cdr->answer;
219  LENGTHEN_BUF2(31);
220  ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double) (ast_tvdiff_us(cdr->end, *when) / 1000000.0));
221  } else {
222  /* Char field, probably */
223  struct timeval *when = cur->name[0] == 'd' ? &cdr->start : ast_tvzero(cdr->answer) ? &cdr->end : &cdr->answer;
224  LENGTHEN_BUF2(31);
225  ast_str_append(&sql2, 0, "%s'%f'", first ? "" : ",", (double) (ast_tvdiff_us(cdr->end, *when) / 1000000.0));
226  }
227  } else if (strcmp(cur->name, "disposition") == 0 || strcmp(cur->name, "amaflags") == 0) {
228  if (strncmp(cur->type, "int", 3) == 0) {
229  /* Integer, no need to escape anything */
230  ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 1);
231  LENGTHEN_BUF2(13);
232  ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", value);
233  } else {
234  /* Although this is a char field, there are no special characters in the values for these fields */
235  ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0);
236  LENGTHEN_BUF2(31);
237  ast_str_append(&sql2, 0, "%s'%s'", first ? "" : ",", value);
238  }
239  } else {
240  /* Arbitrary field, could be anything */
241  ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0);
242  if (strncmp(cur->type, "int", 3) == 0) {
243  long long whatever;
244  if (value && sscanf(value, "%30lld", &whatever) == 1) {
245  LENGTHEN_BUF2(26);
246  ast_str_append(&sql2, 0, "%s%lld", first ? "" : ",", whatever);
247  } else {
248  LENGTHEN_BUF2(2);
249  ast_str_append(&sql2, 0, "%s0", first ? "" : ",");
250  }
251  } else if (strncmp(cur->type, "float", 5) == 0) {
252  long double whatever;
253  if (value && sscanf(value, "%30Lf", &whatever) == 1) {
254  LENGTHEN_BUF2(51);
255  ast_str_append(&sql2, 0, "%s%30Lf", first ? "" : ",", whatever);
256  } else {
257  LENGTHEN_BUF2(2);
258  ast_str_append(&sql2, 0, "%s0", first ? "" : ",");
259  }
260  /* XXX Might want to handle dates, times, and other misc fields here XXX */
261  } else {
262  if (value)
263  PQescapeStringConn(conn, escapebuf, value, strlen(value), NULL);
264  else
265  escapebuf[0] = '\0';
266  LENGTHEN_BUF2(strlen(escapebuf) + 3);
267  ast_str_append(&sql2, 0, "%s'%s'", first ? "" : ",", escapebuf);
268  }
269  }
270  first = 0;
271  }
272 
273  LENGTHEN_BUF1(ast_str_strlen(sql2) + 2);
275  ast_str_append(&sql, 0, ")%s)", ast_str_buffer(sql2));
276  ast_verb(11, "[%s]\n", ast_str_buffer(sql));
277 
278  ast_debug(2, "inserting a CDR record.\n");
279 
280  /* Test to be sure we're still connected... */
281  /* If we're connected, and connection is working, good. */
282  /* Otherwise, attempt reconnect. If it fails... sorry... */
283  if (PQstatus(conn) == CONNECTION_OK) {
284  connected = 1;
285  } else {
286  ast_log(LOG_ERROR, "Connection was lost... attempting to reconnect.\n");
287  PQreset(conn);
288  if (PQstatus(conn) == CONNECTION_OK) {
289  ast_log(LOG_ERROR, "Connection reestablished.\n");
290  connected = 1;
291  } else {
292  pgerror = PQerrorMessage(conn);
293  ast_log(LOG_ERROR, "Unable to reconnect to database server %s. Calls will not be logged!\n", pghostname);
294  ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
295  PQfinish(conn);
296  conn = NULL;
297  connected = 0;
299  ast_free(sql);
300  ast_free(sql2);
301  return -1;
302  }
303  }
304  result = PQexec(conn, ast_str_buffer(sql));
305  if (PQresultStatus(result) != PGRES_COMMAND_OK) {
306  pgerror = PQresultErrorMessage(result);
307  ast_log(LOG_ERROR, "Failed to insert call detail record into database!\n");
308  ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
309  ast_log(LOG_ERROR, "Connection may have been lost... attempting to reconnect.\n");
310  PQreset(conn);
311  if (PQstatus(conn) == CONNECTION_OK) {
312  ast_log(LOG_ERROR, "Connection reestablished.\n");
313  connected = 1;
314  PQclear(result);
315  result = PQexec(conn, ast_str_buffer(sql));
316  if (PQresultStatus(result) != PGRES_COMMAND_OK) {
317  pgerror = PQresultErrorMessage(result);
318  ast_log(LOG_ERROR, "HARD ERROR! Attempted reconnection failed. DROPPING CALL RECORD!\n");
319  ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
320  }
321  }
323  PQclear(result);
324  ast_free(sql);
325  ast_free(sql2);
326  return -1;
327  }
328  PQclear(result);
329  ast_free(sql);
330  ast_free(sql2);
331  }
333  return 0;
334 }
unsigned int hasdefault
Definition: cdr_pgsql.c:69
static PGconn * conn
Definition: cdr_pgsql.c:62
static char * encoding
Definition: cdr_pgsql.c:56
static char * pgdbuser
Definition: cdr_pgsql.c:56
static ast_mutex_t pgsql_lock
Definition: cdr_pgsql.c:60
static char * pgdbport
Definition: cdr_pgsql.c:56
#define LOG_WARNING
Definition: logger.h:144
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:497
#define AST_RWLIST_UNLOCK(head)
Attempts to unlock a read/write based list.
Definition: linkedlists.h:150
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:1570
int ast_tvzero(const struct timeval t)
Returns true if the argument is 0,0.
Definition: time.h:100
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:900
struct ast_str * ast_str_create(size_t init_len)
Create a malloc&#39;ed dynamic length string.
Definition: strings.h:420
#define ast_mutex_lock(a)
Definition: lock.h:155
static char * tz
Definition: cdr_pgsql.c:56
int value
Definition: syslog.c:39
#define ast_verb(level,...)
Definition: logger.h:243
SQLSMALLINT type
void ast_cdr_getvar(struct ast_cdr *cdr, const char *name, char **ret, char *workspace, int workspacelen, int recur, int raw)
Definition: cdr.c:264
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:874
#define AST_RWLIST_RDLOCK(head)
Read locks a list.
Definition: linkedlists.h:77
static char * table
Definition: cdr_pgsql.c:56
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:236
static char * pgdbname
Definition: cdr_pgsql.c:56
static char * pgpassword
Definition: cdr_pgsql.c:56
#define AST_RWLIST_TRAVERSE
Definition: linkedlists.h:493
struct timeval answer
Definition: cdr.h:102
#define LOG_ERROR
Definition: logger.h:155
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:364
struct sla_ringing_trunk * first
Definition: app_meetme.c:965
void ast_log(int level, const char *file, int line, const char *function, const char *fmt,...)
Used for sending a log message This is the standard logger function. Probably the only way you will i...
Definition: logger.c:1207
#define LENGTHEN_BUF1(size)
Definition: cdr_pgsql.c:75
struct timeval start
Definition: cdr.h:100
static int maxsize
Definition: cdr_pgsql.c:58
#define ast_free(a)
Definition: astmm.h:97
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:2351
static char * pghostname
Definition: cdr_pgsql.c:56
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:471
struct timeval end
Definition: cdr.h:104
unsigned int notnull
Definition: cdr_pgsql.c:68
static int connected
Definition: cdr_pgsql.c:57
int64_t ast_tvdiff_us(struct timeval end, struct timeval start)
Computes the difference (in microseconds) between two struct timeval instances.
Definition: time.h:70
#define DATE_FORMAT
Definition: cdr_pgsql.c:52
static int maxsize2
Definition: cdr_pgsql.c:58
#define ast_mutex_unlock(a)
Definition: lock.h:156
#define LENGTHEN_BUF2(size)
Definition: cdr_pgsql.c:90
static int reload ( void  )
static

Definition at line 646 of file cdr_pgsql.c.

References config_module().

647 {
648  return config_module(1);
649 }
static int config_module(int reload)
Definition: cdr_pgsql.c:368
static int unload_module ( void  )
static

Definition at line 348 of file cdr_pgsql.c.

References ast_cdr_unregister(), ast_free, and empty_columns().

Referenced by config_module().

349 {
351 
352  PQfinish(conn);
353 
359  ast_free(table);
361  ast_free(tz);
362 
363  empty_columns();
364 
365  return 0;
366 }
static PGconn * conn
Definition: cdr_pgsql.c:62
static char * encoding
Definition: cdr_pgsql.c:56
static char * pgdbuser
Definition: cdr_pgsql.c:56
static char * pgdbport
Definition: cdr_pgsql.c:56
static char * tz
Definition: cdr_pgsql.c:56
static char * table
Definition: cdr_pgsql.c:56
static char * pgdbname
Definition: cdr_pgsql.c:56
static char * pgpassword
Definition: cdr_pgsql.c:56
static const char name[]
#define ast_free(a)
Definition: astmm.h:97
static char * pghostname
Definition: cdr_pgsql.c:56
static void empty_columns(void)
Definition: cdr_pgsql.c:337
void ast_cdr_unregister(const char *name)
Unregister a CDR handling engine.
Definition: cdr.c:165

Variable Documentation

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

Definition at line 656 of file cdr_pgsql.c.

Definition at line 656 of file cdr_pgsql.c.

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

Definition at line 55 of file cdr_pgsql.c.

PGconn* conn = NULL
static

Definition at line 62 of file cdr_pgsql.c.

char * encoding = NULL
static

Definition at line 56 of file cdr_pgsql.c.

Referenced by check_header(), and custom_prepare().

int maxsize = 512
static

Definition at line 58 of file cdr_pgsql.c.

int maxsize2 = 512
static

Definition at line 58 of file cdr_pgsql.c.

Referenced by pgsql_log().

const char name[] = "pgsql"
static

Definition at line 54 of file cdr_pgsql.c.

char * pgdbname = NULL
static

Definition at line 56 of file cdr_pgsql.c.

char * pgdbport = NULL
static

Definition at line 56 of file cdr_pgsql.c.

char * pgdbuser = NULL
static

Definition at line 56 of file cdr_pgsql.c.

char* pghostname = NULL
static

Definition at line 56 of file cdr_pgsql.c.

char * pgpassword = NULL
static

Definition at line 56 of file cdr_pgsql.c.

ast_mutex_t pgsql_lock = { PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP , NULL, 1 }
static

Definition at line 60 of file cdr_pgsql.c.

Referenced by config_module(), and pgsql_log().

struct psql_columns psql_columns = { .first = NULL, .last = NULL, .lock = { PTHREAD_RWLOCK_INITIALIZER , NULL, 1 } , }
static
char * table = NULL
static

Definition at line 56 of file cdr_pgsql.c.

Referenced by config_module().