Asterisk - The Open Source Telephony Project  18.5.0
Data Structures | Macros | Functions | Variables
cel_odbc.c File Reference

ODBC CEL backend. More...

#include "asterisk.h"
#include <sys/types.h>
#include <time.h>
#include <math.h>
#include <sql.h>
#include <sqlext.h>
#include <sqltypes.h>
#include "asterisk/config.h"
#include "asterisk/channel.h"
#include "asterisk/lock.h"
#include "asterisk/linkedlists.h"
#include "asterisk/res_odbc.h"
#include "asterisk/cel.h"
#include "asterisk/module.h"
Include dependency graph for cel_odbc.c:

Go to the source code of this file.

Data Structures

struct  columns
 
struct  tables::odbc_columns
 
struct  odbc_tables
 
struct  tables
 

Macros

#define CEL_SHOW_USERDEF_DEFAULT   0
 show_user_def is off by default More...
 
#define CONFIG   "cel_odbc.conf"
 
#define LENGTHEN_BUF(size, var_sql)
 
#define LENGTHEN_BUF1(size)   LENGTHEN_BUF(size, sql);
 
#define LENGTHEN_BUF2(size)   LENGTHEN_BUF(size, sql2);
 
#define ODBC_BACKEND_NAME   "ODBC CEL backend"
 

Functions

static void __reg_module (void)
 
static void __unreg_module (void)
 
struct ast_moduleAST_MODULE_SELF_SYM (void)
 
static int free_config (void)
 
static SQLHSTMT generic_prepare (struct odbc_obj *obj, void *data)
 
static int load_config (void)
 
static int load_module (void)
 
static void odbc_log (struct ast_event *event)
 
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 = "ODBC CEL 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 = "30ef0c93b36035ec78c9cfd712d36d9b" , .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CDR_DRIVER, .requires = "cel,res_odbc", }
 
static const struct ast_module_infoast_module_info = &__mod_info
 
static unsigned char cel_show_user_def
 
static int maxsize = 512
 
static int maxsize2 = 512
 
static struct odbc_tables odbc_tables = { .first = NULL, .last = NULL, .lock = { PTHREAD_RWLOCK_INITIALIZER , NULL, {1, 0} } , }
 

Detailed Description

ODBC CEL backend.

Author
Tilghman Lesher
<tlesher AT digium DOT com> 

Definition in file cel_odbc.c.

Macro Definition Documentation

◆ CEL_SHOW_USERDEF_DEFAULT

#define CEL_SHOW_USERDEF_DEFAULT   0

show_user_def is off by default

Definition at line 58 of file cel_odbc.c.

Referenced by load_config().

◆ CONFIG

#define CONFIG   "cel_odbc.conf"

Definition at line 53 of file cel_odbc.c.

Referenced by load_config().

◆ LENGTHEN_BUF

#define LENGTHEN_BUF (   size,
  var_sql 
)

Definition at line 345 of file cel_odbc.c.

◆ LENGTHEN_BUF1

#define LENGTHEN_BUF1 (   size)    LENGTHEN_BUF(size, sql);

Definition at line 359 of file cel_odbc.c.

Referenced by odbc_log().

◆ LENGTHEN_BUF2

#define LENGTHEN_BUF2 (   size)    LENGTHEN_BUF(size, sql2);

Definition at line 362 of file cel_odbc.c.

Referenced by odbc_log().

◆ ODBC_BACKEND_NAME

#define ODBC_BACKEND_NAME   "ODBC CEL backend"

Definition at line 55 of file cel_odbc.c.

Referenced by load_module(), and unload_module().

Function Documentation

◆ __reg_module()

static void __reg_module ( void  )
static

Definition at line 860 of file cel_odbc.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

Definition at line 860 of file cel_odbc.c.

◆ AST_MODULE_SELF_SYM()

struct ast_module* AST_MODULE_SELF_SYM ( void  )

Definition at line 860 of file cel_odbc.c.

◆ free_config()

static int free_config ( void  )
static

Definition at line 298 of file cel_odbc.c.

References ast_free, AST_LIST_REMOVE_HEAD, AST_RWLIST_REMOVE_HEAD, tables::columns, columns::list, and table.

Referenced by load_module(), reload(), and unload_module().

299 {
300  struct tables *table;
301  struct columns *entry;
302  while ((table = AST_RWLIST_REMOVE_HEAD(&odbc_tables, list))) {
303  while ((entry = AST_LIST_REMOVE_HEAD(&(table->columns), list))) {
304  ast_free(entry);
305  }
306  ast_free(table);
307  }
308  return 0;
309 }
struct columns::@8 list
static char * table
Definition: cdr_odbc.c:58
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:832
#define ast_free(a)
Definition: astmm.h:182
#define AST_RWLIST_REMOVE_HEAD
Definition: linkedlists.h:843
struct tables::mysql_columns columns
Definition: search.h:40

◆ generic_prepare()

static SQLHSTMT generic_prepare ( struct odbc_obj obj,
void *  data 
)
static

Definition at line 311 of file cel_odbc.c.

References ast_log, ast_odbc_prepare(), odbc_obj::con, LOG_WARNING, and NULL.

Referenced by odbc_log().

312 {
313  int res, i;
314  char *sql = data;
315  SQLHSTMT stmt;
316  SQLINTEGER nativeerror = 0, numfields = 0;
317  SQLSMALLINT diagbytes = 0;
318  unsigned char state[10], diagnostic[256];
319 
320  res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
321  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
322  ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
323  return NULL;
324  }
325 
326  res = ast_odbc_prepare(obj, stmt, sql);
327  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
328  ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
329  SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
330  for (i = 0; i < numfields; i++) {
331  SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
332  ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
333  if (i > 10) {
334  ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
335  break;
336  }
337  }
338  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
339  return NULL;
340  }
341 
342  return stmt;
343 }
SQLHDBC con
Definition: res_odbc.h:47
#define LOG_WARNING
Definition: logger.h:274
int ast_odbc_prepare(struct odbc_obj *obj, SQLHSTMT *stmt, const char *sql)
Prepares a SQL query on a statement.
Definition: res_odbc.c:463
#define NULL
Definition: resample.c:96
#define ast_log
Definition: astobj2.c:42

