52 #define DATE_FORMAT "'%Y-%m-%d %T'"
54 static const char name[] =
"pgsql";
55 static const char config[] =
"cdr_pgsql.conf";
62 static PGconn *
conn = NULL;
75 #define LENGTHEN_BUF1(size) \
78 if (ast_str_strlen(sql) + size + 1 > ast_str_size(sql)) { \
79 if (ast_str_make_space(&sql, ((ast_str_size(sql) + size + 3) / 512 + 1) * 512) != 0) { \
80 ast_log(LOG_ERROR, "Unable to allocate sufficient memory. Insert CDR failed.\n"); \
83 AST_RWLIST_UNLOCK(&psql_columns); \
84 ast_mutex_unlock(&pgsql_lock); \
90 #define LENGTHEN_BUF2(size) \
92 if (ast_str_strlen(sql2) + size + 1 > ast_str_size(sql2)) { \
93 if (ast_str_make_space(&sql2, ((ast_str_size(sql2) + size + 3) / 512 + 1) * 512) != 0) { \
94 ast_log(LOG_ERROR, "Unable to allocate sufficient memory. Insert CDR failed.\n"); \
97 AST_RWLIST_UNLOCK(&psql_columns); \
98 ast_mutex_unlock(&pgsql_lock); \
112 if ((!connected) && pghostname && pgdbuser && pgpassword && pgdbname) {
113 conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword);
114 if (PQstatus(conn) != CONNECTION_BAD) {
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)));
120 ast_log(
LOG_WARNING,
"Failed to set encoding to '%s'. Encoding set to default.\n", encoding);
124 pgerror = PQerrorMessage(conn);
125 ast_log(
LOG_ERROR,
"Unable to connect to database server %s. Calls will not be logged!\n", pghostname);
135 char buf[257], escapebuf[513], *
value;
151 if (strcmp(cur->
name,
"calldate") == 0 && !value) {
169 if (strcmp(cur->
name,
"start") == 0 || strcmp(cur->
name,
"calldate") == 0) {
170 if (strncmp(cur->
type,
"int", 3) == 0) {
173 }
else if (strncmp(cur->
type,
"float", 5) == 0) {
175 ast_str_append(&sql2, 0,
"%s%f", first ?
"" :
",", (
double)cdr->
start.tv_sec + (
double)cdr->
start.tv_usec / 1000000.0);
183 }
else if (strcmp(cur->
name,
"answer") == 0) {
184 if (strncmp(cur->
type,
"int", 3) == 0) {
187 }
else if (strncmp(cur->
type,
"float", 5) == 0) {
197 }
else if (strcmp(cur->
name,
"end") == 0) {
198 if (strncmp(cur->
type,
"int", 3) == 0) {
201 }
else if (strncmp(cur->
type,
"float", 5) == 0) {
203 ast_str_append(&sql2, 0,
"%s%f", first ?
"" :
",", (
double)cdr->
end.tv_sec + (
double)cdr->
end.tv_usec / 1000000.0);
211 }
else if (strcmp(cur->
name,
"duration") == 0 || strcmp(cur->
name,
"billsec") == 0) {
212 if (cur->
type[0] ==
'i') {
217 }
else if (strncmp(cur->
type,
"float", 5) == 0) {
227 }
else if (strcmp(cur->
name,
"disposition") == 0 || strcmp(cur->
name,
"amaflags") == 0) {
228 if (strncmp(cur->
type,
"int", 3) == 0) {
242 if (strncmp(cur->
type,
"int", 3) == 0) {
244 if (value && sscanf(value,
"%30lld", &whatever) == 1) {
251 }
else if (strncmp(cur->
type,
"float", 5) == 0) {
252 long double whatever;
253 if (value && sscanf(value,
"%30Lf", &whatever) == 1) {
263 PQescapeStringConn(conn, escapebuf, value, strlen(value), NULL);
278 ast_debug(2,
"inserting a CDR record.\n");
283 if (PQstatus(conn) == CONNECTION_OK) {
288 if (PQstatus(conn) == CONNECTION_OK) {
292 pgerror = PQerrorMessage(conn);
293 ast_log(
LOG_ERROR,
"Unable to reconnect to database server %s. Calls will not be logged!\n", pghostname);
305 if (PQresultStatus(result) != PGRES_COMMAND_OK) {
306 pgerror = PQresultErrorMessage(result);
309 ast_log(
LOG_ERROR,
"Connection may have been lost... attempting to reconnect.\n");
311 if (PQstatus(conn) == CONNECTION_OK) {
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");
339 struct columns *current;
390 ast_log(
LOG_NOTICE,
"cdr_pgsql configuration contains no global section, skipping module %s.\n",
391 reload ?
"reload" :
"load");
396 ast_log(
LOG_WARNING,
"PostgreSQL server hostname not specified. Assuming unix socket connection\n");
409 tmp =
"asteriskcdrdb";
444 ast_log(
LOG_WARNING,
"PostgreSQL database port not specified. Using default 5432.\n");
494 ast_debug(1,
"using default unix socket\n");
496 ast_debug(1,
"got hostname of %s\n", pghostname);
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);
507 conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword);
508 if (PQstatus(conn) != CONNECTION_BAD) {
510 char *fname, *ftype, *flen, *fnotnull, *fdef;
512 ast_debug(1,
"Successfully connected to PostgreSQL database.\n");
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)));
518 ast_log(
LOG_WARNING,
"Failed to set encoding to '%s'. Encoding set to default.\n", encoding);
521 version = PQserverVersion(conn);
523 if (version >= 70300) {
524 char *schemaname, *tablename;
525 if (strchr(table,
'.')) {
527 tablename = strchr(schemaname,
'.');
535 if (strchr(schemaname,
'\\') || strchr(schemaname,
'\'')) {
536 char *tmp = schemaname, *ptr;
538 ptr = schemaname =
ast_alloca(strlen(tmp) * 2 + 1);
539 for (; *tmp; tmp++) {
540 if (strchr(
"\\'", *tmp)) {
548 if (strchr(tablename,
'\\') || strchr(tablename,
'\'')) {
549 char *tmp = tablename, *ptr;
551 ptr = tablename =
ast_alloca(strlen(tmp) * 2 + 1);
552 for (; *tmp; tmp++) {
553 if (strchr(
"\\'", *tmp)) {
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",
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);
568 result = PQexec(conn, sqlcmd);
569 if (PQresultStatus(result) != PGRES_TUPLES_OK) {
570 pgerror = PQresultErrorMessage(result);
578 rows = PQntuples(result);
580 ast_log(
LOG_ERROR,
"cdr_pgsql: Failed to query database columns. No columns found, does the table exist?\n");
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) {
598 flen = PQgetvalue(result, i, 5);
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);
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') {
625 pgerror = PQerrorMessage(conn);
626 ast_log(
LOG_ERROR,
"Unable to connect to database server %s. CALLS WILL NOT BE LOGGED!!\n", pghostname);
Asterisk main include file. File version handling, generic pbx functions.
const char * ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable)
Gets a variable.
#define AST_RWLIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a read/write list of specified type, statically initialized...
static const char config[]
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
static ast_mutex_t pgsql_lock
#define AST_RWLIST_WRLOCK(head)
Write locks a list.
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category)
Goes through variables.
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
#define AST_RWLIST_UNLOCK(head)
Attempts to unlock a read/write based list.
struct ast_tm * ast_localtime(const struct timeval *timep, struct ast_tm *p_tm, const char *zone)
Timezone-independent version of localtime_r(3).
Structure for variables, used for configurations and for channel variables.
int ast_tvzero(const struct timeval t)
Returns true if the argument is 0,0.
Configuration File Parser.
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
struct ast_str * ast_str_create(size_t init_len)
Create a malloc'ed dynamic length string.
#define ast_mutex_lock(a)
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
#define ast_verb(level,...)
void ast_config_destroy(struct ast_config *config)
Destroys a config.
void ast_cdr_getvar(struct ast_cdr *cdr, const char *name, char **ret, char *workspace, int workspacelen, int recur, int raw)
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
#define AST_RWLIST_RDLOCK(head)
Read locks a list.
#define ast_debug(level,...)
Log a DEBUG message.
int ast_cdr_register(const char *name, const char *desc, ast_cdrbe be)
Register a CDR handling engine.
General Asterisk PBX channel definitions.
#define ast_config_load(filename, flags)
Load a config file.
static force_inline int attribute_pure ast_strlen_zero(const char *s)
#define AST_RWLIST_TRAVERSE
#define ast_strdupa(s)
duplicate a string in memory from the stack
Responsible for call detail data.
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
struct sla_ringing_trunk * first
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...
#define LENGTHEN_BUF1(size)
#define AST_RWLIST_REMOVE_HEAD
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...
Structure used to handle boolean flags.
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
#define AST_RWLIST_INSERT_TAIL
static int load_module(void)
static int pgsql_log(struct ast_cdr *cdr)
int64_t ast_tvdiff_us(struct timeval end, struct timeval start)
Computes the difference (in microseconds) between two struct timeval instances.
static int unload_module(void)
#define CONFIG_STATUS_FILEINVALID
static int config_module(int reload)
#define ASTERISK_GPL_KEY
The text the key() function should return.
Asterisk module definitions.
static void empty_columns(void)
#define AST_MUTEX_DEFINE_STATIC(mutex)
void ast_cdr_unregister(const char *name)
Unregister a CDR handling engine.
#define ASTERISK_FILE_VERSION(file, version)
Register/unregister a source code file with the core.
#define CONFIG_STATUS_FILEUNCHANGED
#define ast_mutex_unlock(a)
#define LENGTHEN_BUF2(size)