Wed Jan 8 2020 09:49:50

Asterisk developer's documentation


res_odbc.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2012, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * res_odbc.c <ODBC resource manager>
9  * Copyright (C) 2004 - 2005 Anthony Minessale II <anthmct@yahoo.com>
10  *
11  * See http://www.asterisk.org for more information about
12  * the Asterisk project. Please do not directly contact
13  * any of the maintainers of this project for assistance;
14  * the project provides a web site, mailing lists and IRC
15  * channels for your use.
16  *
17  * This program is free software, distributed under the terms of
18  * the GNU General Public License Version 2. See the LICENSE file
19  * at the top of the source tree.
20  */
21 
22 /*! \file
23  *
24  * \brief ODBC resource manager
25  *
26  * \author Mark Spencer <markster@digium.com>
27  * \author Anthony Minessale II <anthmct@yahoo.com>
28  * \author Tilghman Lesher <tilghman@digium.com>
29  *
30  * \arg See also: \ref cdr_odbc
31  */
32 
33 /*** MODULEINFO
34  <depend>generic_odbc</depend>
35  <depend>ltdl</depend>
36  <support_level>core</support_level>
37  ***/
38 
39 #include "asterisk.h"
40 
41 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 413586 $")
42 
43 #include "asterisk/file.h"
44 #include "asterisk/channel.h"
45 #include "asterisk/config.h"
46 #include "asterisk/pbx.h"
47 #include "asterisk/module.h"
48 #include "asterisk/cli.h"
49 #include "asterisk/lock.h"
50 #include "asterisk/res_odbc.h"
51 #include "asterisk/time.h"
52 #include "asterisk/astobj2.h"
53 #include "asterisk/app.h"
54 #include "asterisk/strings.h"
55 #include "asterisk/threadstorage.h"
56 #include "asterisk/data.h"
57 
58 /*** DOCUMENTATION
59  <function name="ODBC" language="en_US">
60  <synopsis>
61  Controls ODBC transaction properties.
62  </synopsis>
63  <syntax>
64  <parameter name="property" required="true">
65  <enumlist>
66  <enum name="transaction">
67  <para>Gets or sets the active transaction ID. If set, and the transaction ID does not
68  exist and a <replaceable>database name</replaceable> is specified as an argument, it will be created.</para>
69  </enum>
70  <enum name="forcecommit">
71  <para>Controls whether a transaction will be automatically committed when the channel
72  hangs up. Defaults to false. If a <replaceable>transaction ID</replaceable> is specified in the optional argument,
73  the property will be applied to that ID, otherwise to the current active ID.</para>
74  </enum>
75  <enum name="isolation">
76  <para>Controls the data isolation on uncommitted transactions. May be one of the
77  following: <literal>read_committed</literal>, <literal>read_uncommitted</literal>,
78  <literal>repeatable_read</literal>, or <literal>serializable</literal>. Defaults to the
79  database setting in <filename>res_odbc.conf</filename> or <literal>read_committed</literal>
80  if not specified. If a <replaceable>transaction ID</replaceable> is specified as an optional argument, it will be
81  applied to that ID, otherwise the current active ID.</para>
82  </enum>
83  </enumlist>
84  </parameter>
85  <parameter name="argument" required="false" />
86  </syntax>
87  <description>
88  <para>The ODBC() function allows setting several properties to influence how a connected
89  database processes transactions.</para>
90  </description>
91  </function>
92  <application name="ODBC_Commit" language="en_US">
93  <synopsis>
94  Commits a currently open database transaction.
95  </synopsis>
96  <syntax>
97  <parameter name="transaction ID" required="no" />
98  </syntax>
99  <description>
100  <para>Commits the database transaction specified by <replaceable>transaction ID</replaceable>
101  or the current active transaction, if not specified.</para>
102  </description>
103  </application>
104  <application name="ODBC_Rollback" language="en_US">
105  <synopsis>
106  Rollback a currently open database transaction.
107  </synopsis>
108  <syntax>
109  <parameter name="transaction ID" required="no" />
110  </syntax>
111  <description>
112  <para>Rolls back the database transaction specified by <replaceable>transaction ID</replaceable>
113  or the current active transaction, if not specified.</para>
114  </description>
115  </application>
116  ***/
117 
119 {
121  char name[80];
122  char dsn[80];
123  char *username;
124  char *password;
125  char *sanitysql;
126  SQLHENV env;
127  unsigned int haspool:1; /*!< Boolean - TDS databases need this */
128  unsigned int delme:1; /*!< Purge the class */
129  unsigned int backslash_is_escape:1; /*!< On this database, the backslash is a native escape sequence */
130  unsigned int forcecommit:1; /*!< Should uncommitted transactions be auto-committed on handle release? */
131  unsigned int allow_empty_strings:1; /*!< Implicit conversion from an empty string to a number is valid for this database */
132  unsigned int isolation; /*!< Flags for how the DB should deal with data in other, uncommitted transactions */
133  unsigned int limit; /*!< Maximum number of database handles we will allow */
134  int count; /*!< Running count of pooled connections */
135  unsigned int idlecheck; /*!< Recheck the connection if it is idle for this long (in seconds) */
136  unsigned int conntimeout; /*!< Maximum time the connection process should take */
137  /*! When a connection fails, cache that failure for how long? */
139  /*! When a connection fails, when did that last occur? */
140  struct timeval last_negative_connect;
141  /*! List of handles associated with this class */
143 };
144 
146 
148 
149 static odbc_status odbc_obj_connect(struct odbc_obj *obj);
150 static odbc_status odbc_obj_disconnect(struct odbc_obj *obj);
151 static int odbc_register_class(struct odbc_class *class, int connect);
152 static void odbc_txn_free(void *data);
153 static void odbc_release_obj2(struct odbc_obj *obj, struct odbc_txn_frame *tx);
154 
156 
157 static const struct ast_datastore_info txn_info = {
158  .type = "ODBC_Transaction",
159  .destroy = odbc_txn_free,
160 };
161 
164  struct ast_channel *owner;
165  struct odbc_obj *obj; /*!< Database handle within which transacted statements are run */
166  /*!\brief Is this record the current active transaction within the channel?
167  * Note that the active flag is really only necessary for statements which
168  * are triggered from the dialplan, as there isn't a direct correlation
169  * between multiple statements. Applications wishing to use transactions
170  * may simply perform each statement on the same odbc_obj, which keeps the
171  * transaction persistent.
172  */
173  unsigned int active:1;
174  unsigned int forcecommit:1; /*!< Should uncommitted transactions be auto-committed on handle release? */
175  unsigned int isolation; /*!< Flags for how the DB should deal with data in other, uncommitted transactions */
176  char name[0]; /*!< Name of this transaction ID */
177 };
178 
179 #define DATA_EXPORT_ODBC_CLASS(MEMBER) \
180  MEMBER(odbc_class, name, AST_DATA_STRING) \
181  MEMBER(odbc_class, dsn, AST_DATA_STRING) \
182  MEMBER(odbc_class, username, AST_DATA_STRING) \
183  MEMBER(odbc_class, password, AST_DATA_PASSWORD) \
184  MEMBER(odbc_class, limit, AST_DATA_INTEGER) \
185  MEMBER(odbc_class, count, AST_DATA_INTEGER) \
186  MEMBER(odbc_class, forcecommit, AST_DATA_BOOLEAN)
187 
189 
190 static const char *isolation2text(int iso)
191 {
192  if (iso == SQL_TXN_READ_COMMITTED) {
193  return "read_committed";
194  } else if (iso == SQL_TXN_READ_UNCOMMITTED) {
195  return "read_uncommitted";
196  } else if (iso == SQL_TXN_SERIALIZABLE) {
197  return "serializable";
198  } else if (iso == SQL_TXN_REPEATABLE_READ) {
199  return "repeatable_read";
200  } else {
201  return "unknown";
202  }
203 }
204 
205 static int text2isolation(const char *txt)
206 {
207  if (strncasecmp(txt, "read_", 5) == 0) {
208  if (strncasecmp(txt + 5, "c", 1) == 0) {
209  return SQL_TXN_READ_COMMITTED;
210  } else if (strncasecmp(txt + 5, "u", 1) == 0) {
211  return SQL_TXN_READ_UNCOMMITTED;
212  } else {
213  return 0;
214  }
215  } else if (strncasecmp(txt, "ser", 3) == 0) {
216  return SQL_TXN_SERIALIZABLE;
217  } else if (strncasecmp(txt, "rep", 3) == 0) {
218  return SQL_TXN_REPEATABLE_READ;
219  } else {
220  return 0;
221  }
222 }
223 
224 static struct odbc_txn_frame *find_transaction(struct ast_channel *chan, struct odbc_obj *obj, const char *name, int active)
225 {
226  struct ast_datastore *txn_store;
227  AST_LIST_HEAD(, odbc_txn_frame) *oldlist;
228  struct odbc_txn_frame *txn = NULL;
229 
230  if (!chan && obj && obj->txf && obj->txf->owner) {
231  chan = obj->txf->owner;
232  } else if (!chan) {
233  /* No channel == no transaction */
234  return NULL;
235  }
236 
237  ast_channel_lock(chan);
238  if ((txn_store = ast_channel_datastore_find(chan, &txn_info, NULL))) {
239  oldlist = txn_store->data;
240  } else {
241  /* Need to create a new datastore */
242  if (!(txn_store = ast_datastore_alloc(&txn_info, NULL))) {
243  ast_log(LOG_ERROR, "Unable to allocate a new datastore. Cannot create a new transaction.\n");
244  ast_channel_unlock(chan);
245  return NULL;
246  }
247 
248  if (!(oldlist = ast_calloc(1, sizeof(*oldlist)))) {
249  ast_log(LOG_ERROR, "Unable to allocate datastore list head. Cannot create a new transaction.\n");
250  ast_datastore_free(txn_store);
251  ast_channel_unlock(chan);
252  return NULL;
253  }
254 
255  txn_store->data = oldlist;
256  AST_LIST_HEAD_INIT(oldlist);
257  ast_channel_datastore_add(chan, txn_store);
258  }
259 
260  AST_LIST_LOCK(oldlist);
261  ast_channel_unlock(chan);
262 
263  /* Scanning for an object is *fast*. Scanning for a name is much slower. */
264  if (obj != NULL || active == 1) {
265  AST_LIST_TRAVERSE(oldlist, txn, list) {
266  if (txn->obj == obj || txn->active) {
267  AST_LIST_UNLOCK(oldlist);
268  return txn;
269  }
270  }
271  }
272 
273  if (name != NULL) {
274  AST_LIST_TRAVERSE(oldlist, txn, list) {
275  if (!strcasecmp(txn->name, name)) {
276  AST_LIST_UNLOCK(oldlist);
277  return txn;
278  }
279  }
280  }
281 
282  /* Nothing found, create one */
283  if (name && obj && (txn = ast_calloc(1, sizeof(*txn) + strlen(name) + 1))) {
284  struct odbc_txn_frame *otxn;
285 
286  strcpy(txn->name, name); /* SAFE */
287  txn->obj = obj;
288  txn->isolation = obj->parent->isolation;
289  txn->forcecommit = obj->parent->forcecommit;
290  txn->owner = chan;
291  txn->active = 1;
292 
293  /* On creation, the txn becomes active, and all others inactive */
294  AST_LIST_TRAVERSE(oldlist, otxn, list) {
295  otxn->active = 0;
296  }
297  AST_LIST_INSERT_TAIL(oldlist, txn, list);
298 
299  obj->txf = txn;
300  obj->tx = 1;
301  }
302  AST_LIST_UNLOCK(oldlist);
303 
304  return txn;
305 }
306 
308 {
309  if (!tx) {
310  return NULL;
311  }
312 
313  ast_debug(2, "release_transaction(%p) called (tx->obj = %p, tx->obj->txf = %p)\n", tx, tx->obj, tx->obj ? tx->obj->txf : NULL);
314 
315  /* If we have an owner, disassociate */
316  if (tx->owner) {
317  struct ast_datastore *txn_store;
318  AST_LIST_HEAD(, odbc_txn_frame) *oldlist;
319 
320  ast_channel_lock(tx->owner);
321  if ((txn_store = ast_channel_datastore_find(tx->owner, &txn_info, NULL))) {
322  oldlist = txn_store->data;
323  AST_LIST_LOCK(oldlist);
324  AST_LIST_REMOVE(oldlist, tx, list);
325  AST_LIST_UNLOCK(oldlist);
326  }
328  tx->owner = NULL;
329  }
330 
331  if (tx->obj) {
332  /* If we have any uncommitted transactions, they are handled when we release the object */
333  struct odbc_obj *obj = tx->obj;
334  /* Prevent recursion during destruction */
335  tx->obj->txf = NULL;
336  tx->obj = NULL;
337  odbc_release_obj2(obj, tx);
338  }
339  ast_free(tx);
340  return NULL;
341 }
342 
343 static void odbc_txn_free(void *vdata)
344 {
345  struct odbc_txn_frame *tx;
346  AST_LIST_HEAD(, odbc_txn_frame) *oldlist = vdata;
347 
348  ast_debug(2, "odbc_txn_free(%p) called\n", vdata);
349 
350  AST_LIST_LOCK(oldlist);
351  while ((tx = AST_LIST_REMOVE_HEAD(oldlist, list))) {
353  }
354  AST_LIST_UNLOCK(oldlist);
355  AST_LIST_HEAD_DESTROY(oldlist);
356  ast_free(oldlist);
357 }
358 
359 static int mark_transaction_active(struct ast_channel *chan, struct odbc_txn_frame *tx)
360 {
361  struct ast_datastore *txn_store;
362  AST_LIST_HEAD(, odbc_txn_frame) *oldlist;
363  struct odbc_txn_frame *active = NULL, *txn;
364 
365  if (!chan && tx && tx->owner) {
366  chan = tx->owner;
367  }
368 
369  if (!chan) {
370  return -1;
371  }
372 
373  ast_channel_lock(chan);
374  if (!(txn_store = ast_channel_datastore_find(chan, &txn_info, NULL))) {
375  ast_channel_unlock(chan);
376  return -1;
377  }
378 
379  oldlist = txn_store->data;
380  AST_LIST_LOCK(oldlist);
381  AST_LIST_TRAVERSE(oldlist, txn, list) {
382  if (txn == tx) {
383  txn->active = 1;
384  active = txn;
385  } else {
386  txn->active = 0;
387  }
388  }
389  AST_LIST_UNLOCK(oldlist);
390  ast_channel_unlock(chan);
391  return active ? 0 : -1;
392 }
393 
394 static void odbc_class_destructor(void *data)
395 {
396  struct odbc_class *class = data;
397  /* Due to refcounts, we can safely assume that any objects with a reference
398  * to us will prevent our destruction, so we don't need to worry about them.
399  */
400  if (class->username) {
401  ast_free(class->username);
402  }
403  if (class->password) {
404  ast_free(class->password);
405  }
406  if (class->sanitysql) {
407  ast_free(class->sanitysql);
408  }
409  ao2_ref(class->obj_container, -1);
410  SQLFreeHandle(SQL_HANDLE_ENV, class->env);
411 }
412 
413 static int null_hash_fn(const void *obj, const int flags)
414 {
415  return 0;
416 }
417 
418 static void odbc_obj_destructor(void *data)
419 {
420  struct odbc_obj *obj = data;
421  struct odbc_class *class = obj->parent;
422  obj->parent = NULL;
423  odbc_obj_disconnect(obj);
424  ast_mutex_destroy(&obj->lock);
425  ao2_ref(class, -1);
426 }
427 
429  struct odbc_cache_columns *col;
430  ast_debug(1, "Destroying table cache for %s\n", table->table);
431  AST_RWLIST_WRLOCK(&table->columns);
432  while ((col = AST_RWLIST_REMOVE_HEAD(&table->columns, list))) {
433  ast_free(col);
434  }
435  AST_RWLIST_UNLOCK(&table->columns);
437  ast_free(table);
438 }
439 
440 /*!
441  * \brief Find or create an entry describing the table specified.
442  * \param database Name of an ODBC class on which to query the table
443  * \param tablename Tablename to describe
444  * \retval A structure describing the table layout, or NULL, if the table is not found or another error occurs.
445  * When a structure is returned, the contained columns list will be
446  * rdlock'ed, to ensure that it will be retained in memory.
447  * \since 1.6.1
448  */
449 struct odbc_cache_tables *ast_odbc_find_table(const char *database, const char *tablename)
450 {
451  struct odbc_cache_tables *tableptr;
452  struct odbc_cache_columns *entry;
453  char columnname[80];
454  SQLLEN sqlptr;
455  SQLHSTMT stmt = NULL;
456  int res = 0, error = 0, try = 0;
457  struct odbc_obj *obj = ast_odbc_request_obj(database, 0);
458 
460  AST_RWLIST_TRAVERSE(&odbc_tables, tableptr, list) {
461  if (strcmp(tableptr->connection, database) == 0 && strcmp(tableptr->table, tablename) == 0) {
462  break;
463  }
464  }
465  if (tableptr) {
466  AST_RWLIST_RDLOCK(&tableptr->columns);
468  if (obj) {
470  }
471  return tableptr;
472  }
473 
474  if (!obj) {
475  ast_log(LOG_WARNING, "Unable to retrieve database handle for table description '%s@%s'\n", tablename, database);
477  return NULL;
478  }
479 
480  /* Table structure not already cached; build it now. */
481  ast_mutex_lock(&obj->lock);
482  do {
483  res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
484  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
485  if (try == 0) {
486  try = 1;
488  continue;
489  }
490  ast_log(LOG_WARNING, "SQL Alloc Handle failed on connection '%s'!\n", database);
491  break;
492  }
493 
494  res = SQLColumns(stmt, NULL, 0, NULL, 0, (unsigned char *)tablename, SQL_NTS, (unsigned char *)"%", SQL_NTS);
495  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
496  if (try == 0) {
497  try = 1;
498  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
500  continue;
501  }
502  ast_log(LOG_ERROR, "Unable to query database columns on connection '%s'.\n", database);
503  break;
504  }
505 
506  if (!(tableptr = ast_calloc(sizeof(char), sizeof(*tableptr) + strlen(database) + 1 + strlen(tablename) + 1))) {
507  ast_log(LOG_ERROR, "Out of memory creating entry for table '%s' on connection '%s'\n", tablename, database);
508  break;
509  }
510 
511  tableptr->connection = (char *)tableptr + sizeof(*tableptr);
512  tableptr->table = (char *)tableptr + sizeof(*tableptr) + strlen(database) + 1;
513  strcpy(tableptr->connection, database); /* SAFE */
514  strcpy(tableptr->table, tablename); /* SAFE */
515  AST_RWLIST_HEAD_INIT(&(tableptr->columns));
516 
517  while ((res = SQLFetch(stmt)) != SQL_NO_DATA && res != SQL_ERROR) {
518  SQLGetData(stmt, 4, SQL_C_CHAR, columnname, sizeof(columnname), &sqlptr);
519 
520  if (!(entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(columnname) + 1))) {
521  ast_log(LOG_ERROR, "Out of memory creating entry for column '%s' in table '%s' on connection '%s'\n", columnname, tablename, database);
522  error = 1;
523  break;
524  }
525  entry->name = (char *)entry + sizeof(*entry);
526  strcpy(entry->name, columnname);
527 
528  SQLGetData(stmt, 5, SQL_C_SHORT, &entry->type, sizeof(entry->type), NULL);
529  SQLGetData(stmt, 7, SQL_C_LONG, &entry->size, sizeof(entry->size), NULL);
530  SQLGetData(stmt, 9, SQL_C_SHORT, &entry->decimals, sizeof(entry->decimals), NULL);
531  SQLGetData(stmt, 10, SQL_C_SHORT, &entry->radix, sizeof(entry->radix), NULL);
532  SQLGetData(stmt, 11, SQL_C_SHORT, &entry->nullable, sizeof(entry->nullable), NULL);
533  SQLGetData(stmt, 16, SQL_C_LONG, &entry->octetlen, sizeof(entry->octetlen), NULL);
534 
535  /* Specification states that the octenlen should be the maximum number of bytes
536  * returned in a char or binary column, but it seems that some drivers just set
537  * it to NULL. (Bad Postgres! No biscuit!) */
538  if (entry->octetlen == 0) {
539  entry->octetlen = entry->size;
540  }
541 
542  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);
543  /* Insert column info into column list */
544  AST_LIST_INSERT_TAIL(&(tableptr->columns), entry, list);
545  }
546  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
547 
548  AST_RWLIST_INSERT_TAIL(&odbc_tables, tableptr, list);
549  AST_RWLIST_RDLOCK(&(tableptr->columns));
550  break;
551  } while (1);
552  ast_mutex_unlock(&obj->lock);
553 
555 
556  if (error) {
557  destroy_table_cache(tableptr);
558  tableptr = NULL;
559  }
560  if (obj) {
562  }
563  return tableptr;
564 }
565 
566 struct odbc_cache_columns *ast_odbc_find_column(struct odbc_cache_tables *table, const char *colname)
567 {
568  struct odbc_cache_columns *col;
569  AST_RWLIST_TRAVERSE(&table->columns, col, list) {
570  if (strcasecmp(col->name, colname) == 0) {
571  return col;
572  }
573  }
574  return NULL;
575 }
576 
577 int ast_odbc_clear_cache(const char *database, const char *tablename)
578 {
579  struct odbc_cache_tables *tableptr;
580 
582  AST_RWLIST_TRAVERSE_SAFE_BEGIN(&odbc_tables, tableptr, list) {
583  if (strcmp(tableptr->connection, database) == 0 && strcmp(tableptr->table, tablename) == 0) {
585  destroy_table_cache(tableptr);
586  break;
587  }
588  }
591  return tableptr ? 0 : -1;
592 }
593 
594 SQLHSTMT ast_odbc_direct_execute(struct odbc_obj *obj, SQLHSTMT (*exec_cb)(struct odbc_obj *obj, void *data), void *data)
595 {
596  int attempt;
597  SQLHSTMT stmt;
598 
599  ast_mutex_lock(&obj->lock);
600 
601  for (attempt = 0; attempt < 2; attempt++) {
602  stmt = exec_cb(obj, data);
603 
604  if (stmt) {
605  break;
606  } else if (obj->tx) {
607  ast_log(LOG_WARNING, "Failed to execute, but unable to reconnect, as we're transactional.\n");
608  break;
609  } else if (attempt == 0) {
610  ast_log(LOG_WARNING, "SQL Execute error! Verifying connection to %s [%s]...\n", obj->parent->name, obj->parent->dsn);
611  }
612  if (!ast_odbc_sanity_check(obj)) {
613  break;
614  }
615  }
616 
617  ast_mutex_unlock(&obj->lock);
618 
619  return stmt;
620 }
621 
622 SQLHSTMT ast_odbc_prepare_and_execute(struct odbc_obj *obj, SQLHSTMT (*prepare_cb)(struct odbc_obj *obj, void *data), void *data)
623 {
624  int res = 0, i, attempt;
625  SQLINTEGER nativeerror=0, numfields=0;
626  SQLSMALLINT diagbytes=0;
627  unsigned char state[10], diagnostic[256];
628  SQLHSTMT stmt;
629 
630  ast_mutex_lock(&obj->lock);
631 
632  for (attempt = 0; attempt < 2; attempt++) {
633  /* This prepare callback may do more than just prepare -- it may also
634  * bind parameters, bind results, etc. The real key, here, is that
635  * when we disconnect, all handles become invalid for most databases.
636  * We must therefore redo everything when we establish a new
637  * connection. */
638  stmt = prepare_cb(obj, data);
639 
640  if (stmt) {
641  res = SQLExecute(stmt);
642  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) {
643  if (res == SQL_ERROR) {
644  SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
645  for (i = 0; i < numfields; i++) {
646  SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
647  ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
648  if (i > 10) {
649  ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
650  break;
651  }
652  }
653  }
654 
655  if (obj->tx) {
656  ast_log(LOG_WARNING, "SQL Execute error, but unable to reconnect, as we're transactional.\n");
657  break;
658  } else {
659  ast_log(LOG_WARNING, "SQL Execute error %d! Verifying connection to %s [%s]...\n", res, obj->parent->name, obj->parent->dsn);
660  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
661  stmt = NULL;
662 
663  obj->up = 0;
664  /*
665  * While this isn't the best way to try to correct an error, this won't automatically
666  * fail when the statement handle invalidates.
667  */
668  if (!ast_odbc_sanity_check(obj)) {
669  break;
670  }
671  continue;
672  }
673  } else {
674  obj->last_used = ast_tvnow();
675  }
676  break;
677  } else if (attempt == 0) {
679  }
680  }
681 
682  ast_mutex_unlock(&obj->lock);
683 
684  return stmt;
685 }
686 
687 int ast_odbc_smart_execute(struct odbc_obj *obj, SQLHSTMT stmt)
688 {
689  int res = 0, i;
690  SQLINTEGER nativeerror=0, numfields=0;
691  SQLSMALLINT diagbytes=0;
692  unsigned char state[10], diagnostic[256];
693 
694  ast_mutex_lock(&obj->lock);
695 
696  res = SQLExecute(stmt);
697  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) {
698  if (res == SQL_ERROR) {
699  SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
700  for (i = 0; i < numfields; i++) {
701  SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
702  ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
703  if (i > 10) {
704  ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
705  break;
706  }
707  }
708  }
709  } else {
710  obj->last_used = ast_tvnow();
711  }
712 
713  ast_mutex_unlock(&obj->lock);
714 
715  return res;
716 }
717 
718 SQLRETURN ast_odbc_ast_str_SQLGetData(struct ast_str **buf, int pmaxlen, SQLHSTMT StatementHandle, SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType, SQLLEN *StrLen_or_Ind)
719 {
720  SQLRETURN res;
721 
722  if (pmaxlen == 0) {
723  if (SQLGetData(StatementHandle, ColumnNumber, TargetType, ast_str_buffer(*buf), 0, StrLen_or_Ind) == SQL_SUCCESS_WITH_INFO) {
724  ast_str_make_space(buf, *StrLen_or_Ind + 1);
725  }
726  } else if (pmaxlen > 0) {
727  ast_str_make_space(buf, pmaxlen);
728  }
729  res = SQLGetData(StatementHandle, ColumnNumber, TargetType, ast_str_buffer(*buf), ast_str_size(*buf), StrLen_or_Ind);
730  ast_str_update(*buf);
731 
732  return res;
733 }
734 
736 {
737  char *test_sql = "select 1";
738  SQLHSTMT stmt;
739  int res = 0;
740 
741  if (!ast_strlen_zero(obj->parent->sanitysql))
742  test_sql = obj->parent->sanitysql;
743 
744  if (obj->up) {
745  res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
746  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
747  obj->up = 0;
748  } else {
749  res = SQLPrepare(stmt, (unsigned char *)test_sql, SQL_NTS);
750  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
751  obj->up = 0;
752  } else {
753  res = SQLExecute(stmt);
754  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
755  obj->up = 0;
756  }
757  }
758  }
759  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
760  }
761 
762  if (!obj->up && !obj->tx) { /* Try to reconnect! */
763  ast_log(LOG_WARNING, "Connection is down attempting to reconnect...\n");
764  odbc_obj_disconnect(obj);
765  odbc_obj_connect(obj);
766  }
767  return obj->up;
768 }
769 
770 static int load_odbc_config(void)
771 {
772  static char *cfg = "res_odbc.conf";
773  struct ast_config *config;
774  struct ast_variable *v;
775  char *cat;
776  const char *dsn, *username, *password, *sanitysql;
778  struct timeval ncache = { 0, 0 };
779  unsigned int idlecheck;
780  int preconnect = 0, res = 0;
781  struct ast_flags config_flags = { 0 };
782 
783  struct odbc_class *new;
784 
785  config = ast_config_load(cfg, config_flags);
786  if (config == CONFIG_STATUS_FILEMISSING || config == CONFIG_STATUS_FILEINVALID) {
787  ast_log(LOG_WARNING, "Unable to load config file res_odbc.conf\n");
788  return -1;
789  }
790  for (cat = ast_category_browse(config, NULL); cat; cat=ast_category_browse(config, cat)) {
791  if (!strcasecmp(cat, "ENV")) {
792  for (v = ast_variable_browse(config, cat); v; v = v->next) {
793  setenv(v->name, v->value, 1);
794  ast_log(LOG_NOTICE, "Adding ENV var: %s=%s\n", v->name, v->value);
795  }
796  } else {
797  /* Reset all to defaults for each class of odbc connections */
798  dsn = username = password = sanitysql = NULL;
799  enabled = 1;
800  preconnect = idlecheck = 0;
801  pooling = 0;
802  limit = 0;
803  bse = 1;
804  conntimeout = 10;
805  forcecommit = 0;
806  allow_empty_strings = 1;
807  isolation = SQL_TXN_READ_COMMITTED;
808  for (v = ast_variable_browse(config, cat); v; v = v->next) {
809  if (!strcasecmp(v->name, "pooling")) {
810  if (ast_true(v->value))
811  pooling = 1;
812  } else if (!strncasecmp(v->name, "share", 5)) {
813  /* "shareconnections" is a little clearer in meaning than "pooling" */
814  if (ast_false(v->value))
815  pooling = 1;
816  } else if (!strcasecmp(v->name, "limit")) {
817  sscanf(v->value, "%30d", &limit);
818  if (ast_true(v->value) && !limit) {
819  ast_log(LOG_WARNING, "Limit should be a number, not a boolean: '%s'. Setting limit to 1023 for ODBC class '%s'.\n", v->value, cat);
820  limit = 1023;
821  } else if (ast_false(v->value)) {
822  ast_log(LOG_WARNING, "Limit should be a number, not a boolean: '%s'. Disabling ODBC class '%s'.\n", v->value, cat);
823  enabled = 0;
824  break;
825  }
826  } else if (!strcasecmp(v->name, "idlecheck")) {
827  sscanf(v->value, "%30u", &idlecheck);
828  } else if (!strcasecmp(v->name, "enabled")) {
829  enabled = ast_true(v->value);
830  } else if (!strcasecmp(v->name, "pre-connect")) {
831  preconnect = ast_true(v->value);
832  } else if (!strcasecmp(v->name, "dsn")) {
833  dsn = v->value;
834  } else if (!strcasecmp(v->name, "username")) {
835  username = v->value;
836  } else if (!strcasecmp(v->name, "password")) {
837  password = v->value;
838  } else if (!strcasecmp(v->name, "sanitysql")) {
839  sanitysql = v->value;
840  } else if (!strcasecmp(v->name, "backslash_is_escape")) {
841  bse = ast_true(v->value);
842  } else if (!strcasecmp(v->name, "allow_empty_string_in_nontext")) {
843  allow_empty_strings = ast_true(v->value);
844  } else if (!strcasecmp(v->name, "connect_timeout")) {
845  if (sscanf(v->value, "%d", &conntimeout) != 1 || conntimeout < 1) {
846  ast_log(LOG_WARNING, "connect_timeout must be a positive integer\n");
847  conntimeout = 10;
848  }
849  } else if (!strcasecmp(v->name, "negative_connection_cache")) {
850  double dncache;
851  if (sscanf(v->value, "%lf", &dncache) != 1 || dncache < 0) {
852  ast_log(LOG_WARNING, "negative_connection_cache must be a non-negative integer\n");
853  /* 5 minutes sounds like a reasonable default */
854  ncache.tv_sec = 300;
855  ncache.tv_usec = 0;
856  } else {
857  ncache.tv_sec = (int)dncache;
858  ncache.tv_usec = (dncache - ncache.tv_sec) * 1000000;
859  }
860  } else if (!strcasecmp(v->name, "forcecommit")) {
861  forcecommit = ast_true(v->value);
862  } else if (!strcasecmp(v->name, "isolation")) {
863  if ((isolation = text2isolation(v->value)) == 0) {
864  ast_log(LOG_ERROR, "Unrecognized value for 'isolation': '%s' in section '%s'\n", v->value, cat);
865  isolation = SQL_TXN_READ_COMMITTED;
866  }
867  }
868  }
869 
870  if (enabled && !ast_strlen_zero(dsn)) {
871  new = ao2_alloc(sizeof(*new), odbc_class_destructor);
872 
873  if (!new) {
874  res = -1;
875  break;
876  }
877 
878  SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &new->env);
879  res = SQLSetEnvAttr(new->env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
880 
881  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
882  ast_log(LOG_WARNING, "res_odbc: Error SetEnv\n");
883  ao2_ref(new, -1);
884  return res;
885  }
886 
887  new->obj_container = ao2_container_alloc(1, null_hash_fn, ao2_match_by_addr);
888 
889  if (pooling) {
890  new->haspool = pooling;
891  if (limit) {
892  new->limit = limit;
893  } else {
894  ast_log(LOG_WARNING, "Pooling without also setting a limit is pointless. Changing limit from 0 to 5.\n");
895  new->limit = 5;
896  }
897  }
898 
899  new->backslash_is_escape = bse ? 1 : 0;
900  new->forcecommit = forcecommit ? 1 : 0;
901  new->isolation = isolation;
902  new->idlecheck = idlecheck;
903  new->conntimeout = conntimeout;
904  new->negative_connection_cache = ncache;
905  new->allow_empty_strings = allow_empty_strings ? 1 : 0;
906 
907  if (cat)
908  ast_copy_string(new->name, cat, sizeof(new->name));
909  if (dsn)
910  ast_copy_string(new->dsn, dsn, sizeof(new->dsn));
911  if (username && !(new->username = ast_strdup(username))) {
912  ao2_ref(new, -1);
913  break;
914  }
915  if (password && !(new->password = ast_strdup(password))) {
916  ao2_ref(new, -1);
917  break;
918  }
919  if (sanitysql && !(new->sanitysql = ast_strdup(sanitysql))) {
920  ao2_ref(new, -1);
921  break;
922  }
923 
924  odbc_register_class(new, preconnect);
925  ast_log(LOG_NOTICE, "Registered ODBC class '%s' dsn->[%s]\n", cat, dsn);
926  ao2_ref(new, -1);
927  new = NULL;
928  }
929  }
930  }
931  ast_config_destroy(config);
932  return res;
933 }
934 
935 static char *handle_cli_odbc_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
936 {
937  struct ao2_iterator aoi;
938  struct odbc_class *class;
939  struct odbc_obj *current;
940  int length = 0;
941  int which = 0;
942  char *ret = NULL;
943 
944  switch (cmd) {
945  case CLI_INIT:
946  e->command = "odbc show";
947  e->usage =
948  "Usage: odbc show [class]\n"
949  " List settings of a particular ODBC class or,\n"
950  " if not specified, all classes.\n";
951  return NULL;
952  case CLI_GENERATE:
953  if (a->pos != 2)
954  return NULL;
955  length = strlen(a->word);
957  while ((class = ao2_iterator_next(&aoi))) {
958  if (!strncasecmp(a->word, class->name, length) && ++which > a->n) {
959  ret = ast_strdup(class->name);
960  }
961  ao2_ref(class, -1);
962  if (ret) {
963  break;
964  }
965  }
966  ao2_iterator_destroy(&aoi);
967  if (!ret && !strncasecmp(a->word, "all", length) && ++which > a->n) {
968  ret = ast_strdup("all");
969  }
970  return ret;
971  }
972 
973  ast_cli(a->fd, "\nODBC DSN Settings\n");
974  ast_cli(a->fd, "-----------------\n\n");
976  while ((class = ao2_iterator_next(&aoi))) {
977  if ((a->argc == 2) || (a->argc == 3 && !strcmp(a->argv[2], "all")) || (!strcmp(a->argv[2], class->name))) {
978  int count = 0;
979  char timestr[80];
980  struct ast_tm tm;
981 
982  ast_localtime(&class->last_negative_connect, &tm, NULL);
983  ast_strftime(timestr, sizeof(timestr), "%Y-%m-%d %T", &tm);
984  ast_cli(a->fd, " Name: %s\n DSN: %s\n", class->name, class->dsn);
985  ast_cli(a->fd, " Last connection attempt: %s\n", timestr);
986 
987  if (class->haspool) {
988  struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0);
989 
990  ast_cli(a->fd, " Pooled: Yes\n Limit: %u\n Connections in use: %d\n", class->limit, class->count);
991 
992  while ((current = ao2_iterator_next(&aoi2))) {
993  ast_mutex_lock(&current->lock);
994 #ifdef DEBUG_THREADS
995  ast_cli(a->fd, " - Connection %d: %s (%s:%d %s)\n", ++count,
996  current->used ? "in use" :
997  current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected",
998  current->file, current->lineno, current->function);
999 #else
1000  ast_cli(a->fd, " - Connection %d: %s\n", ++count,
1001  current->used ? "in use" :
1002  current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected");
1003 #endif
1004  ast_mutex_unlock(&current->lock);
1005  ao2_ref(current, -1);
1006  }
1007  ao2_iterator_destroy(&aoi2);
1008  } else {
1009  /* Should only ever be one of these (unless there are transactions) */
1010  struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0);
1011  while ((current = ao2_iterator_next(&aoi2))) {
1012  ast_cli(a->fd, " Pooled: No\n Connected: %s\n", current->used ? "In use" :
1013  current->up && ast_odbc_sanity_check(current) ? "Yes" : "No");
1014  ao2_ref(current, -1);
1015  }
1016  ao2_iterator_destroy(&aoi2);
1017  }
1018  ast_cli(a->fd, "\n");
1019  }
1020  ao2_ref(class, -1);
1021  }
1022  ao2_iterator_destroy(&aoi);
1023 
1024  return CLI_SUCCESS;
1025 }
1026 
1027 static struct ast_cli_entry cli_odbc[] = {
1028  AST_CLI_DEFINE(handle_cli_odbc_show, "List ODBC DSN(s)")
1029 };
1030 
1031 static int odbc_register_class(struct odbc_class *class, int preconnect)
1032 {
1033  struct odbc_obj *obj;
1034  if (class) {
1035  ao2_link(class_container, class);
1036  /* I still have a reference in the caller, so a deref is NOT missing here. */
1037 
1038  if (preconnect) {
1039  /* Request and release builds a connection */
1040  obj = ast_odbc_request_obj(class->name, 0);
1041  if (obj) {
1042  ast_odbc_release_obj(obj);
1043  }
1044  }
1045 
1046  return 0;
1047  } else {
1048  ast_log(LOG_WARNING, "Attempted to register a NULL class?\n");
1049  return -1;
1050  }
1051 }
1052 
1053 static void odbc_release_obj2(struct odbc_obj *obj, struct odbc_txn_frame *tx)
1054 {
1055  SQLINTEGER nativeerror=0, numfields=0;
1056  SQLSMALLINT diagbytes=0, i;
1057  unsigned char state[10], diagnostic[256];
1058 
1059  ast_debug(2, "odbc_release_obj2(%p) called (obj->txf = %p)\n", obj, obj->txf);
1060  if (tx) {
1061  ast_debug(1, "called on a transactional handle with %s\n", tx->forcecommit ? "COMMIT" : "ROLLBACK");
1062  if (SQLEndTran(SQL_HANDLE_DBC, obj->con, tx->forcecommit ? SQL_COMMIT : SQL_ROLLBACK) == SQL_ERROR) {
1063  /* Handle possible transaction commit failure */
1064  SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
1065  for (i = 0; i < numfields; i++) {
1066  SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
1067  ast_log(LOG_WARNING, "SQLEndTran returned an error: %s: %s\n", state, diagnostic);
1068  if (!strcmp((char *)state, "25S02") || !strcmp((char *)state, "08007")) {
1069  /* These codes mean that a commit failed and a transaction
1070  * is still active. We must rollback, or things will get
1071  * very, very weird for anybody using the handle next. */
1072  SQLEndTran(SQL_HANDLE_DBC, obj->con, SQL_ROLLBACK);
1073  }
1074  if (i > 10) {
1075  ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
1076  break;
1077  }
1078  }
1079  }
1080 
1081  /* Transaction is done, reset autocommit */
1082  if (SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_ON, 0) == SQL_ERROR) {
1083  SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
1084  for (i = 0; i < numfields; i++) {
1085  SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
1086  ast_log(LOG_WARNING, "SetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic);
1087  if (i > 10) {
1088  ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
1089  break;
1090  }
1091  }
1092  }
1093  }
1094 
1095 #ifdef DEBUG_THREADS
1096  obj->file[0] = '\0';
1097  obj->function[0] = '\0';
1098  obj->lineno = 0;
1099 #endif
1100 
1101  /* For pooled connections, this frees the connection to be
1102  * reused. For non-pooled connections, it does nothing. */
1103  obj->used = 0;
1104  if (obj->txf) {
1105  /* Prevent recursion -- transaction is already closed out. */
1106  obj->txf->obj = NULL;
1107  obj->txf = release_transaction(obj->txf);
1108  }
1109  ao2_ref(obj, -1);
1110 }
1111 
1113 {
1114  struct odbc_txn_frame *tx = find_transaction(NULL, obj, NULL, 0);
1115  odbc_release_obj2(obj, tx);
1116 }
1117 
1119 {
1120  return obj->parent->backslash_is_escape;
1121 }
1122 
1124 {
1125  return obj->parent->allow_empty_strings;
1126 }
1127 
1128 static int commit_exec(struct ast_channel *chan, const char *data)
1129 {
1130  struct odbc_txn_frame *tx;
1131  SQLINTEGER nativeerror=0, numfields=0;
1132  SQLSMALLINT diagbytes=0, i;
1133  unsigned char state[10], diagnostic[256];
1134 
1135  if (ast_strlen_zero(data)) {
1136  tx = find_transaction(chan, NULL, NULL, 1);
1137  } else {
1138  tx = find_transaction(chan, NULL, data, 0);
1139  }
1140 
1141  pbx_builtin_setvar_helper(chan, "COMMIT_RESULT", "OK");
1142 
1143  if (tx) {
1144  if (SQLEndTran(SQL_HANDLE_DBC, tx->obj->con, SQL_COMMIT) == SQL_ERROR) {
1145  struct ast_str *errors = ast_str_thread_get(&errors_buf, 16);
1146  ast_str_reset(errors);
1147 
1148  /* Handle possible transaction commit failure */
1149  SQLGetDiagField(SQL_HANDLE_DBC, tx->obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
1150  for (i = 0; i < numfields; i++) {
1151  SQLGetDiagRec(SQL_HANDLE_DBC, tx->obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
1152  ast_str_append(&errors, 0, "%s%s", ast_str_strlen(errors) ? "," : "", state);
1153  ast_log(LOG_WARNING, "SQLEndTran returned an error: %s: %s\n", state, diagnostic);
1154  if (i > 10) {
1155  ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
1156  break;
1157  }
1158  }
1159  pbx_builtin_setvar_helper(chan, "COMMIT_RESULT", ast_str_buffer(errors));
1160  }
1161  }
1162  return 0;
1163 }
1164 
1165 static int rollback_exec(struct ast_channel *chan, const char *data)
1166 {
1167  struct odbc_txn_frame *tx;
1168  SQLINTEGER nativeerror=0, numfields=0;
1169  SQLSMALLINT diagbytes=0, i;
1170  unsigned char state[10], diagnostic[256];
1171 
1172  if (ast_strlen_zero(data)) {
1173  tx = find_transaction(chan, NULL, NULL, 1);
1174  } else {
1175  tx = find_transaction(chan, NULL, data, 0);
1176  }
1177 
1178  pbx_builtin_setvar_helper(chan, "ROLLBACK_RESULT", "OK");
1179 
1180  if (tx) {
1181  if (SQLEndTran(SQL_HANDLE_DBC, tx->obj->con, SQL_ROLLBACK) == SQL_ERROR) {
1182  struct ast_str *errors = ast_str_thread_get(&errors_buf, 16);
1183  ast_str_reset(errors);
1184 
1185  /* Handle possible transaction commit failure */
1186  SQLGetDiagField(SQL_HANDLE_DBC, tx->obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
1187  for (i = 0; i < numfields; i++) {
1188  SQLGetDiagRec(SQL_HANDLE_DBC, tx->obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
1189  ast_str_append(&errors, 0, "%s%s", ast_str_strlen(errors) ? "," : "", state);
1190  ast_log(LOG_WARNING, "SQLEndTran returned an error: %s: %s\n", state, diagnostic);
1191  if (i > 10) {
1192  ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
1193  break;
1194  }
1195  }
1196  pbx_builtin_setvar_helper(chan, "ROLLBACK_RESULT", ast_str_buffer(errors));
1197  }
1198  }
1199  return 0;
1200 }
1201 
1202 static int aoro2_class_cb(void *obj, void *arg, int flags)
1203 {
1204  struct odbc_class *class = obj;
1205  char *name = arg;
1206  if (!strcmp(class->name, name) && !class->delme) {
1207  return CMP_MATCH | CMP_STOP;
1208  }
1209  return 0;
1210 }
1211 
1212 #define USE_TX (void *)(long)1
1213 #define NO_TX (void *)(long)2
1214 #define EOR_TX (void *)(long)3
1215 
1216 static int aoro2_obj_cb(void *vobj, void *arg, int flags)
1217 {
1218  struct odbc_obj *obj = vobj;
1219  ast_mutex_lock(&obj->lock);
1220  if ((arg == NO_TX && !obj->tx) || (arg == EOR_TX && !obj->used) || (arg == USE_TX && obj->tx && !obj->used)) {
1221  obj->used = 1;
1222  ast_mutex_unlock(&obj->lock);
1223  return CMP_MATCH | CMP_STOP;
1224  }
1225  ast_mutex_unlock(&obj->lock);
1226  return 0;
1227 }
1228 
1229 /* This function should only be called for shared connections. Otherwise, the lack of
1230  * setting vobj->used breaks EOR_TX searching. For nonshared connections, use
1231  * aoro2_obj_cb instead. */
1232 static int aoro2_obj_notx_cb(void *vobj, void *arg, int flags)
1233 {
1234  struct odbc_obj *obj = vobj;
1235  if (!obj->tx) {
1236  return CMP_MATCH | CMP_STOP;
1237  }
1238  return 0;
1239 }
1240 
1241 struct odbc_obj *_ast_odbc_request_obj2(const char *name, struct ast_flags flags, const char *file, const char *function, int lineno)
1242 {
1243  struct odbc_obj *obj = NULL;
1244  struct odbc_class *class;
1245  SQLINTEGER nativeerror=0, numfields=0;
1246  SQLSMALLINT diagbytes=0, i;
1247  unsigned char state[10], diagnostic[256];
1248 
1249  if (!(class = ao2_callback(class_container, 0, aoro2_class_cb, (char *) name))) {
1250  ast_debug(1, "Class '%s' not found!\n", name);
1251  return NULL;
1252  }
1253 
1254  ast_assert(ao2_ref(class, 0) > 1);
1255 
1256  if (class->haspool) {
1257  /* Recycle connections before building another */
1258  obj = ao2_callback(class->obj_container, 0, aoro2_obj_cb, EOR_TX);
1259 
1260  if (obj) {
1261  ast_assert(ao2_ref(obj, 0) > 1);
1262  }
1263  if (!obj && (ast_atomic_fetchadd_int(&class->count, +1) < class->limit) &&
1264  (time(NULL) > class->last_negative_connect.tv_sec + class->negative_connection_cache.tv_sec)) {
1265  obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor);
1266  if (!obj) {
1267  class->count--;
1268  ao2_ref(class, -1);
1269  ast_debug(3, "Unable to allocate object\n");
1270  ast_atomic_fetchadd_int(&class->count, -1);
1271  return NULL;
1272  }
1273  ast_assert(ao2_ref(obj, 0) == 1);
1274  ast_mutex_init(&obj->lock);
1275  /* obj inherits the outstanding reference to class */
1276  obj->parent = class;
1277  class = NULL;
1278  if (odbc_obj_connect(obj) == ODBC_FAIL) {
1279  ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
1280  ast_assert(ao2_ref(obj->parent, 0) > 0);
1281  /* Because it was never within the container, we have to manually decrement the count here */
1282  ast_atomic_fetchadd_int(&obj->parent->count, -1);
1283  ao2_ref(obj, -1);
1284  obj = NULL;
1285  } else {
1286  obj->used = 1;
1287  ao2_link(obj->parent->obj_container, obj);
1288  }
1289  } else {
1290  /* If construction fails due to the limit (or negative timecache), reverse our increment. */
1291  if (!obj) {
1292  ast_atomic_fetchadd_int(&class->count, -1);
1293  }
1294  /* Object is not constructed, so delete outstanding reference to class. */
1295  ao2_ref(class, -1);
1296  class = NULL;
1297  }
1298 
1299  if (!obj) {
1300  return NULL;
1301  }
1302 
1303  ast_mutex_lock(&obj->lock);
1304 
1306  /* Ensure this connection has autocommit turned off. */
1307  if (SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_OFF, 0) == SQL_ERROR) {
1308  SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
1309  for (i = 0; i < numfields; i++) {
1310  SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
1311  ast_log(LOG_WARNING, "SQLSetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic);
1312  if (i > 10) {
1313  ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
1314  break;
1315  }
1316  }
1317  }
1318  }
1319  } else if (ast_test_flag(&flags, RES_ODBC_INDEPENDENT_CONNECTION)) {
1320  /* Non-pooled connections -- but must use a separate connection handle */
1321  if (!(obj = ao2_callback(class->obj_container, 0, aoro2_obj_cb, USE_TX))) {
1322  ast_debug(1, "Object not found\n");
1323  obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor);
1324  if (!obj) {
1325  ao2_ref(class, -1);
1326  ast_debug(3, "Unable to allocate object\n");
1327  return NULL;
1328  }
1329  ast_mutex_init(&obj->lock);
1330  /* obj inherits the outstanding reference to class */
1331  obj->parent = class;
1332  class = NULL;
1333  if (odbc_obj_connect(obj) == ODBC_FAIL) {
1334  ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
1335  ao2_ref(obj, -1);
1336  obj = NULL;
1337  } else {
1338  obj->used = 1;
1339  ao2_link(obj->parent->obj_container, obj);
1340  ast_atomic_fetchadd_int(&obj->parent->count, +1);
1341  }
1342  }
1343 
1344  if (!obj) {
1345  return NULL;
1346  }
1347 
1348  ast_mutex_lock(&obj->lock);
1349 
1350  if (SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_OFF, 0) == SQL_ERROR) {
1351  SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
1352  for (i = 0; i < numfields; i++) {
1353  SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
1354  ast_log(LOG_WARNING, "SetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic);
1355  if (i > 10) {
1356  ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
1357  break;
1358  }
1359  }
1360  }
1361  } else {
1362  /* Non-pooled connection: multiple modules can use the same connection. */
1363  if ((obj = ao2_callback(class->obj_container, 0, aoro2_obj_notx_cb, NO_TX))) {
1364  /* Object is not constructed, so delete outstanding reference to class. */
1365  ast_assert(ao2_ref(class, 0) > 1);
1366  ao2_ref(class, -1);
1367  class = NULL;
1368  } else {
1369  /* No entry: build one */
1370  if (!(obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor))) {
1371  ast_assert(ao2_ref(class, 0) > 1);
1372  ao2_ref(class, -1);
1373  ast_debug(3, "Unable to allocate object\n");
1374  return NULL;
1375  }
1376  ast_mutex_init(&obj->lock);
1377  /* obj inherits the outstanding reference to class */
1378  obj->parent = class;
1379  class = NULL;
1380  if (odbc_obj_connect(obj) == ODBC_FAIL) {
1381  ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
1382  ao2_ref(obj, -1);
1383  obj = NULL;
1384  } else {
1385  ao2_link(obj->parent->obj_container, obj);
1386  ast_assert(ao2_ref(obj, 0) > 1);
1387  }
1388  }
1389 
1390  if (!obj) {
1391  return NULL;
1392  }
1393 
1394  ast_mutex_lock(&obj->lock);
1395 
1396  if (SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_ON, 0) == SQL_ERROR) {
1397  SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
1398  for (i = 0; i < numfields; i++) {
1399  SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
1400  ast_log(LOG_WARNING, "SetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic);
1401  if (i > 10) {
1402  ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
1403  break;
1404  }
1405  }
1406  }
1407  }
1408 
1409  ast_assert(obj != NULL);
1410 
1411  /* Set the isolation property */
1412  if (SQLSetConnectAttr(obj->con, SQL_ATTR_TXN_ISOLATION, (void *)(long)obj->parent->isolation, 0) == SQL_ERROR) {
1413  SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
1414  for (i = 0; i < numfields; i++) {
1415  SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
1416  ast_log(LOG_WARNING, "SetConnectAttr (Txn isolation) returned an error: %s: %s\n", state, diagnostic);
1417  if (i > 10) {
1418  ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
1419  break;
1420  }
1421  }
1422  }
1423 
1424  if (ast_test_flag(&flags, RES_ODBC_CONNECTED) && !obj->up) {
1425  /* Check if this connection qualifies for reconnection, with negative connection cache time */
1426  if (time(NULL) > obj->parent->last_negative_connect.tv_sec + obj->parent->negative_connection_cache.tv_sec) {
1427  odbc_obj_connect(obj);
1428  }
1429  } else if (ast_test_flag(&flags, RES_ODBC_SANITY_CHECK)) {
1430  ast_odbc_sanity_check(obj);
1431  } else if (obj->parent->idlecheck > 0 && ast_tvdiff_sec(ast_tvnow(), obj->last_used) > obj->parent->idlecheck) {
1432  odbc_obj_connect(obj);
1433  }
1434 
1435 #ifdef DEBUG_THREADS
1436  ast_copy_string(obj->file, file, sizeof(obj->file));
1437  ast_copy_string(obj->function, function, sizeof(obj->function));
1438  obj->lineno = lineno;
1439 #endif
1440 
1441  /* We had it locked because of the obj_connects we see here. */
1442  ast_mutex_unlock(&obj->lock);
1443 
1444  ast_assert(class == NULL);
1445 
1446  ast_assert(ao2_ref(obj, 0) > 1);
1447  return obj;
1448 }
1449 
1450 struct odbc_obj *_ast_odbc_request_obj(const char *name, int check, const char *file, const char *function, int lineno)
1451 {
1452  struct ast_flags flags = { check ? RES_ODBC_SANITY_CHECK : 0 };
1453  return _ast_odbc_request_obj2(name, flags, file, function, lineno);
1454 }
1455 
1456 struct odbc_obj *ast_odbc_retrieve_transaction_obj(struct ast_channel *chan, const char *objname)
1457 {
1458  struct ast_datastore *txn_store;
1459  AST_LIST_HEAD(, odbc_txn_frame) *oldlist;
1460  struct odbc_txn_frame *txn = NULL;
1461 
1462  if (!chan) {
1463  /* No channel == no transaction */
1464  return NULL;
1465  }
1466 
1467  ast_channel_lock(chan);
1468  if ((txn_store = ast_channel_datastore_find(chan, &txn_info, NULL))) {
1469  oldlist = txn_store->data;
1470  } else {
1471  ast_channel_unlock(chan);
1472  return NULL;
1473  }
1474 
1475  AST_LIST_LOCK(oldlist);
1476  ast_channel_unlock(chan);
1477 
1478  AST_LIST_TRAVERSE(oldlist, txn, list) {
1479  if (txn->obj && txn->obj->parent && !strcmp(txn->obj->parent->name, objname)) {
1480  AST_LIST_UNLOCK(oldlist);
1481  return txn->obj;
1482  }
1483  }
1484  AST_LIST_UNLOCK(oldlist);
1485  return NULL;
1486 }
1487 
1489 {
1490  int res;
1491  SQLINTEGER err;
1492  short int mlen;
1493  unsigned char msg[200], state[10];
1494  SQLHDBC con;
1495 
1496  /* Nothing to disconnect */
1497  if (!obj->con) {
1498  return ODBC_SUCCESS;
1499  }
1500 
1501  con = obj->con;
1502  obj->con = NULL;
1503  res = SQLDisconnect(con);
1504 
1505  if (obj->parent) {
1506  if (res == SQL_SUCCESS || res == SQL_SUCCESS_WITH_INFO) {
1507  ast_log(LOG_DEBUG, "Disconnected %d from %s [%s]\n", res, obj->parent->name, obj->parent->dsn);
1508  } else {
1509  ast_log(LOG_DEBUG, "res_odbc: %s [%s] already disconnected\n", obj->parent->name, obj->parent->dsn);
1510  }
1511  }
1512 
1513  if ((res = SQLFreeHandle(SQL_HANDLE_DBC, con) == SQL_SUCCESS)) {
1514  ast_log(LOG_DEBUG, "Database handle %p deallocated\n", con);
1515  } else {
1516  SQLGetDiagRec(SQL_HANDLE_DBC, con, 1, state, &err, msg, 100, &mlen);
1517  ast_log(LOG_WARNING, "Unable to deallocate database handle %p? %d errno=%d %s\n", con, res, (int)err, msg);
1518  }
1519 
1520  obj->up = 0;
1521  return ODBC_SUCCESS;
1522 }
1523 
1525 {
1526  int res;
1527  SQLINTEGER err;
1528  short int mlen;
1529  unsigned char msg[200], state[10];
1530 #ifdef NEEDTRACE
1531  SQLINTEGER enable = 1;
1532  char *tracefile = "/tmp/odbc.trace";
1533 #endif
1534  SQLHDBC con;
1535 
1536  if (obj->up) {
1537  odbc_obj_disconnect(obj);
1538  ast_log(LOG_NOTICE, "Re-connecting %s\n", obj->parent->name);
1539  } else {
1540  ast_assert(obj->con == NULL);
1541  ast_log(LOG_NOTICE, "Connecting %s\n", obj->parent->name);
1542  }
1543 
1544  res = SQLAllocHandle(SQL_HANDLE_DBC, obj->parent->env, &con);
1545 
1546  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1547  ast_log(LOG_WARNING, "res_odbc: Error AllocHDB %d\n", res);
1549  return ODBC_FAIL;
1550  }
1551  SQLSetConnectAttr(con, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)(long) obj->parent->conntimeout, 0);
1552  SQLSetConnectAttr(con, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER *)(long) obj->parent->conntimeout, 0);
1553 #ifdef NEEDTRACE
1554  SQLSetConnectAttr(con, SQL_ATTR_TRACE, &enable, SQL_IS_INTEGER);
1555  SQLSetConnectAttr(con, SQL_ATTR_TRACEFILE, tracefile, strlen(tracefile));
1556 #endif
1557 
1558  res = SQLConnect(con,
1559  (SQLCHAR *) obj->parent->dsn, SQL_NTS,
1560  (SQLCHAR *) obj->parent->username, SQL_NTS,
1561  (SQLCHAR *) obj->parent->password, SQL_NTS);
1562 
1563  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1564  SQLGetDiagRec(SQL_HANDLE_DBC, con, 1, state, &err, msg, 100, &mlen);
1566  ast_log(LOG_WARNING, "res_odbc: Error SQLConnect=%d errno=%d %s\n", res, (int)err, msg);
1567  if ((res = SQLFreeHandle(SQL_HANDLE_DBC, con) != SQL_SUCCESS)) {
1568  SQLGetDiagRec(SQL_HANDLE_DBC, con, 1, state, &err, msg, 100, &mlen);
1569  ast_log(LOG_WARNING, "Unable to deallocate database handle %p? %d errno=%d %s\n", con, res, (int)err, msg);
1570  }
1571  return ODBC_FAIL;
1572  } else {
1573  ast_log(LOG_NOTICE, "res_odbc: Connected to %s [%s]\n", obj->parent->name, obj->parent->dsn);
1574  obj->up = 1;
1575  obj->last_used = ast_tvnow();
1576  }
1577 
1578  obj->con = con;
1579  return ODBC_SUCCESS;
1580 }
1581 
1582 static int acf_transaction_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1583 {
1585  AST_APP_ARG(property);
1586  AST_APP_ARG(opt);
1587  );
1588  struct odbc_txn_frame *tx;
1589 
1590  AST_STANDARD_APP_ARGS(args, data);
1591  if (strcasecmp(args.property, "transaction") == 0) {
1592  if ((tx = find_transaction(chan, NULL, NULL, 1))) {
1593  ast_copy_string(buf, tx->name, len);
1594  return 0;
1595  }
1596  } else if (strcasecmp(args.property, "isolation") == 0) {
1597  if (!ast_strlen_zero(args.opt)) {
1598  tx = find_transaction(chan, NULL, args.opt, 0);
1599  } else {
1600  tx = find_transaction(chan, NULL, NULL, 1);
1601  }
1602  if (tx) {
1603  ast_copy_string(buf, isolation2text(tx->isolation), len);
1604  return 0;
1605  }
1606  } else if (strcasecmp(args.property, "forcecommit") == 0) {
1607  if (!ast_strlen_zero(args.opt)) {
1608  tx = find_transaction(chan, NULL, args.opt, 0);
1609  } else {
1610  tx = find_transaction(chan, NULL, NULL, 1);
1611  }
1612  if (tx) {
1613  ast_copy_string(buf, tx->forcecommit ? "1" : "0", len);
1614  return 0;
1615  }
1616  }
1617  return -1;
1618 }
1619 
1620 static int acf_transaction_write(struct ast_channel *chan, const char *cmd, char *s, const char *value)
1621 {
1623  AST_APP_ARG(property);
1624  AST_APP_ARG(opt);
1625  );
1626  struct odbc_txn_frame *tx;
1627  SQLINTEGER nativeerror=0, numfields=0;
1628  SQLSMALLINT diagbytes=0, i;
1629  unsigned char state[10], diagnostic[256];
1630 
1632  if (strcasecmp(args.property, "transaction") == 0) {
1633  /* Set active transaction */
1634  struct odbc_obj *obj;
1635  if ((tx = find_transaction(chan, NULL, value, 0))) {
1636  mark_transaction_active(chan, tx);
1637  } else {
1638  /* No such transaction, create one */
1639  struct ast_flags flags = { RES_ODBC_INDEPENDENT_CONNECTION };
1640  if (ast_strlen_zero(args.opt) || !(obj = ast_odbc_request_obj2(args.opt, flags))) {
1641  ast_log(LOG_ERROR, "Could not create transaction: invalid database specification '%s'\n", S_OR(args.opt, ""));
1642  pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "INVALID_DB");
1643  return -1;
1644  }
1645  if (!find_transaction(chan, obj, value, 0)) {
1646  pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "FAILED_TO_CREATE");
1647  return -1;
1648  }
1649  obj->tx = 1;
1650  }
1651  pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "OK");
1652  return 0;
1653  } else if (strcasecmp(args.property, "forcecommit") == 0) {
1654  /* Set what happens when an uncommitted transaction ends without explicit Commit or Rollback */
1655  if (ast_strlen_zero(args.opt)) {
1656  tx = find_transaction(chan, NULL, NULL, 1);
1657  } else {
1658  tx = find_transaction(chan, NULL, args.opt, 0);
1659  }
1660  if (!tx) {
1661  pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "FAILED_TO_CREATE");
1662  return -1;
1663  }
1664  if (ast_true(value)) {
1665  tx->forcecommit = 1;
1666  } else if (ast_false(value)) {
1667  tx->forcecommit = 0;
1668  } else {
1669  ast_log(LOG_ERROR, "Invalid value for forcecommit: '%s'\n", S_OR(value, ""));
1670  pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "INVALID_VALUE");
1671  return -1;
1672  }
1673 
1674  pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "OK");
1675  return 0;
1676  } else if (strcasecmp(args.property, "isolation") == 0) {
1677  /* How do uncommitted transactions affect reads? */
1678  int isolation = text2isolation(value);
1679  if (ast_strlen_zero(args.opt)) {
1680  tx = find_transaction(chan, NULL, NULL, 1);
1681  } else {
1682  tx = find_transaction(chan, NULL, args.opt, 0);
1683  }
1684  if (!tx) {
1685  pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "FAILED_TO_CREATE");
1686  return -1;
1687  }
1688  if (isolation == 0) {
1689  pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "INVALID_VALUE");
1690  ast_log(LOG_ERROR, "Invalid isolation specification: '%s'\n", S_OR(value, ""));
1691  } else if (SQLSetConnectAttr(tx->obj->con, SQL_ATTR_TXN_ISOLATION, (void *)(long)isolation, 0) == SQL_ERROR) {
1692  pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "SQL_ERROR");
1693  SQLGetDiagField(SQL_HANDLE_DBC, tx->obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
1694  for (i = 0; i < numfields; i++) {
1695  SQLGetDiagRec(SQL_HANDLE_DBC, tx->obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
1696  ast_log(LOG_WARNING, "SetConnectAttr (Txn isolation) returned an error: %s: %s\n", state, diagnostic);
1697  if (i > 10) {
1698  ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
1699  break;
1700  }
1701  }
1702  } else {
1703  pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "OK");
1704  tx->isolation = isolation;
1705  }
1706  return 0;
1707  } else {
1708  ast_log(LOG_ERROR, "Unknown property: '%s'\n", args.property);
1709  return -1;
1710  }
1711 }
1712 
1714  .name = "ODBC",
1715  .read = acf_transaction_read,
1716  .write = acf_transaction_write,
1717 };
1718 
1719 static const char * const app_commit = "ODBC_Commit";
1720 static const char * const app_rollback = "ODBC_Rollback";
1721 
1722 /*!
1723  * \internal
1724  * \brief Implements the channels provider.
1725  */
1726 static int data_odbc_provider_handler(const struct ast_data_search *search,
1727  struct ast_data *root)
1728 {
1729  struct ao2_iterator aoi, aoi2;
1730  struct odbc_class *class;
1731  struct odbc_obj *current;
1732  struct ast_data *data_odbc_class, *data_odbc_connections, *data_odbc_connection;
1733  struct ast_data *enum_node;
1734  int count;
1735 
1737  while ((class = ao2_iterator_next(&aoi))) {
1738  data_odbc_class = ast_data_add_node(root, "class");
1739  if (!data_odbc_class) {
1740  ao2_ref(class, -1);
1741  continue;
1742  }
1743 
1744  ast_data_add_structure(odbc_class, data_odbc_class, class);
1745 
1746  if (!ao2_container_count(class->obj_container)) {
1747  ao2_ref(class, -1);
1748  continue;
1749  }
1750 
1751  data_odbc_connections = ast_data_add_node(data_odbc_class, "connections");
1752  if (!data_odbc_connections) {
1753  ao2_ref(class, -1);
1754  continue;
1755  }
1756 
1757  ast_data_add_bool(data_odbc_class, "shared", !class->haspool);
1758  /* isolation */
1759  enum_node = ast_data_add_node(data_odbc_class, "isolation");
1760  if (!enum_node) {
1761  ao2_ref(class, -1);
1762  continue;
1763  }
1764  ast_data_add_int(enum_node, "value", class->isolation);
1765  ast_data_add_str(enum_node, "text", isolation2text(class->isolation));
1766 
1767  count = 0;
1768  aoi2 = ao2_iterator_init(class->obj_container, 0);
1769  while ((current = ao2_iterator_next(&aoi2))) {
1770  data_odbc_connection = ast_data_add_node(data_odbc_connections, "connection");
1771  if (!data_odbc_connection) {
1772  ao2_ref(current, -1);
1773  continue;
1774  }
1775 
1776  ast_mutex_lock(&current->lock);
1777  ast_data_add_str(data_odbc_connection, "status", current->used ? "in use" :
1778  current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected");
1779  ast_data_add_bool(data_odbc_connection, "transactional", current->tx);
1780  ast_mutex_unlock(&current->lock);
1781 
1782  if (class->haspool) {
1783  ast_data_add_int(data_odbc_connection, "number", ++count);
1784  }
1785 
1786  ao2_ref(current, -1);
1787  }
1788  ao2_iterator_destroy(&aoi2);
1789  ao2_ref(class, -1);
1790 
1791  if (!ast_data_search_match(search, data_odbc_class)) {
1792  ast_data_remove_node(root, data_odbc_class);
1793  }
1794  }
1795  ao2_iterator_destroy(&aoi);
1796  return 0;
1797 }
1798 
1799 /*!
1800  * \internal
1801  * \brief /asterisk/res/odbc/listprovider.
1802  */
1803 static const struct ast_data_handler odbc_provider = {
1806 };
1807 
1808 static const struct ast_data_entry odbc_providers[] = {
1809  AST_DATA_ENTRY("/asterisk/res/odbc", &odbc_provider),
1810 };
1811 
1812 static int reload(void)
1813 {
1814  struct odbc_cache_tables *table;
1815  struct odbc_class *class;
1816  struct odbc_obj *current;
1818 
1819  /* First, mark all to be purged */
1820  while ((class = ao2_iterator_next(&aoi))) {
1821  class->delme = 1;
1822  ao2_ref(class, -1);
1823  }
1824  ao2_iterator_destroy(&aoi);
1825 
1826  load_odbc_config();
1827 
1828  /* Purge remaining classes */
1829 
1830  /* Note on how this works; this is a case of circular references, so we
1831  * explicitly do NOT want to use a callback here (or we wind up in
1832  * recursive hell).
1833  *
1834  * 1. Iterate through all the classes. Note that the classes will currently
1835  * contain two classes of the same name, one of which is marked delme and
1836  * will be purged when all remaining objects of the class are released, and
1837  * the other, which was created above when we re-parsed the config file.
1838  * 2. On each class, there is a reference held by the master container and
1839  * a reference held by each connection object. There are two cases for
1840  * destruction of the class, noted below. However, in all cases, all O-refs
1841  * (references to objects) will first be freed, which will cause the C-refs
1842  * (references to classes) to be decremented (but never to 0, because the
1843  * class container still has a reference).
1844  * a) If the class has outstanding objects, the C-ref by the class
1845  * container will then be freed, which leaves only C-refs by any
1846  * outstanding objects. When the final outstanding object is released
1847  * (O-refs held by applications and dialplan functions), it will in turn
1848  * free the final C-ref, causing class destruction.
1849  * b) If the class has no outstanding objects, when the class container
1850  * removes the final C-ref, the class will be destroyed.
1851  */
1853  while ((class = ao2_iterator_next(&aoi))) { /* C-ref++ (by iterator) */
1854  if (class->delme) {
1855  struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0);
1856  while ((current = ao2_iterator_next(&aoi2))) { /* O-ref++ (by iterator) */
1857  ao2_unlink(class->obj_container, current); /* unlink O-ref from class (reference handled implicitly) */
1858  ao2_ref(current, -1); /* O-ref-- (by iterator) */
1859  /* At this point, either
1860  * a) there's an outstanding O-ref, or
1861  * b) the object has already been destroyed.
1862  */
1863  }
1864  ao2_iterator_destroy(&aoi2);
1865  ao2_unlink(class_container, class); /* unlink C-ref from container (reference handled implicitly) */
1866  /* At this point, either
1867  * a) there's an outstanding O-ref, which holds an outstanding C-ref, or
1868  * b) the last remaining C-ref is held by the iterator, which will be
1869  * destroyed in the next step.
1870  */
1871  }
1872  ao2_ref(class, -1); /* C-ref-- (by iterator) */
1873  }
1874  ao2_iterator_destroy(&aoi);
1875 
1876  /* Empty the cache; it will get rebuilt the next time the tables are needed. */
1878  while ((table = AST_RWLIST_REMOVE_HEAD(&odbc_tables, list))) {
1879  destroy_table_cache(table);
1880  }
1882 
1883  return 0;
1884 }
1885 
1886 static int unload_module(void)
1887 {
1888  /* Prohibit unloading */
1889  return -1;
1890 }
1891 
1892 static int load_module(void)
1893 {
1895  return AST_MODULE_LOAD_DECLINE;
1896  if (load_odbc_config() == -1)
1897  return AST_MODULE_LOAD_DECLINE;
1903  ast_log(LOG_NOTICE, "res_odbc loaded.\n");
1904  return 0;
1905 }
1906 
1908  .load = load_module,
1909  .unload = unload_module,
1910  .reload = reload,
1911  .load_pri = AST_MODPRI_REALTIME_DEPEND,
1912  );
const char * type
Definition: datastore.h:32
SQLHDBC con
Definition: res_odbc.h:48
#define AST_THREADSTORAGE(name)
Define a thread storage variable.
Definition: threadstorage.h:81
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:1118
#define ast_channel_lock(chan)
Definition: channel.h:2466
Main Channel structure associated with a channel.
Definition: channel.h:742
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:191
#define AST_RWLIST_HEAD_DESTROY(head)
Destroys an rwlist head structure.
Definition: linkedlists.h:652
struct odbc_obj * ast_odbc_retrieve_transaction_obj(struct ast_channel *chan, const char *objname)
Retrieve a stored ODBC object, if a transaction has been started.
Definition: res_odbc.c:1456
static void odbc_class_destructor(void *data)
Definition: res_odbc.c:394
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:39
Asterisk locking-related definitions:
Asterisk main include file. File version handling, generic pbx functions.
#define ao2_link(arg1, arg2)
Definition: astobj2.h:785
struct ao2_container * obj_container
Definition: res_odbc.c:142
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
Definition: astobj2.c:470
#define AST_LIST_HEAD(name, type)
Defines a structure to be used to hold a list of specified type.
Definition: linkedlists.h:172
static int null_hash_fn(const void *obj, const int flags)
Definition: res_odbc.c:413
static const char config[]
Definition: cdr_csv.c:57
char name[80]
Definition: res_odbc.c:121
#define ARRAY_LEN(a)
Definition: isdn_lib.c:42
The data tree to be returned by the callbacks and managed by functions local to this file...
Definition: data.c:85
static int load_odbc_config(void)
Definition: res_odbc.c:770
unsigned int forcecommit
Definition: res_odbc.c:130
unsigned int active
Is this record the current active transaction within the channel? Note that the active flag is really...
Definition: res_odbc.c:173
#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
static struct odbc_txn_frame * release_transaction(struct odbc_txn_frame *tx)
Definition: res_odbc.c:307
struct odbc_class::@337 list
String manipulation functions.
#define ast_strdup(a)
Definition: astmm.h:109
struct odbc_obj * _ast_odbc_request_obj2(const char *name, struct ast_flags flags, const char *file, const char *function, int lineno)
Retrieves a connected ODBC object.
Definition: res_odbc.c:1241
#define ast_test_flag(p, flag)
Definition: utils.h:63
size_t ast_str_size(const struct ast_str *buf)
Returns the current maximum length (without reallocation) of the current buffer.
Definition: strings.h:482
Time-related functions and macros.
static int aoro2_obj_cb(void *vobj, void *arg, int flags)
Definition: res_odbc.c:1216
int count
Definition: res_odbc.c:134
#define AST_RWLIST_WRLOCK(head)
Write locks a list.
Definition: linkedlists.h:51
#define ao2_iterator_next(arg1)
Definition: astobj2.h:1126
descriptor for a cli entry.
Definition: cli.h:165
const int argc
Definition: cli.h:154
#define LOG_WARNING
Definition: logger.h:144
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:139
static struct ast_custom_function odbc_function
Definition: res_odbc.c:1713
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category)
Goes through variables.
Definition: config.c:597
int64_t ast_tvdiff_sec(struct timeval end, struct timeval start)
Computes the difference (in seconds) between two struct timeval instances.
Definition: time.h:56
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:497
#define ao2_callback(c, flags, cb_fn, arg)
Definition: astobj2.h:910
#define AST_RWLIST_UNLOCK(head)
Attempts to unlock a read/write based list.
Definition: linkedlists.h:150
struct ast_tm * ast_localtime(const struct timeval *timep, struct ast_tm *p_tm, const char *zone)
Timezone-independent version of localtime_r(3).
Definition: localtime.c:1570
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application&#39;s arguments.
Definition: app.h:572
static int aoro2_class_cb(void *obj, void *arg, int flags)
Definition: res_odbc.c:1202
This entries are for multiple registers.
Definition: data.h:253
Data retrieval API.
Structure for variables, used for configurations and for channel variables.
Definition: config.h:75
char * password
Definition: res_odbc.c:124
SQLSMALLINT nullable
Definition: res_odbc.h:70
unsigned int idlecheck
Definition: res_odbc.c:135
static struct ast_data_handler odbc_provider
Definition: res_odbc.c:1803
Definition: cli.h:146
Structure for a data store type.
Definition: datastore.h:31
Configuration File Parser.
static struct ast_threadstorage errors_buf
Definition: res_odbc.c:155
#define USE_TX
Definition: res_odbc.c:1212
char * connection
Definition: res_odbc.h:76
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:900
char name[0]
Definition: res_odbc.c:176
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:142
#define ast_odbc_request_obj2(a, b)
Definition: res_odbc.h:122
struct ast_data * ast_data_add_bool(struct ast_data *root, const char *childname, unsigned int boolean)
Add a boolean node type.
Definition: data.c:2344
static struct ast_datastore_info txn_info
Definition: res_odbc.c:157
#define ast_assert(a)
Definition: utils.h:738
#define ast_mutex_lock(a)
Definition: lock.h:155
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags)
Create an iterator for a container.
Definition: astobj2.c:818
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:374
static const char *const app_rollback
Definition: res_odbc.c:1720
Structure for a data store object.
Definition: datastore.h:54
struct ast_datastore * ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, const char *uid)
Find a datastore on a channel.
Definition: channel.c:2604
unsigned int tx
Definition: res_odbc.h:58
#define AST_RWLIST_HEAD_INIT(head)
Initializes an rwlist head structure.
Definition: linkedlists.h:624
struct ast_channel * owner
Definition: res_odbc.c:164
Definitions to aid in the use of thread local storage.
int value
Definition: syslog.c:39
void ast_cli(int fd, const char *fmt,...)
Definition: cli.c:105
#define AST_LIST_REMOVE(head, elm, field)
Removes a specific entry from a list.
Definition: linkedlists.h:841
struct odbc_obj * _ast_odbc_request_obj(const char *name, int check, const char *file, const char *function, int lineno)
Definition: res_odbc.c:1450
static char * handle_cli_odbc_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: res_odbc.c:935
static void destroy_table_cache(struct odbc_cache_tables *table)
Definition: res_odbc.c:428
#define LOG_DEBUG
Definition: logger.h:122
#define AST_DATA_ENTRY(__path, __handler)
Definition: data.h:260
These structures are used for adaptive capabilities.
Definition: res_odbc.h:64
struct timeval negative_connection_cache
Definition: res_odbc.c:138
#define ast_verb(level,...)
Definition: logger.h:243
void ast_config_destroy(struct ast_config *config)
Destroys a config.
Definition: config.c:1037
int ast_atomic_fetchadd_int(volatile int *p, int v)
Atomically add v to *p and return * the previous value of *p. This can be used to handle reference co...
Definition: lock.h:603
int ast_datastore_free(struct ast_datastore *datastore)
Free a data store object.
Definition: datastore.c:65
static const char *const app_commit
Definition: res_odbc.c:1719
SQLINTEGER octetlen
Definition: res_odbc.h:71
#define AST_LIST_HEAD_DESTROY(head)
Destroys a list head structure.
Definition: linkedlists.h:638
#define CONFIG_STATUS_FILEMISSING
Definition: config.h:50
unsigned int isolation
Definition: res_odbc.c:132
static void odbc_obj_destructor(void *data)
Definition: res_odbc.c:418
static char * table
Definition: cdr_odbc.c:50
static void odbc_txn_free(void *data)
Definition: res_odbc.c:343
int ast_odbc_smart_execute(struct odbc_obj *obj, SQLHSTMT stmt)
Executes a prepared statement handle.
Definition: res_odbc.c:687
static struct ast_cli_entry cli_odbc[]
Definition: res_odbc.c:1027
#define AST_RWLIST_RDLOCK(head)
Read locks a list.
Definition: linkedlists.h:77
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:236
#define AST_DATA_HANDLER_VERSION
The Data API structures version.
Definition: data.h:204
const char * value
Definition: config.h:79
General Asterisk PBX channel definitions.
#define EOR_TX
Definition: res_odbc.c:1214
static int acf_transaction_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition: res_odbc.c:1582
ODBC container.
Definition: res_odbc.h:46
int ast_str_make_space(struct ast_str **buf, size_t new_len)
Definition: strings.h:588
struct odbc_cache_columns * ast_odbc_find_column(struct odbc_cache_tables *table, const char *colname)
Find a column entry within a cached table structure.
Definition: res_odbc.c:566
const int fd
Definition: cli.h:153
static void odbc_release_obj2(struct odbc_obj *obj, struct odbc_txn_frame *tx)
Definition: res_odbc.c:1053
#define ast_config_load(filename, flags)
Load a config file.
Definition: config.h:170
unsigned int enabled
Definition: devicestate.c:205
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:63
const int n
Definition: cli.h:159
Data structure associated with a custom dialplan function.
Definition: pbx.h:95
struct ast_data * ast_data_add_node(struct ast_data *root, const char *childname)
Add a container child.
Definition: data.c:2317
#define AST_RWLIST_TRAVERSE
Definition: linkedlists.h:493
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:554
char * ast_category_browse(struct ast_config *config, const char *prev)
Goes through categories.
Definition: config.c:810
#define ao2_ref(o, delta)
Definition: astobj2.h:472
unsigned int used
Definition: res_odbc.h:56
unsigned int conntimeout
Definition: res_odbc.c:136
const char * name
Definition: config.h:77
unsigned int up
Definition: res_odbc.h:57
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:818
ODBC resource manager.
#define ast_data_register_multiple(data_entries, entries)
Definition: data.h:377
struct odbc_class * parent
Definition: res_odbc.h:49
Core PBX routines and definitions.
#define AST_RWLIST_TRAVERSE_SAFE_BEGIN
Definition: linkedlists.h:542
#define ast_data_add_structure(structure_name, root, structure)
Definition: data.h:620
The list of nodes with their search requirement.
Definition: data.c:122
const char *const * argv
Definition: cli.h:155
unsigned int delme
Definition: res_odbc.c:128
struct odbc_cache_tables * ast_odbc_find_table(const char *database, const char *tablename)
Find or create an entry describing the table specified.
Definition: res_odbc.c:449
static odbc_status odbc_obj_connect(struct odbc_obj *obj)
Definition: res_odbc.c:1524
#define DATA_EXPORT_ODBC_CLASS(MEMBER)
Definition: res_odbc.c:179
#define LOG_ERROR
Definition: logger.h:155
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:716
SQLHSTMT ast_odbc_direct_execute(struct odbc_obj *obj, SQLHSTMT(*exec_cb)(struct odbc_obj *obj, void *data), void *data)
Executes an non prepared statement and returns the resulting statement handle.
Definition: res_odbc.c:594
unsigned int limit
Definition: res_odbc.c:133
unsigned int backslash_is_escape
Definition: res_odbc.c:129
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is &quot;true&quot;. This function checks to see whether a string passed to it is an indication of an &quot;true&quot; value. It checks to see if the string is &quot;yes&quot;, &quot;true&quot;, &quot;y&quot;, &quot;t&quot;, &quot;on&quot; or &quot;1&quot;.
Definition: utils.c:1533
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:364
#define NO_TX
Definition: res_odbc.c:1213
static struct @350 args
int ast_odbc_allow_empty_string_in_nontext(struct odbc_obj *obj)
Checks if the database natively supports implicit conversion from an empty string to a number (0)...
Definition: res_odbc.c:1123
void ast_data_remove_node(struct ast_data *root, struct ast_data *child)
Remove a node that was added using ast_data_add_.
Definition: data.c:2486
struct odbc_txn_frame * txf
Definition: res_odbc.h:59
int setenv(const char *name, const char *value, int overwrite)
static int data_odbc_provider_handler(const struct ast_data_search *search, struct ast_data *root)
Definition: res_odbc.c:1726
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
void ast_log(int level, const char *file, int line, const char *function, const char *fmt,...)
Used for sending a log message This is the standard logger function. Probably the only way you will i...
Definition: logger.c:1207
unsigned int haspool
Definition: res_odbc.c:127
static odbc_status odbc_obj_disconnect(struct odbc_obj *obj)
Definition: res_odbc.c:1488
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:430
struct ast_datastore * ast_datastore_alloc(const struct ast_datastore_info *info, const char *uid)
Definition: datastore.c:98
odbc_status
Definition: res_odbc.h:36
#define LOG_NOTICE
Definition: logger.h:133
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:490
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:409
#define ast_channel_unlock(chan)
Definition: channel.h:2467
static int mark_transaction_active(struct ast_channel *chan, struct odbc_txn_frame *tx)
Definition: res_odbc.c:359
#define AST_LIST_HEAD_INIT(head)
Initializes a list head structure.
Definition: linkedlists.h:611
#define ast_free(a)
Definition: astmm.h:97
char * command
Definition: cli.h:180
SQLINTEGER size
Definition: res_odbc.h:67
static const char * isolation2text(int iso)
Definition: res_odbc.c:190
#define ast_odbc_request_obj(a, b)
Definition: res_odbc.h:123
int ast_odbc_clear_cache(const char *database, const char *tablename)
Remove a cache entry from memory This function may be called to clear entries created and cached by t...
Definition: res_odbc.c:577
const char * word
Definition: cli.h:157
#define AST_RWLIST_REMOVE_HEAD
Definition: linkedlists.h:829
static int reload(void)
Definition: res_odbc.c:1812
int ast_odbc_sanity_check(struct odbc_obj *obj)
Checks an ODBC object to ensure it is still connected.
Definition: res_odbc.c:735
static int rollback_exec(struct ast_channel *chan, const char *data)
Definition: res_odbc.c:1165
static struct odbc_txn_frame * find_transaction(struct ast_channel *chan, struct odbc_obj *obj, const char *name, int active)
Definition: res_odbc.c:224
char * sanitysql
Definition: res_odbc.c:125
int ast_strftime(char *buf, size_t len, const char *format, const struct ast_tm *tm)
Special version of strftime(3) that handles fractions of a second. Takes the same arguments as strfti...
Definition: localtime.c:2351
static int text2isolation(const char *txt)
Definition: res_odbc.c:205
static int odbc_register_class(struct odbc_class *class, int connect)
Definition: res_odbc.c:1031
void ao2_iterator_destroy(struct ao2_iterator *i)
Destroy a container iterator.
Definition: astobj2.c:833
Structure used to handle boolean flags.
Definition: utils.h:200
unsigned int forcecommit
Definition: res_odbc.c:174
ao2_callback_fn ao2_match_by_addr
a very common callback is one that matches by address.
Definition: astobj2.h:646
const char * usage
Definition: cli.h:171
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name...
Definition: pbx.c:10546
void ast_str_reset(struct ast_str *buf)
Reset the content of a dynamic string. Useful before a series of ast_str_append.
Definition: strings.h:436
#define CLI_SUCCESS
Definition: cli.h:43
static int acf_transaction_write(struct ast_channel *chan, const char *cmd, char *s, const char *value)
Definition: res_odbc.c:1620
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:471
void * data
Definition: datastore.h:56
#define AST_RWLIST_INSERT_TAIL
Definition: linkedlists.h:726
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:622
ast_mutex_t lock
Definition: res_odbc.h:47
unsigned int allow_empty_strings
Definition: res_odbc.c:131
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1053
Standard Command Line Interface.
#define ast_calloc(a, b)
Definition: astmm.h:82
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:223
#define ao2_container_alloc(arg1, arg2, arg3)
Definition: astobj2.h:734
void ast_str_update(struct ast_str *buf)
Update the length of the buffer, after using ast_str merely as a buffer.
Definition: strings.h:446
SQLSMALLINT decimals
Definition: res_odbc.h:68
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:77
static int unload_module(void)
Definition: res_odbc.c:1886
int attribute_pure ast_false(const char *val)
Make sure something is false. Determine if a string containing a boolean value is &quot;false&quot;...
Definition: utils.c:1550
int ast_cli_register_multiple(struct ast_cli_entry *e, int len)
Register multiple commands.
Definition: cli.c:2167
const int pos
Definition: cli.h:158
static int load_module(void)
Definition: res_odbc.c:1892
struct timeval last_used
Definition: res_odbc.h:50
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
#define AST_DATA_STRUCTURE(__struct, __name)
Definition: data.h:300
unsigned int isolation
Definition: res_odbc.c:175
SQLHENV env
Definition: res_odbc.c:126
static struct ast_data_entry odbc_providers[]
Definition: res_odbc.c:1808
SQLSMALLINT radix
Definition: res_odbc.h:69
const char * name
Definition: pbx.h:96
SQLRETURN ast_odbc_ast_str_SQLGetData(struct ast_str **buf, int pmaxlen, SQLHSTMT StatementHandle, SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType, SQLLEN *StrLen_or_Ind)
Wrapper for SQLGetData to use with dynamic strings.
Definition: res_odbc.c:718
#define AST_APP_ARG(name)
Define an application argument.
Definition: app.h:555
struct ast_variable * next
Definition: config.h:82
#define ast_mutex_init(pmutex)
Definition: lock.h:152
struct ast_str * ast_str_thread_get(struct ast_threadstorage *ts, size_t init_len)
Retrieve a thread locally stored dynamic string.
Definition: strings.h:669
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the &#39;standard&#39; argument separation process for an application.
Definition: app.h:604
#define CONFIG_STATUS_FILEINVALID
Definition: config.h:52
#define ast_mutex_destroy(a)
Definition: lock.h:154
struct timeval last_negative_connect
Definition: res_odbc.c:140
SQLSMALLINT type
Definition: res_odbc.h:66
void ast_odbc_release_obj(struct odbc_obj *obj)
Releases an ODBC object previously allocated by ast_odbc_request_obj()
Definition: res_odbc.c:1112
char * username
Definition: res_odbc.c:123
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:38
struct ast_data * ast_data_add_str(struct ast_data *root, const char *childname, const char *string)
Add a string node type.
Definition: data.c:2401
Asterisk module definitions.
char dsn[80]
Definition: res_odbc.c:122
struct odbc_obj * obj
Definition: res_odbc.c:165
static int commit_exec(struct ast_channel *chan, const char *data)
Definition: res_odbc.c:1128
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2590
struct ast_data * ast_data_add_int(struct ast_data *root, const char *childname, int value)
Add an integer node type.
Definition: data.c:2322
#define ao2_unlink(arg1, arg2)
Definition: astobj2.h:817
int ast_data_search_match(const struct ast_data_search *search, struct ast_data *data)
Check the current generated node to know if it matches the search condition.
Definition: data.c:1458
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1164
static struct ao2_container * class_container
Definition: res_odbc.c:145
#define AST_RWLIST_TRAVERSE_SAFE_END
Definition: linkedlists.h:602
uint32_t version
Structure version.
Definition: data.h:247
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:437
The structure of the node handler.
Definition: data.h:245
#define ASTERISK_FILE_VERSION(file, version)
Register/unregister a source code file with the core.
Definition: asterisk.h:180
#define ast_mutex_unlock(a)
Definition: lock.h:156
static int aoro2_obj_notx_cb(void *vobj, void *arg, int flags)
Definition: res_odbc.c:1232
struct odbc_cache_tables::_columns columns