◆ load_config()

static int load_config ( void  )
static

Definition at line 91 of file cel_odbc.c.

References tables::allowleapsec, ast_calloc, ast_category_browse(), ast_config_destroy(), ast_config_load, ast_copy_string(), ast_free, AST_LIST_FIRST, AST_LIST_INSERT_TAIL, ast_log, ast_odbc_release_obj(), ast_odbc_request_obj, AST_RWLIST_INSERT_TAIL, ast_strdupa, ast_strip(), ast_strlen_zero, ast_true(), ast_variable_browse(), ast_variable_retrieve(), ast_verb, cel_show_user_def, CEL_SHOW_USERDEF_DEFAULT, columns::celname, tables::columns, odbc_obj::con, CONFIG, CONFIG_STATUS_FILEINVALID, tables::connection, columns::decimals, columns::filtervalue, item, columns::list, LOG_ERROR, LOG_NOTICE, LOG_WARNING, ast_variable::name, columns::name, ast_variable::next, NULL, columns::nullable, columns::octetlen, columns::radix, columns::size, columns::staticvalue, table, tables::table, tmp(), columns::type, tables::usegmtime, ast_variable::value, and var.

Referenced by load_module(), and reload().

92 {
93  struct ast_config *cfg;
94  struct ast_variable *var;
95  const char *tmp, *catg;
96  struct tables *tableptr;
97  struct columns *entry;
98  struct odbc_obj *obj;
99  char columnname[80];
100  char connection[40];
101  char table[40];
102  int lenconnection, lentable;
103  SQLLEN sqlptr;
104  int res = 0;
105  SQLHSTMT stmt = NULL;
106  struct ast_flags config_flags = { 0 }; /* Part of our config comes from the database */
107 
108  cfg = ast_config_load(CONFIG, config_flags);
109  if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
110  ast_log(LOG_WARNING, "Unable to load " CONFIG ". No ODBC CEL records!\n");
111  return -1;
112  }
113 
114  /* Process the general category */
116  for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
117  if (!strcasecmp(var->name, "show_user_defined")) {
118  cel_show_user_def = ast_true(var->value) ? 1 : 0;
119  } else {
120  /* Unknown option name. */
121  }
122  }
123 
124  for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) {
125  if (!strcasecmp(catg, "general")) {
126  continue;
127  }
128  var = ast_variable_browse(cfg, catg);
129  if (!var)
130  continue;
131 
132  if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "connection"))) {
133  ast_log(LOG_WARNING, "No connection parameter found in '%s'. Skipping.\n", catg);
134  continue;
135  }
136  ast_copy_string(connection, tmp, sizeof(connection));
137  lenconnection = strlen(connection);
138 
139  /* When loading, we want to be sure we can connect. */
140  obj = ast_odbc_request_obj(connection, 1);
141  if (!obj) {
142  ast_log(LOG_WARNING, "No such connection '%s' in the '%s' section of " CONFIG ". Check res_odbc.conf.\n", connection, catg);
143  continue;
144  }
145 
146  if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "table"))) {
147  ast_log(LOG_NOTICE, "No table name found. Assuming 'cel'.\n");
148  tmp = "cel";
149  }
150  ast_copy_string(table, tmp, sizeof(table));
151  lentable = strlen(table);
152 
153  res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
154  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
155  ast_log(LOG_WARNING, "SQL Alloc Handle failed on connection '%s'!\n", connection);
157  continue;
158  }
159 
160  res = SQLColumns(stmt, NULL, 0, NULL, 0, (unsigned char *)table, SQL_NTS, (unsigned char *)"%", SQL_NTS);
161  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
162  ast_log(LOG_ERROR, "Unable to query database columns on connection '%s'. Skipping.\n", connection);
164  continue;
165  }
166 
167  tableptr = ast_calloc(sizeof(char), sizeof(*tableptr) + lenconnection + 1 + lentable + 1);
168  if (!tableptr) {
169  ast_log(LOG_ERROR, "Out of memory creating entry for table '%s' on connection '%s'\n", table, connection);
171  res = -1;
172  break;
173  }
174 
175  tableptr->connection = (char *)tableptr + sizeof(*tableptr);
176  tableptr->table = (char *)tableptr + sizeof(*tableptr) + lenconnection + 1;
177  ast_copy_string(tableptr->connection, connection, lenconnection + 1);
178  ast_copy_string(tableptr->table, table, lentable + 1);
179 
180  tableptr->usegmtime = 0;
181  if (!ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "usegmtime"))) {
182  tableptr->usegmtime = ast_true(tmp);
183  }
184 
185  tableptr->allowleapsec = 1;
186  if (!ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "allowleapsecond"))) {
187  tableptr->allowleapsec = ast_true(tmp);
188  }
189 
190  ast_verb(3, "Found CEL table %s@%s.\n", tableptr->table, tableptr->connection);
191 
192  /* Check for filters first */
193  for (var = ast_variable_browse(cfg, catg); var; var = var->next) {
194  if (strncmp(var->name, "filter", 6) == 0) {
195  char *celvar = ast_strdupa(var->name + 6);
196  celvar = ast_strip(celvar);
197  ast_verb(3, "Found filter %s for cel variable %s in %s@%s\n", var->value, celvar, tableptr->table, tableptr->connection);
198 
199  entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(celvar) + 1 + strlen(var->value) + 1);
200  if (!entry) {
201  ast_log(LOG_ERROR, "Out of memory creating filter entry for CEL variable '%s' in table '%s' on connection '%s'\n", celvar, table, connection);
202  res = -1;
203  break;
204  }
205 
206  /* NULL column entry means this isn't a column in the database */
207  entry->name = NULL;
208  entry->celname = (char *)entry + sizeof(*entry);
209  entry->filtervalue = (char *)entry + sizeof(*entry) + strlen(celvar) + 1;
210  strcpy(entry->celname, celvar);
211  strcpy(entry->filtervalue, var->value);
212 
213  AST_LIST_INSERT_TAIL(&(tableptr->columns), entry, list);
214  }
215  }
216 
217  while ((res = SQLFetch(stmt)) != SQL_NO_DATA && res != SQL_ERROR) {
218  char *celvar = "", *staticvalue = "";
219 
220  SQLGetData(stmt, 4, SQL_C_CHAR, columnname, sizeof(columnname), &sqlptr);
221 
222  /* Is there an alias for this column? */
223 
224  /* NOTE: This seems like a non-optimal parse method, but I'm going
225  * for user configuration readability, rather than fast parsing. We
226  * really don't parse this file all that often, anyway.
227  */
228  for (var = ast_variable_browse(cfg, catg); var; var = var->next) {
229  if (strncmp(var->name, "alias", 5) == 0 && strcasecmp(var->value, columnname) == 0) {
230  char *alias = ast_strdupa(var->name + 5);
231  celvar = ast_strip(alias);
232  ast_verb(3, "Found alias %s for column %s in %s@%s\n", celvar, columnname, tableptr->table, tableptr->connection);
233  break;
234  } else if (strncmp(var->name, "static", 6) == 0 && strcasecmp(var->value, columnname) == 0) {
235  char *item = ast_strdupa(var->name + 6);
236  item = ast_strip(item);
237  if (item[0] == '"' && item[strlen(item) - 1] == '"') {
238  /* Remove surrounding quotes */
239  item[strlen(item) - 1] = '\0';
240  item++;
241  }
242  staticvalue = item;
243  }
244  }
245 
246  entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(columnname) + 1 + strlen(celvar) + 1 + strlen(staticvalue) + 1);
247  if (!entry) {
248  ast_log(LOG_ERROR, "Out of memory creating entry for column '%s' in table '%s' on connection '%s'\n", columnname, table, connection);
249  res = -1;
250  break;
251  }
252  entry->name = (char *)entry + sizeof(*entry);
253  strcpy(entry->name, columnname);
254 
255  if (!ast_strlen_zero(celvar)) {
256  entry->celname = entry->name + strlen(columnname) + 1;
257  strcpy(entry->celname, celvar);
258  } else { /* Point to same place as the column name */
259  entry->celname = (char *)entry + sizeof(*entry);
260  }
261 
262  if (!ast_strlen_zero(staticvalue)) {
263  entry->staticvalue = entry->celname + strlen(entry->celname) + 1;
264  strcpy(entry->staticvalue, staticvalue);
265  }
266 
267  SQLGetData(stmt, 5, SQL_C_SHORT, &entry->type, sizeof(entry->type), NULL);
268  SQLGetData(stmt, 7, SQL_C_LONG, &entry->size, sizeof(entry->size), NULL);
269  SQLGetData(stmt, 9, SQL_C_SHORT, &entry->decimals, sizeof(entry->decimals), NULL);
270  SQLGetData(stmt, 10, SQL_C_SHORT, &entry->radix, sizeof(entry->radix), NULL);
271  SQLGetData(stmt, 11, SQL_C_SHORT, &entry->nullable, sizeof(entry->nullable), NULL);
272  SQLGetData(stmt, 16, SQL_C_LONG, &entry->octetlen, sizeof(entry->octetlen), NULL);
273 
274  /* Specification states that the octenlen should be the maximum number of bytes
275  * returned in a char or binary column, but it seems that some drivers just set
276  * it to NULL. (Bad Postgres! No biscuit!) */
277  if (entry->octetlen == 0)
278  entry->octetlen = entry->size;
279 
280  ast_verb(10, "Found %s column with type %hd with len %ld, octetlen %ld, and numlen (%hd,%hd)\n", entry->name, entry->type, (long) entry->size, (long) entry->octetlen, entry->decimals, entry->radix);
281  /* Insert column info into column list */
282  AST_LIST_INSERT_TAIL(&(tableptr->columns), entry, list);
283  res = 0;
284  }
285 
286  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
288 
289  if (AST_LIST_FIRST(&(tableptr->columns)))
290  AST_RWLIST_INSERT_TAIL(&odbc_tables, tableptr, list);
291  else
292  ast_free(tableptr);
293  }
294  ast_config_destroy(cfg);
295  return res;
296 }
SQLHDBC con
Definition: res_odbc.h:47
struct ast_variable * next
SQLSMALLINT radix
#define AST_LIST_FIRST(head)
Returns the first entry contained in a list.
Definition: linkedlists.h:420
char * filtervalue
static unsigned char cel_show_user_def
Definition: cel_odbc.c:61
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category_name)
Definition: extconf.c:1216
#define LOG_WARNING
Definition: logger.h:274
#define CONFIG_STATUS_FILEINVALID
SQLSMALLINT nullable
static int tmp()
Definition: bt_open.c:389
Structure for variables, used for configurations and for channel variables.
#define var
Definition: ast_expr2f.c:614
static struct aco_type item
Definition: test_config.c:1463
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition: extconf.c:3328
SQLINTEGER octetlen
#define NULL
Definition: resample.c:96
unsigned int allowleapsec
Definition: cel_odbc.c:84
#define ast_verb(level,...)
Definition: logger.h:463
#define ast_strlen_zero(foo)
Definition: strings.h:52
char * celname
Definition: cel_odbc.c:68
static char * table
Definition: cdr_odbc.c:58
#define ast_log
Definition: astobj2.c:42
SQLSMALLINT decimals
#define ast_config_load(filename, flags)
Load a config file.
#define CEL_SHOW_USERDEF_DEFAULT
show_user_def is off by default
Definition: cel_odbc.c:58
SQLINTEGER size
ODBC container.
Definition: res_odbc.h:46
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:219
void ast_config_destroy(struct ast_config *config)
Destroys a config.
Definition: extconf.c:1290
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
#define CONFIG
Definition: cel_odbc.c:53
char * connection
#define LOG_ERROR
Definition: logger.h:285
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:730
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
#define LOG_NOTICE
Definition: logger.h:263
#define ast_free(a)
Definition: astmm.h:182
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
#define ast_odbc_request_obj(a, b)
Definition: res_odbc.h:122
Structure used to handle boolean flags.
Definition: utils.h:199
char * table
const char * ast_variable_retrieve(struct ast_config *config, const char *category, const char *variable)
Definition: main/config.c:694
#define AST_RWLIST_INSERT_TAIL
Definition: linkedlists.h:740
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
struct tables::mysql_columns columns
char * staticvalue
Definition: search.h:40
void ast_odbc_release_obj(struct odbc_obj *obj)
Releases an ODBC object previously allocated by ast_odbc_request_obj()
Definition: res_odbc.c:813
unsigned int usegmtime

