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