Asterisk - The Open Source Telephony Project GIT-master-67613d1
cel_pgsql.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2008
5 *
6 * Steve Murphy - adapted to CEL, from:
7 * Matthew D. Hardeman <mhardemn@papersoft.com>
8 * Adapted from the MySQL CDR logger originally by James Sharp
9 *
10 * Modified April, 2007; Dec, 2008
11 * Steve Murphy <murf@digium.com>
12
13 * Modified September 2003
14 * Matthew D. Hardeman <mhardemn@papersoft.com>
15 *
16 * See http://www.asterisk.org for more information about
17 * the Asterisk project. Please do not directly contact
18 * any of the maintainers of this project for assistance;
19 * the project provides a web site, mailing lists and IRC
20 * channels for your use.
21 *
22 * This program is free software, distributed under the terms of
23 * the GNU General Public License Version 2. See the LICENSE file
24 * at the top of the source tree.
25 */
26
27/*! \file
28 *
29 * \brief PostgreSQL CEL logger
30 *
31 * \author Steve Murphy <murf@digium.com>
32 * PostgreSQL http://www.postgresql.org/
33 * \ingroup cel_drivers
34 */
35
36/*** MODULEINFO
37 <depend>pgsql</depend>
38 <support_level>extended</support_level>
39 ***/
40
41#include "asterisk.h"
42
43#include <libpq-fe.h>
44
45#include "asterisk/config.h"
46#include "asterisk/options.h"
47#include "asterisk/channel.h"
48#include "asterisk/cel.h"
49#include "asterisk/module.h"
50#include "asterisk/logger.h"
51#include "asterisk.h"
52
53#define DATE_FORMAT "%Y-%m-%d %T.%6q"
54
55#define PGSQL_BACKEND_NAME "CEL PGSQL backend"
56
57#define PGSQL_MIN_VERSION_SCHEMA 70300
58
59static char *config = "cel_pgsql.conf";
60
61static char *pghostname;
62static char *pgdbname;
63static char *pgdbuser;
64static char *pgpassword;
65static char *pgappname;
66static char *pgdbport;
67static char *table;
68static char *schema;
69
70static int connected = 0;
71/* Optimization to reduce number of memory allocations */
72static int maxsize = 512, maxsize2 = 512;
73static int usegmtime = 0;
74
75/*! \brief show_user_def is off by default */
76#define CEL_SHOW_USERDEF_DEFAULT 0
77
78/*! TRUE if we should set the eventtype field to USER_DEFINED on user events. */
79static unsigned char cel_show_user_def;
80
82
83static PGconn *conn = NULL;
84static PGresult *result = NULL;
85
86struct columns {
87 char *name;
88 char *type;
89 int len;
90 unsigned int notnull:1;
91 unsigned int hasdefault:1;
93};
94
96
97#define LENGTHEN_BUF(size, var_sql) \
98 do { \
99 /* Lengthen buffer, if necessary */ \
100 if (ast_str_strlen(var_sql) + size + 1 > ast_str_size(var_sql)) { \
101 if (ast_str_make_space(&var_sql, ((ast_str_size(var_sql) + size + 3) / 512 + 1) * 512) != 0) { \
102 ast_log(LOG_ERROR, "Unable to allocate sufficient memory. Insert CEL '%s:%s' failed.\n", pghostname, table); \
103 ast_free(sql); \
104 ast_free(sql2); \
105 AST_RWLIST_UNLOCK(&psql_columns); \
106 return; \
107 } \
108 } \
109 } while (0)
110
111#define LENGTHEN_BUF1(size) \
112 LENGTHEN_BUF(size, sql);
113#define LENGTHEN_BUF2(size) \
114 LENGTHEN_BUF(size, sql2);
115
116static void pgsql_reconnect(void)
117{
118 struct ast_str *conn_info = ast_str_create(128);
119 if (!conn_info) {
120 ast_log(LOG_ERROR, "Failed to allocate memory for connection string.\n");
121 return;
122 }
123
124 if (conn) {
125 PQfinish(conn);
126 conn = NULL;
127 }
128
129 ast_str_set(&conn_info, 0, "host=%s port=%s dbname=%s user=%s",
131
133 ast_str_append(&conn_info, 0, " application_name=%s", pgappname);
134 }
135
137 ast_str_append(&conn_info, 0, " password=%s", pgpassword);
138 }
139
140 conn = PQconnectdb(ast_str_buffer(conn_info));
141 ast_free(conn_info);
142}
143
144
145static void pgsql_log(struct ast_event *event)
146{
147 struct ast_tm tm;
148 char timestr[128];
149 char *pgerror;
150 struct ast_cel_event_record record = {
152 };
153
154 if (ast_cel_fill_record(event, &record)) {
155 return;
156 }
157
159
160 ast_localtime(&record.event_time, &tm, usegmtime ? "GMT" : NULL);
161 ast_strftime(timestr, sizeof(timestr), DATE_FORMAT, &tm);
162
163 if ((!connected) && pghostname && pgdbuser && pgpassword && pgdbname) {
165 if (PQstatus(conn) != CONNECTION_BAD) {
166 connected = 1;
167 } else {
168 pgerror = PQerrorMessage(conn);
169 ast_log(LOG_ERROR, "cel_pgsql: Unable to connect to database server %s. Calls will not be logged!\n", pghostname);
170 ast_log(LOG_ERROR, "cel_pgsql: Reason: %s\n", pgerror);
171 PQfinish(conn);
172 conn = NULL;
173 }
174 }
175 if (connected) {
176 struct columns *cur;
177 struct ast_str *sql = ast_str_create(maxsize), *sql2 = ast_str_create(maxsize2);
178 char buf[257];
179 char *escapebuf = NULL;
180 const char *value;
181 int first = 1;
182 size_t bufsize = 513;
183
184 escapebuf = ast_malloc(bufsize);
185 if (!escapebuf || !sql || !sql2) {
186 goto ast_log_cleanup;
187 }
188
189 ast_str_set(&sql, 0, "INSERT INTO %s (", table);
190 ast_str_set(&sql2, 0, " VALUES (");
191
192#define SEP (first ? "" : ",")
193
195 AST_RWLIST_TRAVERSE(&psql_columns, cur, list) {
196 LENGTHEN_BUF1(strlen(cur->name) + 2);
197 ast_str_append(&sql, 0, "%s\"%s\"", SEP, cur->name);
198
199 if (strcmp(cur->name, "eventtime") == 0) {
200 if (strncmp(cur->type, "int", 3) == 0) {
201 LENGTHEN_BUF2(13);
202 ast_str_append(&sql2, 0, "%s%ld", SEP, (long) record.event_time.tv_sec);
203 } else if (strncmp(cur->type, "float", 5) == 0) {
204 LENGTHEN_BUF2(31);
205 ast_str_append(&sql2, 0, "%s%f",
206 SEP,
207 (double) record.event_time.tv_sec +
208 (double) record.event_time.tv_usec / 1000000.0);
209 } else {
210 /* char, hopefully */
211 LENGTHEN_BUF2(31);
212 ast_localtime(&record.event_time, &tm, usegmtime ? "GMT" : NULL);
213 ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm);
214 ast_str_append(&sql2, 0, "%s'%s'", SEP, buf);
215 }
216 } else if (strcmp(cur->name, "eventtype") == 0) {
217 if (cur->type[0] == 'i') {
218 /* Get integer, no need to escape anything */
219 LENGTHEN_BUF2(5);
220 ast_str_append(&sql2, 0, "%s%d", SEP, (int) record.event_type);
221 } else if (strncmp(cur->type, "float", 5) == 0) {
222 LENGTHEN_BUF2(31);
223 ast_str_append(&sql2, 0, "%s%f", SEP, (double) record.event_type);
224 } else {
225 /* Char field, probably */
226 const char *event_name;
227
228 event_name = (!cel_show_user_def
229 && record.event_type == AST_CEL_USER_DEFINED)
230 ? record.user_defined_name : record.event_name;
231 LENGTHEN_BUF2(strlen(event_name) + 1);
232 ast_str_append(&sql2, 0, "%s'%s'", SEP, event_name);
233 }
234 } else if (strcmp(cur->name, "amaflags") == 0) {
235 if (strncmp(cur->type, "int", 3) == 0) {
236 /* Integer, no need to escape anything */
237 LENGTHEN_BUF2(13);
238 ast_str_append(&sql2, 0, "%s%u", SEP, record.amaflag);
239 } else {
240 /* Although this is a char field, there are no special characters in the values for these fields */
241 LENGTHEN_BUF2(31);
242 ast_str_append(&sql2, 0, "%s'%u'", SEP, record.amaflag);
243 }
244 } else {
245 /* Arbitrary field, could be anything */
246 if (strcmp(cur->name, "userdeftype") == 0) {
247 value = record.user_defined_name;
248 } else if (strcmp(cur->name, "cid_name") == 0) {
249 value = record.caller_id_name;
250 } else if (strcmp(cur->name, "cid_num") == 0) {
251 value = record.caller_id_num;
252 } else if (strcmp(cur->name, "cid_ani") == 0) {
253 value = record.caller_id_ani;
254 } else if (strcmp(cur->name, "cid_rdnis") == 0) {
255 value = record.caller_id_rdnis;
256 } else if (strcmp(cur->name, "cid_dnid") == 0) {
257 value = record.caller_id_dnid;
258 } else if (strcmp(cur->name, "exten") == 0) {
259 value = record.extension;
260 } else if (strcmp(cur->name, "context") == 0) {
261 value = record.context;
262 } else if (strcmp(cur->name, "channame") == 0) {
263 value = record.channel_name;
264 } else if (strcmp(cur->name, "appname") == 0) {
265 value = record.application_name;
266 } else if (strcmp(cur->name, "appdata") == 0) {
267 value = record.application_data;
268 } else if (strcmp(cur->name, "accountcode") == 0) {
269 value = record.account_code;
270 } else if (strcmp(cur->name, "peeraccount") == 0) {
271 value = record.peer_account;
272 } else if (strcmp(cur->name, "uniqueid") == 0) {
273 value = record.unique_id;
274 } else if (strcmp(cur->name, "linkedid") == 0) {
275 value = record.linked_id;
276 } else if (strcmp(cur->name, "userfield") == 0) {
277 value = record.user_field;
278 } else if (strcmp(cur->name, "peer") == 0) {
279 value = record.peer;
280 } else if (strcmp(cur->name, "extra") == 0) {
281 value = record.extra;
282 } else {
283 value = NULL;
284 }
285
286 if (value == NULL) {
287 ast_str_append(&sql2, 0, "%sDEFAULT", SEP);
288 } else if (strncmp(cur->type, "int", 3) == 0) {
289 long long whatever;
290 if (value && sscanf(value, "%30lld", &whatever) == 1) {
291 LENGTHEN_BUF2(26);
292 ast_str_append(&sql2, 0, "%s%lld", SEP, whatever);
293 } else {
294 LENGTHEN_BUF2(2);
295 ast_str_append(&sql2, 0, "%s0", SEP);
296 }
297 } else if (strncmp(cur->type, "float", 5) == 0) {
298 long double whatever;
299 if (value && sscanf(value, "%30Lf", &whatever) == 1) {
300 LENGTHEN_BUF2(51);
301 ast_str_append(&sql2, 0, "%s%30Lf", SEP, whatever);
302 } else {
303 LENGTHEN_BUF2(2);
304 ast_str_append(&sql2, 0, "%s0", SEP);
305 }
306 /* XXX Might want to handle dates, times, and other misc fields here XXX */
307 } else {
308 if (value) {
309 size_t required_size = strlen(value) * 2 + 1;
310
311 /* If our argument size exceeds our buffer, grow it,
312 * as PQescapeStringConn() expects the buffer to be
313 * adequitely sized and does *NOT* do size checking.
314 */
315 if (required_size > bufsize) {
316 char *tmpbuf = ast_realloc(escapebuf, required_size);
317
318 if (!tmpbuf) {
320 goto ast_log_cleanup;
321 }
322
323 escapebuf = tmpbuf;
324 bufsize = required_size;
325 }
326 PQescapeStringConn(conn, escapebuf, value, strlen(value), NULL);
327 } else {
328 escapebuf[0] = '\0';
329 }
330 LENGTHEN_BUF2(strlen(escapebuf) + 3);
331 ast_str_append(&sql2, 0, "%s'%s'", SEP, escapebuf);
332 }
333 }
334 first = 0;
335 }
337 LENGTHEN_BUF1(ast_str_strlen(sql2) + 2);
338 ast_str_append(&sql, 0, ")%s)", ast_str_buffer(sql2));
339
340 ast_debug(3, "Inserting a CEL record: [%s].\n", ast_str_buffer(sql));
341 /* Test to be sure we're still connected... */
342 /* If we're connected, and connection is working, good. */
343 /* Otherwise, attempt reconnect. If it fails... sorry... */
344 if (PQstatus(conn) == CONNECTION_OK) {
345 connected = 1;
346 } else {
347 ast_log(LOG_WARNING, "Connection was lost... attempting to reconnect.\n");
348 PQreset(conn);
349 if (PQstatus(conn) == CONNECTION_OK) {
350 ast_log(LOG_NOTICE, "Connection reestablished.\n");
351 connected = 1;
352 } else {
353 pgerror = PQerrorMessage(conn);
354 ast_log(LOG_ERROR, "Unable to reconnect to database server %s. Calls will not be logged!\n", pghostname);
355 ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
356 PQfinish(conn);
357 conn = NULL;
358 connected = 0;
359 goto ast_log_cleanup;
360 }
361 }
362 result = PQexec(conn, ast_str_buffer(sql));
363 if (PQresultStatus(result) != PGRES_COMMAND_OK) {
364 pgerror = PQresultErrorMessage(result);
365 ast_log(LOG_WARNING, "Failed to insert call detail record into database!\n");
366 ast_log(LOG_WARNING, "Reason: %s\n", pgerror);
367 ast_log(LOG_WARNING, "Connection may have been lost... attempting to reconnect.\n");
368 PQreset(conn);
369 if (PQstatus(conn) == CONNECTION_OK) {
370 ast_log(LOG_NOTICE, "Connection reestablished.\n");
371 connected = 1;
372 PQclear(result);
373 result = PQexec(conn, ast_str_buffer(sql));
374 if (PQresultStatus(result) != PGRES_COMMAND_OK) {
375 pgerror = PQresultErrorMessage(result);
376 ast_log(LOG_ERROR, "HARD ERROR! Attempted reconnection failed. DROPPING CALL RECORD!\n");
377 ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
378 }
379 }
380 }
381 PQclear(result);
382
383 /* Next time, just allocate buffers that are that big to start with. */
384 if (ast_str_strlen(sql) > maxsize) {
385 maxsize = ast_str_strlen(sql);
386 }
387 if (ast_str_strlen(sql2) > maxsize2) {
388 maxsize2 = ast_str_strlen(sql2);
389 }
390
391ast_log_cleanup:
392 ast_free(sql);
393 ast_free(sql2);
394 ast_free(escapebuf);
395 }
396
398}
399
400static int my_unload_module(void)
401{
402 struct columns *current;
403
406 if (conn) {
407 PQfinish(conn);
408 conn = NULL;
409 }
410 if (pghostname) {
413 }
414 if (pgdbname) {
416 pgdbname = NULL;
417 }
418 if (pgdbuser) {
420 pgdbuser = NULL;
421 }
422 if (pgpassword) {
425 }
426 if (pgappname) {
428 pgappname = NULL;
429 }
430 if (pgdbport) {
432 pgdbport = NULL;
433 }
434 if (table) {
436 table = NULL;
437 }
438 if (schema) {
440 schema = NULL;
441 }
444 }
446 return 0;
447}
448
449static int unload_module(void)
450{
451 return my_unload_module();
452}
453
454static int process_my_load_module(struct ast_config *cfg)
455{
456 struct ast_variable *var;
457 char *pgerror;
458 const char *tmp;
459 PGresult *result;
460 struct columns *cur;
461
462 if (!(var = ast_variable_browse(cfg, "global"))) {
463 ast_log(LOG_WARNING,"CEL pgsql config file missing global section.\n");
465 }
466 if (!(tmp = ast_variable_retrieve(cfg,"global","hostname"))) {
467 ast_log(LOG_WARNING,"PostgreSQL server hostname not specified. Assuming unix socket connection\n");
468 tmp = ""; /* connect via UNIX-socket by default */
469 }
470 if (pghostname)
472 if (!(pghostname = ast_strdup(tmp))) {
473 ast_log(LOG_WARNING,"PostgreSQL Ran out of memory copying host info\n");
475 }
476 if (!(tmp = ast_variable_retrieve(cfg, "global", "dbname"))) {
477 ast_log(LOG_WARNING,"PostgreSQL database not specified. Assuming asterisk\n");
478 tmp = "asteriskceldb";
479 }
480 if (pgdbname)
482 if (!(pgdbname = ast_strdup(tmp))) {
483 ast_log(LOG_WARNING,"PostgreSQL Ran out of memory copying dbname info\n");
485 }
486 if (!(tmp = ast_variable_retrieve(cfg, "global", "user"))) {
487 ast_log(LOG_WARNING,"PostgreSQL database user not specified. Assuming asterisk\n");
488 tmp = "asterisk";
489 }
490 if (pgdbuser)
492 if (!(pgdbuser = ast_strdup(tmp))) {
493 ast_log(LOG_WARNING,"PostgreSQL Ran out of memory copying user info\n");
495 }
496 if (!(tmp = ast_variable_retrieve(cfg, "global", "password"))) {
497 ast_log(LOG_WARNING, "PostgreSQL database password not specified. Assuming blank\n");
498 tmp = "";
499 }
500 if (pgpassword)
502 if (!(pgpassword = ast_strdup(tmp))) {
503 ast_log(LOG_WARNING,"PostgreSQL Ran out of memory copying password info\n");
505 }
506 if (!(tmp = ast_variable_retrieve(cfg, "global", "appname"))) {
507 tmp = "";
508 }
509 if (pgappname) {
511 }
512 if (!(pgappname = ast_strdup(tmp))) {
513 ast_log(LOG_WARNING,"PostgreSQL Ran out of memory copying appname info\n");
515 }
516
517 if (!(tmp = ast_variable_retrieve(cfg,"global","port"))) {
518 ast_log(LOG_WARNING,"PostgreSQL database port not specified. Using default 5432.\n");
519 tmp = "5432";
520 }
521 if (pgdbport)
523 if (!(pgdbport = ast_strdup(tmp))) {
524 ast_log(LOG_WARNING,"PostgreSQL Ran out of memory copying port info\n");
526 }
527 if (!(tmp = ast_variable_retrieve(cfg, "global", "table"))) {
528 ast_log(LOG_WARNING,"CEL table not specified. Assuming cel\n");
529 tmp = "cel";
530 }
531 if (table)
533 if (!(table = ast_strdup(tmp))) {
535 }
537 if ((tmp = ast_variable_retrieve(cfg, "global", "show_user_defined"))) {
538 cel_show_user_def = ast_true(tmp) ? 1 : 0;
539 }
540 if ((tmp = ast_variable_retrieve(cfg, "global", "usegmtime"))) {
542 } else {
543 usegmtime = 0;
544 }
545 if (!(tmp = ast_variable_retrieve(cfg, "global", "schema"))) {
546 tmp = "";
547 }
548 if (schema) {
550 }
551 if (!(schema = ast_strdup(tmp))) {
552 ast_log(LOG_WARNING,"PostgreSQL Ran out of memory copying schema info\n");
554 }
555 if (DEBUG_ATLEAST(3)) {
557 ast_log(LOG_DEBUG, "cel_pgsql: using default unix socket\n");
558 } else {
559 ast_log(LOG_DEBUG, "cel_pgsql: got hostname of %s\n", pghostname);
560 }
561 ast_log(LOG_DEBUG, "cel_pgsql: got port of %s\n", pgdbport);
562 ast_log(LOG_DEBUG, "cel_pgsql: got user of %s\n", pgdbuser);
563 ast_log(LOG_DEBUG, "cel_pgsql: got dbname of %s\n", pgdbname);
564 ast_log(LOG_DEBUG, "cel_pgsql: got password of %s\n", pgpassword);
565 ast_log(LOG_DEBUG, "cel_pgsql: got sql table name of %s\n", table);
566 ast_log(LOG_DEBUG, "cel_pgsql: got show_user_defined of %s\n",
567 cel_show_user_def ? "Yes" : "No");
568 }
569
571 if (PQstatus(conn) != CONNECTION_BAD) {
572 char sqlcmd[768];
573 char *fname, *ftype, *flen, *fnotnull, *fdef, *tablename, *tmp_tablename;
574 int i, rows, version;
575
576 ast_debug(1, "Successfully connected to PostgreSQL database.\n");
577 connected = 1;
578
579 version = PQserverVersion(conn);
580 /* Remove any schema name from the table */
581 if ((tmp_tablename = strrchr(table, '.'))) {
582 tmp_tablename++;
583 } else {
584 tmp_tablename = table;
585 }
586 tablename = ast_alloca(strlen(tmp_tablename) * 2 + 1);
587 PQescapeStringConn(conn, tablename, tmp_tablename, strlen(tmp_tablename), NULL);
589 char *schemaname;
590 int lenschema;
591 lenschema = strlen(schema);
592 schemaname = ast_alloca(lenschema * 2 + 1);
593 PQescapeStringConn(conn, schemaname, schema, lenschema, NULL);
594
595 snprintf(sqlcmd, sizeof(sqlcmd),
596 "SELECT a.attname, t.typname, a.attlen, a.attnotnull, pg_catalog.pg_get_expr(d.adbin, d.adrelid) adsrc, a.atttypmod "
597 "FROM (((pg_catalog.pg_class c INNER JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace "
598 "AND c.relname = '%s' AND n.nspname = %s%s%s) "
599 "INNER JOIN pg_catalog.pg_attribute a ON ("
600 "NOT a.attisdropped) AND a.attnum > 0 AND a.attrelid = c.oid) "
601 "INNER JOIN pg_catalog.pg_type t ON t.oid = a.atttypid) "
602 "LEFT OUTER JOIN pg_attrdef d ON a.atthasdef AND d.adrelid = a.attrelid "
603 "AND d.adnum = a.attnum "
604 "ORDER BY n.nspname, c.relname, attnum",
605 tablename,
606 lenschema == 0 ? "" : "'", lenschema == 0 ? "current_schema()" : schemaname, lenschema == 0 ? "" : "'");
607 } else {
608 snprintf(sqlcmd, sizeof(sqlcmd),
609 "SELECT a.attname, t.typname, a.attlen, a.attnotnull, d.adsrc, a.atttypmod "
610 "FROM pg_class c, pg_type t, pg_attribute a "
611 "LEFT OUTER JOIN pg_attrdef d ON a.atthasdef AND d.adrelid = a.attrelid "
612 "AND d.adnum = a.attnum WHERE c.oid = a.attrelid AND a.atttypid = t.oid "
613 "AND (a.attnum > 0) AND c.relname = '%s' ORDER BY c.relname, attnum", tablename);
614 }
615 /* Query the columns */
616 result = PQexec(conn, sqlcmd);
617 if (PQresultStatus(result) != PGRES_TUPLES_OK) {
618 pgerror = PQresultErrorMessage(result);
619 ast_log(LOG_ERROR, "Failed to query database columns: %s\n", pgerror);
620 PQclear(result);
623 }
624
625 rows = PQntuples(result);
626 for (i = 0; i < rows; i++) {
627 fname = PQgetvalue(result, i, 0);
628 ftype = PQgetvalue(result, i, 1);
629 flen = PQgetvalue(result, i, 2);
630 fnotnull = PQgetvalue(result, i, 3);
631 fdef = PQgetvalue(result, i, 4);
632 ast_verb(4, "Found column '%s' of type '%s'\n", fname, ftype);
633 cur = ast_calloc(1, sizeof(*cur) + strlen(fname) + strlen(ftype) + 2);
634 if (cur) {
635 sscanf(flen, "%30d", &cur->len);
636 cur->name = (char *)cur + sizeof(*cur);
637 cur->type = (char *)cur + sizeof(*cur) + strlen(fname) + 1;
638 strcpy(cur->name, fname);
639 strcpy(cur->type, ftype);
640 if (*fnotnull == 't') {
641 cur->notnull = 1;
642 } else {
643 cur->notnull = 0;
644 }
645 if (!ast_strlen_zero(fdef)) {
646 cur->hasdefault = 1;
647 } else {
648 cur->hasdefault = 0;
649 }
651 }
652 }
653 PQclear(result);
654 } else {
655 pgerror = PQerrorMessage(conn);
656 ast_log(LOG_ERROR, "cel_pgsql: Unable to connect to database server %s. CALLS WILL NOT BE LOGGED!!\n", pghostname);
657 ast_log(LOG_ERROR, "cel_pgsql: Reason: %s\n", pgerror);
658 connected = 0;
659 PQfinish(conn);
660 conn = NULL;
661 }
663}
664
665static int my_load_module(int reload)
666{
667 struct ast_config *cfg;
668 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
669
670 if ((cfg = ast_config_load(config, config_flags)) == NULL || cfg == CONFIG_STATUS_FILEINVALID) {
671 ast_log(LOG_WARNING, "Unable to load config for PostgreSQL CEL's: %s\n", config);
673 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
675 }
676
677 if (reload) {
679 }
680
683
685 ast_log(LOG_WARNING, "Unable to subscribe to CEL events for pgsql\n");
687 }
688
690}
691
692static int load_module(void)
693{
694 return my_load_module(0);
695}
696
697static int reload(void)
698{
699 return my_load_module(1);
700}
701
703 .support_level = AST_MODULE_SUPPORT_EXTENDED,
704 .load = load_module,
705 .unload = unload_module,
706 .reload = reload,
707 .load_pri = AST_MODPRI_CDR_DRIVER,
708 .requires = "cel",
struct sla_ringing_trunk * first
Definition: app_sla.c:332
#define var
Definition: ast_expr2f.c:605
Asterisk main include file. File version handling, generic pbx functions.
#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_realloc(p, len)
A wrapper for realloc()
Definition: astmm.h:226
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:191
#define ast_log
Definition: astobj2.c:42
static int tmp()
Definition: bt_open.c:389
Call Event Logging API.
int ast_cel_backend_unregister(const char *name)
Unregister a CEL backend.
Definition: cel.c:1769
@ AST_CEL_USER_DEFINED
a user-defined event, the event name field should be set
Definition: cel.h:69
int ast_cel_fill_record(const struct ast_event *event, struct ast_cel_event_record *r)
Fill in an ast_cel_event_record from a CEL event.
Definition: cel.c:821
#define AST_CEL_EVENT_RECORD_VERSION
struct ABI version
Definition: cel.h:143
int ast_cel_backend_register(const char *name, ast_cel_backend_cb backend_callback)
Register a CEL backend.
Definition: cel.c:1781
#define LENGTHEN_BUF2(size)
Definition: cel_pgsql.c:113
static char * pgdbuser
Definition: cel_pgsql.c:63
static int maxsize2
Definition: cel_pgsql.c:72
static int maxsize
Definition: cel_pgsql.c:72
static int process_my_load_module(struct ast_config *cfg)
Definition: cel_pgsql.c:454
#define CEL_SHOW_USERDEF_DEFAULT
show_user_def is off by default
Definition: cel_pgsql.c:76
static void pgsql_reconnect(void)
Definition: cel_pgsql.c:116
static char * schema
Definition: cel_pgsql.c:68
static char * pgappname
Definition: cel_pgsql.c:65
static int my_load_module(int reload)
Definition: cel_pgsql.c:665
static char * config
Definition: cel_pgsql.c:59
#define SEP
static int my_unload_module(void)
Definition: cel_pgsql.c:400
static int connected
Definition: cel_pgsql.c:70
#define PGSQL_MIN_VERSION_SCHEMA
Definition: cel_pgsql.c:57
#define PGSQL_BACKEND_NAME
Definition: cel_pgsql.c:55
static ast_mutex_t pgsql_lock
Definition: cel_pgsql.c:81
static char * pgdbport
Definition: cel_pgsql.c:66
static unsigned char cel_show_user_def
Definition: cel_pgsql.c:79
static char * pgdbname
Definition: cel_pgsql.c:62
static void pgsql_log(struct ast_event *event)
Definition: cel_pgsql.c:145
static char * table
Definition: cel_pgsql.c:67
static int usegmtime
Definition: cel_pgsql.c:73
static int load_module(void)
Definition: cel_pgsql.c:692
static char * pghostname
Definition: cel_pgsql.c:61
static char * pgpassword
Definition: cel_pgsql.c:64
static int unload_module(void)
Definition: cel_pgsql.c:449
static int reload(void)
Definition: cel_pgsql.c:697
static PGresult * result
Definition: cel_pgsql.c:84
#define DATE_FORMAT
Definition: cel_pgsql.c:53
static PGconn * conn
Definition: cel_pgsql.c:83
#define LENGTHEN_BUF1(size)
Definition: cel_pgsql.c:111
static char version[AST_MAX_EXTENSION]
Definition: chan_ooh323.c:391
General Asterisk PBX channel definitions.
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
Configuration File Parser.
#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
Support for logging to various files, console and syslog Configuration in file logger....
#define DEBUG_ATLEAST(level)
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_DEBUG
#define LOG_ERROR
#define ast_verb(level,...)
#define LOG_NOTICE
#define LOG_WARNING
#define AST_RWLIST_RDLOCK(head)
Read locks a list.
Definition: linkedlists.h:78
#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_HEAD_STATIC(name, type)
Defines a structure to be used to hold a read/write list of specified type, statically initialized.
Definition: linkedlists.h:333
#define AST_RWLIST_REMOVE_HEAD
Definition: linkedlists.h:844
#define AST_RWLIST_TRAVERSE
Definition: linkedlists.h:494
#define AST_RWLIST_INSERT_TAIL
Definition: linkedlists.h:741
#define AST_RWLIST_ENTRY
Definition: linkedlists.h:415
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
#define ast_mutex_unlock(a)
Definition: lock.h:190
#define ast_mutex_lock(a)
Definition: lock.h:189
#define AST_MUTEX_DEFINE_STATIC(mutex)
Definition: lock.h:520
size_t current
Definition: main/cli.c:113
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition: module.h:317
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:543
@ AST_MODPRI_CDR_DRIVER
Definition: module.h:331
@ AST_MODULE_SUPPORT_EXTENDED
Definition: module.h:122
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
Options provided by main asterisk program.
#define NULL
Definition: resample.c:96
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
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true"....
Definition: utils.c:2199
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
#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
Helper struct for getting the fields out of a CEL event.
Definition: cel.h:138
const char * caller_id_dnid
Definition: cel.h:157
const char * application_data
Definition: cel.h:162
const char * account_code
Definition: cel.h:163
const char * caller_id_rdnis
Definition: cel.h:156
const char * extra
Definition: cel.h:170
const char * extension
Definition: cel.h:158
const char * caller_id_num
Definition: cel.h:154
const char * channel_name
Definition: cel.h:160
const char * linked_id
Definition: cel.h:166
const char * peer_account
Definition: cel.h:164
const char * peer
Definition: cel.h:169
enum ast_cel_event_type event_type
Definition: cel.h:149
const char * unique_id
Definition: cel.h:165
const char * user_defined_name
Definition: cel.h:152
const char * context
Definition: cel.h:159
const char * application_name
Definition: cel.h:161
struct timeval event_time
Definition: cel.h:150
uint32_t version
struct ABI version
Definition: cel.h:148
const char * user_field
Definition: cel.h:168
const char * caller_id_ani
Definition: cel.h:155
const char * caller_id_name
Definition: cel.h:153
const char * event_name
Definition: cel.h:151
An event.
Definition: event.c:81
Structure used to handle boolean flags.
Definition: utils.h:199
Support for dynamic strings.
Definition: strings.h:623
Structure for variables, used for configurations and for channel variables.
unsigned int hasdefault
Definition: cdr_pgsql.c:94
struct columns::@4 list
unsigned int notnull
Definition: cdr_pgsql.c:93
Definition: astman.c:222
int value
Definition: syslog.c:37