◆ load_module()

static int load_module ( void  )
static

Definition at line 822 of file cel_odbc.c.

References ast_cel_backend_register(), ast_log, AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_SUCCESS, AST_RWLIST_HEAD_INIT, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, free_config(), load_config(), LOG_ERROR, ODBC_BACKEND_NAME, and odbc_log().

Referenced by reload().

823 {
825 
827  ast_log(LOG_ERROR, "Unable to lock column list. Load failed.\n");
829  }
830  load_config();
833  ast_log(LOG_ERROR, "Unable to subscribe to CEL events\n");
834  free_config();
836  }
838 }
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
#define AST_RWLIST_UNLOCK(head)
Attempts to unlock a read/write based list.
Definition: linkedlists.h:150
static void odbc_log(struct ast_event *event)
Definition: cel_odbc.c:365
#define AST_RWLIST_HEAD_INIT(head)
Initializes an rwlist head structure.
Definition: linkedlists.h:638
#define ast_log
Definition: astobj2.c:42
static int free_config(void)
Definition: cel_odbc.c:298
#define LOG_ERROR
Definition: logger.h:285
#define ODBC_BACKEND_NAME
Definition: cel_odbc.c:55
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
static int load_config(void)
Definition: cel_odbc.c:91

◆ odbc_log()

static void odbc_log ( struct ast_event event)
static

Definition at line 365 of file cel_odbc.c.

References ast_cel_event_record::account_code, tables::allowleapsec, ast_cel_event_record::amaflag, ast_cel_event_record::application_data, ast_cel_event_record::application_name, AST_CEL_EVENT_RECORD_VERSION, ast_cel_fill_record(), AST_CEL_USER_DEFINED, ast_copy_string(), ast_debug, ast_free, AST_LIST_TRAVERSE, ast_localtime(), ast_log, ast_odbc_backslash_is_escape(), ast_odbc_prepare_and_execute(), ast_odbc_release_obj(), ast_odbc_request_obj, AST_RWLIST_RDLOCK, AST_RWLIST_UNLOCK, ast_str_append(), ast_str_buffer(), ast_str_create, ast_str_set(), ast_str_strlen(), ast_strdupa, ast_strftime(), ast_strlen_zero, ast_verb, ast_cel_event_record::caller_id_ani, ast_cel_event_record::caller_id_dnid, ast_cel_event_record::caller_id_name, ast_cel_event_record::caller_id_num, ast_cel_event_record::caller_id_rdnis, cel_show_user_def, columns::celname, ast_cel_event_record::channel_name, tables::columns, tables::connection, ast_cel_event_record::context, columns::decimals, ast_cel_event_record::event_name, ast_cel_event_record::event_time, ast_cel_event_record::event_type, ast_cel_event_record::extension, ast_cel_event_record::extra, columns::filtervalue, generic_prepare(), LENGTHEN_BUF1, LENGTHEN_BUF2, ast_cel_event_record::linked_id, columns::list, LOG_ERROR, LOG_WARNING, maxsize, maxsize2, columns::name, NULL, columns::octetlen, ast_cel_event_record::peer, ast_cel_event_record::peer_account, columns::radix, columns::staticvalue, tables::table, ast_tm::tm_hour, ast_tm::tm_mday, ast_tm::tm_min, ast_tm::tm_mon, ast_tm::tm_sec, ast_tm::tm_usec, ast_tm::tm_year, tmp(), columns::type, ast_cel_event_record::unique_id, unknown, tables::usegmtime, ast_cel_event_record::user_defined_name, ast_cel_event_record::user_field, and ast_cel_event_record::version.

Referenced by load_module().

366 {
367  struct tables *tableptr;
368  struct columns *entry;
369  struct odbc_obj *obj;
370  struct ast_str *sql = ast_str_create(maxsize), *sql2 = ast_str_create(maxsize2);
371  char *tmp;
372  char colbuf[1024], *colptr;
373  SQLHSTMT stmt = NULL;
374  SQLLEN rows = 0;
375  struct ast_cel_event_record record = {
377  };
378 
379  if (ast_cel_fill_record(event, &record)) {
380  return;
381  }
382 
383  if (!sql || !sql2) {
384  if (sql)
385  ast_free(sql);
386  if (sql2)
387  ast_free(sql2);
388  return;
389  }
390 
392  ast_log(LOG_ERROR, "Unable to lock table list. Insert CEL(s) failed.\n");
393  ast_free(sql);
394  ast_free(sql2);
395  return;
396  }
397 
398  AST_LIST_TRAVERSE(&odbc_tables, tableptr, list) {
399  char *separator = "";
400  ast_str_set(&sql, 0, "INSERT INTO %s (", tableptr->table);
401  ast_str_set(&sql2, 0, " VALUES (");
402 
403  /* No need to check the connection now; we'll handle any failure in prepare_and_execute */
404  if (!(obj = ast_odbc_request_obj(tableptr->connection, 0))) {
405  ast_log(LOG_WARNING, "Unable to retrieve database handle for '%s:%s'. CEL failed: %s\n", tableptr->connection, tableptr->table, ast_str_buffer(sql));
406  continue;
407  }
408 
409  AST_LIST_TRAVERSE(&(tableptr->columns), entry, list) {
410  int datefield = 0;
411  int unknown = 0;
412  if (strcasecmp(entry->celname, "eventtime") == 0) {
413  datefield = 1;
414  }
415 
416  /* Check if we have a similarly named variable */
417  if (entry->staticvalue) {
418  colptr = ast_strdupa(entry->staticvalue);
419  } else if (datefield) {
420  struct timeval date_tv = record.event_time;
421  struct ast_tm tm = { 0, };
422  ast_localtime(&date_tv, &tm, tableptr->usegmtime ? "UTC" : NULL);
423  /* SQL server 2008 added datetime2 and datetimeoffset data types, that
424  are reported to SQLColumns() as SQL_WVARCHAR, according to "Enhanced
425  Date/Time Type Behavior with Previous SQL Server Versions (ODBC)".
426  Here we format the event time with fraction seconds, so these new
427  column types will be set to high-precision event time. However, 'date'
428  and 'time' columns, also newly introduced, reported as SQL_WVARCHAR
429  too, and insertion of the value formatted here into these will fail.
430  This should be ok, however, as nobody is going to store just event
431  date or just time for CDR purposes.
432  */
433  ast_strftime(colbuf, sizeof(colbuf), "%Y-%m-%d %H:%M:%S.%6q", &tm);
434  colptr = colbuf;
435  } else {
436  if (strcmp(entry->celname, "userdeftype") == 0) {
437  ast_copy_string(colbuf, record.user_defined_name, sizeof(colbuf));
438  } else if (strcmp(entry->celname, "cid_name") == 0) {
439  ast_copy_string(colbuf, record.caller_id_name, sizeof(colbuf));
440  } else if (strcmp(entry->celname, "cid_num") == 0) {
441  ast_copy_string(colbuf, record.caller_id_num, sizeof(colbuf));
442  } else if (strcmp(entry->celname, "cid_ani") == 0) {
443  ast_copy_string(colbuf, record.caller_id_ani, sizeof(colbuf));
444  } else if (strcmp(entry->celname, "cid_rdnis") == 0) {
445  ast_copy_string(colbuf, record.caller_id_rdnis, sizeof(colbuf));
446  } else if (strcmp(entry->celname, "cid_dnid") == 0) {
447  ast_copy_string(colbuf, record.caller_id_dnid, sizeof(colbuf));
448  } else if (strcmp(entry->celname, "exten") == 0) {
449  ast_copy_string(colbuf, record.extension, sizeof(colbuf));
450  } else if (strcmp(entry->celname, "context") == 0) {
451  ast_copy_string(colbuf, record.context, sizeof(colbuf));
452  } else if (strcmp(entry->celname, "channame") == 0) {
453  ast_copy_string(colbuf, record.channel_name, sizeof(colbuf));
454  } else if (strcmp(entry->celname, "appname") == 0) {
455  ast_copy_string(colbuf, record.application_name, sizeof(colbuf));
456  } else if (strcmp(entry->celname, "appdata") == 0) {
457  ast_copy_string(colbuf, record.application_data, sizeof(colbuf));
458  } else if (strcmp(entry->celname, "accountcode") == 0) {
459  ast_copy_string(colbuf, record.account_code, sizeof(colbuf));
460  } else if (strcmp(entry->celname, "peeraccount") == 0) {
461  ast_copy_string(colbuf, record.peer_account, sizeof(colbuf));
462  } else if (strcmp(entry->celname, "uniqueid") == 0) {
463  ast_copy_string(colbuf, record.unique_id, sizeof(colbuf));
464  } else if (strcmp(entry->celname, "linkedid") == 0) {
465  ast_copy_string(colbuf, record.linked_id, sizeof(colbuf));
466  } else if (strcmp(entry->celname, "userfield") == 0) {
467  ast_copy_string(colbuf, record.user_field, sizeof(colbuf));
468  } else if (strcmp(entry->celname, "peer") == 0) {
469  ast_copy_string(colbuf, record.peer, sizeof(colbuf));
470  } else if (strcmp(entry->celname, "amaflags") == 0) {
471  snprintf(colbuf, sizeof(colbuf), "%u", record.amaflag);
472  } else if (strcmp(entry->celname, "extra") == 0) {
473  ast_copy_string(colbuf, record.extra, sizeof(colbuf));
474  } else if (strcmp(entry->celname, "eventtype") == 0) {
475  snprintf(colbuf, sizeof(colbuf), "%u", record.event_type);
476  } else {
477  colbuf[0] = 0;
478  unknown = 1;
479  }
480  colptr = colbuf;
481  }
482 
483  if (colptr && !unknown) {
484  /* Check first if the column filters this entry. Note that this
485  * is very specifically NOT ast_strlen_zero(), because the filter
486  * could legitimately specify that the field is blank, which is
487  * different from the field being unspecified (NULL). */
488  if (entry->filtervalue && strcasecmp(colptr, entry->filtervalue) != 0) {
489  ast_verb(4, "CEL column '%s' with value '%s' does not match filter of"
490  " '%s'. Cancelling this CEL.\n",
491  entry->celname, colptr, entry->filtervalue);
492  goto early_release;
493  }
494 
495  /* Only a filter? */
496  if (ast_strlen_zero(entry->name))
497  continue;
498 
499  LENGTHEN_BUF1(strlen(entry->name));
500 
501  switch (entry->type) {
502  case SQL_CHAR:
503  case SQL_VARCHAR:
504  case SQL_LONGVARCHAR:
505 #ifdef HAVE_ODBC_WCHAR
506  case SQL_WCHAR:
507  case SQL_WVARCHAR:
508  case SQL_WLONGVARCHAR:
509 #endif
510  case SQL_BINARY:
511  case SQL_VARBINARY:
512  case SQL_LONGVARBINARY:
513  case SQL_GUID:
514  /* For these two field names, get the rendered form, instead of the raw
515  * form (but only when we're dealing with a character-based field).
516  */
517  if (strcasecmp(entry->name, "eventtype") == 0) {
518  const char *event_name;
519 
520  event_name = (!cel_show_user_def
521  && record.event_type == AST_CEL_USER_DEFINED)
522  ? record.user_defined_name : record.event_name;
523  snprintf(colbuf, sizeof(colbuf), "%s", event_name);
524  }
525 
526  /* Truncate too-long fields */
527  if (entry->type != SQL_GUID) {
528  if (strlen(colptr) > entry->octetlen) {
529  colptr[entry->octetlen] = '\0';
530  }
531  }
532 
533  ast_str_append(&sql, 0, "%s%s", separator, entry->name);
534  LENGTHEN_BUF2(strlen(colptr));
535 
536  /* Encode value, with escaping */
537  ast_str_append(&sql2, 0, "%s'", separator);
538  for (tmp = colptr; *tmp; tmp++) {
539  if (*tmp == '\'') {
540  ast_str_append(&sql2, 0, "''");
541  } else if (*tmp == '\\' && ast_odbc_backslash_is_escape(obj)) {
542  ast_str_append(&sql2, 0, "\\\\");
543  } else {
544  ast_str_append(&sql2, 0, "%c", *tmp);
545  }
546  }
547  ast_str_append(&sql2, 0, "'");
548  break;
549  case SQL_TYPE_DATE:
550  if (ast_strlen_zero(colptr)) {
551  continue;
552  } else {
553  int year = 0, month = 0, day = 0;
554  if (strcasecmp(entry->name, "eventdate") == 0) {
555  struct ast_tm tm;
556  ast_localtime(&record.event_time, &tm, tableptr->usegmtime ? "UTC" : NULL);
557  year = tm.tm_year + 1900;
558  month = tm.tm_mon + 1;
559  day = tm.tm_mday;
560  } else {
561  if (sscanf(colptr, "%4d-%2d-%2d", &year, &month, &day) != 3 || year <= 0 ||
562  month <= 0 || month > 12 || day < 0 || day > 31 ||
563  ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) ||
564  (month == 2 && year % 400 == 0 && day > 29) ||
565  (month == 2 && year % 100 == 0 && day > 28) ||
566  (month == 2 && year % 4 == 0 && day > 29) ||
567  (month == 2 && year % 4 != 0 && day > 28)) {
568  ast_log(LOG_WARNING, "CEL variable %s is not a valid date ('%s').\n", entry->name, colptr);
569  continue;
570  }
571 
572  if (year > 0 && year < 100) {
573  year += 2000;
574  }
575  }
576 
577  ast_str_append(&sql, 0, "%s%s", separator, entry->name);
578  LENGTHEN_BUF2(17);
579  ast_str_append(&sql2, 0, "%s{d '%04d-%02d-%02d'}", separator, year, month, day);
580  }
581  break;
582  case SQL_TYPE_TIME:
583  if (ast_strlen_zero(colptr)) {
584  continue;
585  } else {
586  int hour = 0, minute = 0, second = 0;
587  if (strcasecmp(entry->name, "eventdate") == 0) {
588  struct ast_tm tm;
589  ast_localtime(&record.event_time, &tm, tableptr->usegmtime ? "UTC" : NULL);
590  hour = tm.tm_hour;
591  minute = tm.tm_min;
592  second = (tableptr->allowleapsec || tm.tm_sec < 60) ? tm.tm_sec : 59;
593  } else {
594  int count = sscanf(colptr, "%2d:%2d:%2d", &hour, &minute, &second);
595 
596  if ((count != 2 && count != 3) || hour < 0 || hour > 23 || minute < 0 || minute > 59 || second < 0 || second > (tableptr->allowleapsec ? 60 : 59)) {
597  ast_log(LOG_WARNING, "CEL variable %s is not a valid time ('%s').\n", entry->name, colptr);
598  continue;
599  }
600  }
601 
602  ast_str_append(&sql, 0, "%s%s", separator, entry->name);
603  LENGTHEN_BUF2(15);
604  ast_str_append(&sql2, 0, "%s{t '%02d:%02d:%02d'}", separator, hour, minute, second);
605  }
606  break;
607  case SQL_TYPE_TIMESTAMP:
608  case SQL_TIMESTAMP:
609  if (ast_strlen_zero(colptr)) {
610  continue;
611  } else {
612  if (datefield) {
613  /*
614  * We've already properly formatted the timestamp so there's no need
615  * to parse it and re-format it.
616  */
617  ast_str_append(&sql, 0, "%s%s", separator, entry->name);
618  LENGTHEN_BUF2(27);
619  ast_str_append(&sql2, 0, "%s{ts '%s'}", separator, colptr);
620  } else {
621  int year = 0, month = 0, day = 0, hour = 0, minute = 0;
622  /* MUST use double for microsecond precision */
623  double second = 0.0;
624  if (strcasecmp(entry->name, "eventdate") == 0) {
625  /*
626  * There doesn't seem to be any reference to 'eventdate' anywhere
627  * other than in this module. It should be considered for removal
628  * at a later date.
629  */
630  struct ast_tm tm;
631  ast_localtime(&record.event_time, &tm, tableptr->usegmtime ? "UTC" : NULL);
632  year = tm.tm_year + 1900;
633  month = tm.tm_mon + 1;
634  day = tm.tm_mday;
635  hour = tm.tm_hour;
636  minute = tm.tm_min;
637  second = (tableptr->allowleapsec || tm.tm_sec < 60) ? tm.tm_sec : 59;
638  second += (tm.tm_usec / 1000000.0);
639  } else {
640  /*
641  * If we're here, the data to be inserted MAY be a timestamp
642  * but the column is. We parse as much as we can.
643  */
644  int count = sscanf(colptr, "%4d-%2d-%2d %2d:%2d:%lf", &year, &month, &day, &hour, &minute, &second);
645 
646  if ((count != 3 && count != 5 && count != 6) || year <= 0 ||
647  month <= 0 || month > 12 || day < 0 || day > 31 ||
648  ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) ||
649  (month == 2 && year % 400 == 0 && day > 29) ||
650  (month == 2 && year % 100 == 0 && day > 28) ||
651  (month == 2 && year % 4 == 0 && day > 29) ||
652  (month == 2 && year % 4 != 0 && day > 28) ||
653  hour > 23 || minute > 59 || ((int)floor(second)) > (tableptr->allowleapsec ? 60 : 59) ||
654  hour < 0 || minute < 0 || ((int)floor(second)) < 0) {
655  ast_log(LOG_WARNING, "CEL variable %s is not a valid timestamp ('%s').\n", entry->name, colptr);
656  continue;
657  }
658 
659  if (year > 0 && year < 100) {
660  year += 2000;
661  }
662  }
663 
664  ast_str_append(&sql, 0, "%s%s", separator, entry->name);
665  LENGTHEN_BUF2(27);
666  ast_str_append(&sql2, 0, "%s{ts '%04d-%02d-%02d %02d:%02d:%09.6lf'}", separator, year, month, day, hour, minute, second);
667  }
668  }
669  break;
670  case SQL_INTEGER:
671  {
672  int integer = 0;
673  if (sscanf(colptr, "%30d", &integer) != 1) {
674  ast_log(LOG_WARNING, "CEL variable %s is not an integer.\n", entry->name);
675  continue;
676  }
677 
678  ast_str_append(&sql, 0, "%s%s", separator, entry->name);
679  LENGTHEN_BUF2(12);
680  ast_str_append(&sql2, 0, "%s%d", separator, integer);
681  }
682  break;
683  case SQL_BIGINT:
684  {
685  long long integer = 0;
686  int ret;
687  if ((ret = sscanf(colptr, "%30lld", &integer)) != 1) {
688  ast_log(LOG_WARNING, "CEL variable %s is not an integer. (%d - '%s')\n", entry->name, ret, colptr);
689  continue;
690  }
691 
692  ast_str_append(&sql, 0, "%s%s", separator, entry->name);
693  LENGTHEN_BUF2(24);
694  ast_str_append(&sql2, 0, "%s%lld", separator, integer);
695  }
696  break;
697  case SQL_SMALLINT:
698  {
699  short integer = 0;
700  if (sscanf(colptr, "%30hd", &integer) != 1) {
701  ast_log(LOG_WARNING, "CEL variable %s is not an integer.\n", entry->name);
702  continue;
703  }
704 
705  ast_str_append(&sql, 0, "%s%s", separator, entry->name);
706  LENGTHEN_BUF2(7);
707  ast_str_append(&sql2, 0, "%s%d", separator, integer);
708  }
709  break;
710  case SQL_TINYINT:
711  {
712  signed char integer = 0;
713  if (sscanf(colptr, "%30hhd", &integer) != 1) {
714  ast_log(LOG_WARNING, "CEL variable %s is not an integer.\n", entry->name);
715  continue;
716  }
717 
718  ast_str_append(&sql, 0, "%s%s", separator, entry->name);
719  LENGTHEN_BUF2(4);
720  ast_str_append(&sql2, 0, "%s%d", separator, integer);
721  }
722  break;
723  case SQL_BIT:
724  {
725  signed char integer = 0;
726  if (sscanf(colptr, "%30hhd", &integer) != 1) {
727  ast_log(LOG_WARNING, "CEL variable %s is not an integer.\n", entry->name);
728  continue;
729  }
730  if (integer != 0)
731  integer = 1;
732 
733  ast_str_append(&sql, 0, "%s%s", separator, entry->name);
734  LENGTHEN_BUF2(2);
735  ast_str_append(&sql2, 0, "%s%d", separator, integer);
736  }
737  break;
738  case SQL_NUMERIC:
739  case SQL_DECIMAL:
740  {
741  double number = 0.0;
742  if (sscanf(colptr, "%30lf", &number) != 1) {
743  ast_log(LOG_WARNING, "CEL variable %s is not an numeric type.\n", entry->name);
744  continue;
745  }
746 
747  ast_str_append(&sql, 0, "%s%s", separator, entry->name);
748  LENGTHEN_BUF2(entry->decimals + 2);
749  ast_str_append(&sql2, 0, "%s%*.*lf", separator, entry->decimals, entry->radix, number);
750  }
751  break;
752  case SQL_FLOAT:
753  case SQL_REAL:
754  case SQL_DOUBLE:
755  {
756  double number = 0.0;
757  if (sscanf(colptr, "%30lf", &number) != 1) {
758  ast_log(LOG_WARNING, "CEL variable %s is not an numeric type.\n", entry->name);
759  continue;
760  }
761 
762  ast_str_append(&sql, 0, "%s%s", separator, entry->name);
763  LENGTHEN_BUF2(entry->decimals);
764  ast_str_append(&sql2, 0, "%s%lf", separator, number);
765  }
766  break;
767  default:
768  ast_log(LOG_WARNING, "Column type %d (field '%s:%s:%s') is unsupported at this time.\n", entry->type, tableptr->connection, tableptr->table, entry->name);
769  continue;
770  }
771  separator = ", ";
772  }
773  }
774 
775  /* Concatenate the two constructed buffers */
777  ast_str_append(&sql, 0, ")");
778  ast_str_append(&sql2, 0, ")");
779  ast_str_append(&sql, 0, "%s", ast_str_buffer(sql2));
780 
781  ast_debug(3, "Executing SQL statement: [%s]\n", ast_str_buffer(sql));
783  if (stmt) {
784  SQLRowCount(stmt, &rows);
785  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
786  }
787  if (rows == 0) {
788  ast_log(LOG_WARNING, "Insert failed on '%s:%s'. CEL failed: %s\n", tableptr->connection, tableptr->table, ast_str_buffer(sql));
789  }
790 early_release:
792  }
794 
795  /* Next time, just allocate buffers that are that big to start with. */
796  if (ast_str_strlen(sql) > maxsize) {
797  maxsize = ast_str_strlen(sql);
798  }
799  if (ast_str_strlen(sql2) > maxsize2) {
800  maxsize2 = ast_str_strlen(sql2);
801  }
802 
803  ast_free(sql);
804  ast_free(sql2);
805 }
const char * account_code
Definition: cel.h:161
int ast_odbc_backslash_is_escape(struct odbc_obj *obj)
Checks if the database natively supports backslash as an escape character.
Definition: res_odbc.c:842
const char * caller_id_name
Definition: cel.h:151
Helper struct for getting the fields out of a CEL event.
Definition: cel.h:136
SQLSMALLINT radix
const char * linked_id
Definition: cel.h:164
int tm_usec
Definition: localtime.h:48
char * filtervalue
static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
Definition: cel_odbc.c:311
static unsigned char cel_show_user_def
Definition: cel_odbc.c:61
const char * user_defined_name
Definition: cel.h:150
#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
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
const char * extension
Definition: cel.h:156
#define LENGTHEN_BUF1(size)
Definition: cel_odbc.c:359
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
SQLINTEGER octetlen
#define NULL
Definition: resample.c:96
unsigned int allowleapsec
Definition: cel_odbc.c:84
const char * extra
Definition: cel.h:168
int tm_year
Definition: localtime.h:41
#define ast_verb(level,...)
Definition: logger.h:463
#define ast_strlen_zero(foo)
Definition: strings.h:52
char * celname
Definition: cel_odbc.c:68
Number structure.
Definition: app_followme.c:154
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
#define AST_RWLIST_RDLOCK(head)
Read locks a list.
Definition: linkedlists.h:77
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
SQLSMALLINT decimals
#define LENGTHEN_BUF2(size)
Definition: cel_odbc.c:362
ODBC container.
Definition: res_odbc.h:46
const char * context
Definition: cel.h:157
int tm_mon
Definition: localtime.h:40
uint32_t version
struct ABI version
Definition: cel.h:146
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
int tm_mday
Definition: localtime.h:39
char * connection
enum ast_cel_event_type event_type
Definition: cel.h:147
#define LOG_ERROR
Definition: logger.h:285
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:584
const char * caller_id_rdnis
Definition: cel.h:154
const char * peer
Definition: cel.h:167
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:490
#define ast_free(a)
Definition: astmm.h:182
#define ast_odbc_request_obj(a, b)
Definition: res_odbc.h:122
static struct ast_codec unknown
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
int tm_hour
Definition: localtime.h:38
const char * user_field
Definition: cel.h:166
#define AST_CEL_EVENT_RECORD_VERSION
struct ABI version
Definition: cel.h:141
char * table
int tm_sec
Definition: localtime.h:36
const char * peer_account
Definition: cel.h:162
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:688
SQLHSTMT ast_odbc_prepare_and_execute(struct odbc_obj *obj, SQLHSTMT(*prepare_cb)(struct odbc_obj *obj, void *data), void *data)
Prepares, executes, and returns the resulting statement handle.
Definition: res_odbc.c:407
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
const char * unique_id
Definition: cel.h:163
struct tables::mysql_columns columns
char * staticvalue
a user-defined event, the event name field should be set
Definition: cel.h:69
Definition: search.h:40
const char * caller_id_dnid
Definition: cel.h:155
const char * channel_name
Definition: cel.h:158
const char * event_name
Definition: cel.h:149
static int maxsize
Definition: cel_odbc.c:64
void ast_odbc_release_obj(struct odbc_obj *obj)
Releases an ODBC object previously allocated by ast_odbc_request_obj()
Definition: res_odbc.c:813
static int maxsize2
Definition: cel_odbc.c:64
struct timeval event_time
Definition: cel.h:148
INT32 integer
Definition: lpc10.h:80
int tm_min
Definition: localtime.h:37
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
unsigned int usegmtime

◆ reload()

static int reload ( void  )
static

Definition at line 840 of file cel_odbc.c.

References ast_log, AST_MODFLAG_LOAD_ORDER, AST_MODPRI_CDR_DRIVER, AST_MODULE_INFO(), AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_SUCCESS, AST_MODULE_SUPPORT_CORE, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, ASTERISK_GPL_KEY, free_config(), load_config(), load_module(), LOG_ERROR, and unload_module().

841 {
843  ast_log(LOG_ERROR, "Unable to lock column list. Reload failed.\n");
845  }
846 
847  free_config();
848  load_config();
851 }
#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
#define ast_log
Definition: astobj2.c:42
static int free_config(void)
Definition: cel_odbc.c:298
#define LOG_ERROR
Definition: logger.h:285
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
static int load_config(void)
Definition: cel_odbc.c:91

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 807 of file cel_odbc.c.

References ast_cel_backend_unregister(), ast_log, AST_RWLIST_HEAD_DESTROY, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, free_config(), LOG_ERROR, and ODBC_BACKEND_NAME.

Referenced by reload().

808 {
810  ast_log(LOG_ERROR, "Unable to lock column list. Unload failed.\n");
811  return -1;
812  }
813 
815  free_config();
818 
819  return 0;
820 }
#define AST_RWLIST_HEAD_DESTROY(head)
Destroys an rwlist head structure.
Definition: linkedlists.h:666
#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
#define ast_log
Definition: astobj2.c:42
static int free_config(void)
Definition: cel_odbc.c:298
int ast_cel_backend_unregister(const char *name)
Unregister a CEL backend.
Definition: cel.c:1728
#define LOG_ERROR
Definition: logger.h:285
#define ODBC_BACKEND_NAME
Definition: cel_odbc.c:55

Variable Documentation

◆ __mod_info

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "ODBC CEL 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 = "30ef0c93b36035ec78c9cfd712d36d9b" , .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CDR_DRIVER, .requires = "cel,res_odbc", }
static

Definition at line 860 of file cel_odbc.c.

◆ ast_module_info

const struct ast_module_info* ast_module_info = &__mod_info
static

Definition at line 860 of file cel_odbc.c.

◆ cel_show_user_def

unsigned char cel_show_user_def
static

TRUE if we should set the eventtype field to USER_DEFINED on user events.

Definition at line 61 of file cel_odbc.c.

Referenced by load_config(), and odbc_log().

◆ maxsize

int maxsize = 512
static

Definition at line 64 of file cel_odbc.c.

Referenced by odbc_log().

◆ maxsize2

int maxsize2 = 512
static

Definition at line 64 of file cel_odbc.c.

Referenced by odbc_log().

◆ odbc_tables

struct odbc_tables odbc_tables = { .first = NULL, .last = NULL, .lock = { PTHREAD_RWLOCK_INITIALIZER , NULL, {1, 0} } , }
static