Newer
Older
Kevin P. Fleming
committed
res = ast_odbc_ast_str_SQLGetData(&coldata, -1, stmt, x + 1, SQL_CHAR, &indicator);
if (indicator == SQL_NULL_DATA) {
ast_debug(3, "Got NULL data\n");
ast_str_reset(coldata);
}
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", ast_str_buffer(sql));
}
ast_debug(2, "Got coldata of '%s'\n", ast_str_buffer(coldata));
buf[buflen++] = ',';
}
/* Copy data, encoding '\' and ',' for the argument parser */
ptrcoldata = ast_str_buffer(coldata);
for (i = 0; i < ast_str_strlen(coldata); i++) {
if (escapecommas && (ptrcoldata[i] == '\\' || ptrcoldata[i] == ',')) {
buf[buflen++] = '\\';
}
buf[buflen++] = ptrcoldata[i];
if (buflen >= len - 2) {
}
if (ptrcoldata[i] == '\0') {
}
}
ast_debug(2, "buf is now set to '%s'\n", buf);
ast_debug(2, "buf is now set to '%s'\n", buf);
row = ast_calloc(1, sizeof(*row) + buflen + 1);
if (!row) {
ast_log(LOG_ERROR, "Unable to allocate space for more rows in this resultset.\n");
Tilghman Lesher
committed
status = "MEMERROR";
goto end_acf_read;
}
strcpy((char *)row + sizeof(*row), buf);
AST_LIST_INSERT_TAIL(resultset, row, list);
/* Get next row */
res = SQLFetch(stmt);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
if (res != SQL_NO_DATA) {
ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, ast_str_buffer(sql));
}
/* Number of rows in the resultset */
break;
}
if (!bogus_chan) {
snprintf(rowcount, sizeof(rowcount), "%d", y);
pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", ast_str_buffer(colnames));
if (resultset) {
struct ast_datastore *odbc_store;
if (multirow) {
int uid;
uid = ast_atomic_fetchadd_int(&resultcount, +1) + 1;
snprintf(buf, len, "%d", uid);
} else {
/* Name of the query is name of the resultset */
ast_copy_string(buf, cmd, len);
/* If there's one with the same name already, free it */
ast_channel_lock(chan);
if ((odbc_store = ast_channel_datastore_find(chan, &odbc_info, buf))) {
ast_channel_datastore_remove(chan, odbc_store);
ast_datastore_free(odbc_store);
}
ast_channel_unlock(chan);
}
odbc_store = ast_datastore_alloc(&odbc_info, buf);
if (!odbc_store) {
ast_log(LOG_ERROR, "Rows retrieved, but unable to store it in the channel. Results fail.\n");
odbc_datastore_free(resultset);
SQLCloseCursor(stmt);
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
release_obj_or_dsn (&obj, &dsn);
pbx_builtin_setvar_helper(chan, "ODBCSTATUS", "MEMERROR");
return -1;
}
odbc_store->data = resultset;
ast_channel_lock(chan);
ast_channel_datastore_add(chan, odbc_store);
ast_channel_unlock(chan);
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
release_obj_or_dsn (&obj, &dsn);
if (resultset && !multirow) {
/* Fetch the first resultset */
if (!acf_fetch(chan, "", buf, buf, len)) {
buf[0] = '\0';
}
}
if (!bogus_chan) {
static int acf_escape(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len, char character)
char *out = buf;
for (; *data && out - buf < len; data++) {
if (*data == character) {
*out = character;
static int acf_escape_ticks(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
{
return acf_escape(chan, cmd, data, buf, len, '\'');
}
static struct ast_custom_function escape_function = {
.read = acf_escape_ticks,
.write = NULL,
};
static int acf_escape_backslashes(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
{
return acf_escape(chan, cmd, data, buf, len, '\\');
}
static struct ast_custom_function escape_backslashes_function = {
.name = "SQL_ESC_BACKSLASHES",
.read = acf_escape_backslashes,
.write = NULL,
};
static int acf_fetch(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
{
struct ast_datastore *store;
struct odbc_datastore *resultset;
struct odbc_datastore_row *row;
if (!chan) {
ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
return -1;
}
ast_channel_lock(chan);
store = ast_channel_datastore_find(chan, &odbc_info, data);
if (!store) {
ast_channel_unlock(chan);
pbx_builtin_setvar_helper(chan, "ODBC_FETCH_STATUS", "FAILURE");
return -1;
}
resultset = store->data;
AST_LIST_LOCK(resultset);
row = AST_LIST_REMOVE_HEAD(resultset, list);
AST_LIST_UNLOCK(resultset);
if (!row) {
/* Cleanup datastore */
ast_channel_datastore_remove(chan, store);
Kevin P. Fleming
committed
ast_datastore_free(store);
ast_channel_unlock(chan);
pbx_builtin_setvar_helper(chan, "ODBC_FETCH_STATUS", "FAILURE");
return -1;
}
pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", resultset->names);
ast_channel_unlock(chan);
ast_copy_string(buf, row->data, len);
ast_free(row);
pbx_builtin_setvar_helper(chan, "ODBC_FETCH_STATUS", "SUCCESS");
return 0;
}
static struct ast_custom_function fetch_function = {
.name = "ODBC_FETCH",
.read = acf_fetch,
.write = NULL,
};
static char *app_odbcfinish = "ODBCFinish";
static int exec_odbcfinish(struct ast_channel *chan, const char *data)
struct ast_datastore *store;
ast_channel_lock(chan);
store = ast_channel_datastore_find(chan, &odbc_info, data);
if (store) {
ast_channel_datastore_remove(chan, store);
ast_datastore_free(store);
}
ast_channel_unlock(chan);
Joshua Colp
committed
static int free_acf_query(struct acf_odbc_query *query)
{
if (query) {
if (query->acf) {
if (query->acf->name)
ast_free((char *)query->acf->name);
ast_string_field_free_memory(query->acf);
ast_free(query->acf);
}
ast_free(query->sql_read);
ast_free(query->sql_write);
ast_free(query->sql_insert);
ast_free(query);
}
return 0;
}
static int init_acf_query(struct ast_config *cfg, char *catg, struct acf_odbc_query **query)
{
Tilghman Lesher
committed
const char *tmp;
int i;
if (!cfg || !catg) {
return EINVAL;
if (!(*query = ast_calloc(1, sizeof(**query)))) {
return ENOMEM;
if (((tmp = ast_variable_retrieve(cfg, catg, "writehandle"))) || ((tmp = ast_variable_retrieve(cfg, catg, "dsn")))) {
char *tmp2 = ast_strdupa(tmp);
AST_DECLARE_APP_ARGS(writeconf,
AST_APP_ARG(dsn)[5];
);
AST_STANDARD_APP_ARGS(writeconf, tmp2);
for (i = 0; i < 5; i++) {
if (!ast_strlen_zero(writeconf.dsn[i]))
ast_copy_string((*query)->writehandle[i], writeconf.dsn[i], sizeof((*query)->writehandle[i]));
}
}
if ((tmp = ast_variable_retrieve(cfg, catg, "readhandle"))) {
char *tmp2 = ast_strdupa(tmp);
AST_DECLARE_APP_ARGS(readconf,
AST_APP_ARG(dsn)[5];
);
AST_STANDARD_APP_ARGS(readconf, tmp2);
for (i = 0; i < 5; i++) {
if (!ast_strlen_zero(readconf.dsn[i]))
ast_copy_string((*query)->readhandle[i], readconf.dsn[i], sizeof((*query)->readhandle[i]));
}
/* If no separate readhandle, then use the writehandle for reading */
for (i = 0; i < 5; i++) {
if (!ast_strlen_zero((*query)->writehandle[i]))
ast_copy_string((*query)->readhandle[i], (*query)->writehandle[i], sizeof((*query)->readhandle[i]));
}
if ((tmp = ast_variable_retrieve(cfg, catg, "readsql")) ||
(tmp2 = ast_variable_retrieve(cfg, catg, "read"))) {
if (!tmp) {
ast_log(LOG_WARNING, "Parameter 'read' is deprecated for category %s. Please use 'readsql' instead.\n", catg);
tmp = tmp2;
}
if (*tmp != '\0') { /* non-empty string */
if (!((*query)->sql_read = ast_strdup(tmp))) {
free_acf_query(*query);
*query = NULL;
return ENOMEM;
}
}
Tilghman Lesher
committed
}
if ((*query)->sql_read && ast_strlen_zero((*query)->readhandle[0])) {
Joshua Colp
committed
free_acf_query(*query);
ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for reading: %s\n", catg);
return EINVAL;
if ((tmp = ast_variable_retrieve(cfg, catg, "writesql")) ||
(tmp2 = ast_variable_retrieve(cfg, catg, "write"))) {
if (!tmp) {
ast_log(LOG_WARNING, "Parameter 'write' is deprecated for category %s. Please use 'writesql' instead.\n", catg);
tmp = tmp2;
}
if (*tmp != '\0') { /* non-empty string */
if (!((*query)->sql_write = ast_strdup(tmp))) {
free_acf_query(*query);
*query = NULL;
return ENOMEM;
}
}
Tilghman Lesher
committed
}
if ((*query)->sql_write && ast_strlen_zero((*query)->writehandle[0])) {
Joshua Colp
committed
free_acf_query(*query);
ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for writing: %s\n", catg);
return EINVAL;
Tilghman Lesher
committed
if ((tmp = ast_variable_retrieve(cfg, catg, "insertsql"))) {
if (*tmp != '\0') { /* non-empty string */
if (!((*query)->sql_insert = ast_strdup(tmp))) {
free_acf_query(*query);
*query = NULL;
return ENOMEM;
}
}
Tilghman Lesher
committed
}
/* Allow escaping of embedded commas in fields to be turned off */
ast_set_flag((*query), OPT_ESCAPECOMMAS);
if ((tmp = ast_variable_retrieve(cfg, catg, "escapecommas"))) {
if (ast_false(tmp))
ast_clear_flag((*query), OPT_ESCAPECOMMAS);
}
if ((tmp = ast_variable_retrieve(cfg, catg, "mode"))) {
if (strcasecmp(tmp, "multirow") == 0)
ast_set_flag((*query), OPT_MULTIROW);
if ((tmp = ast_variable_retrieve(cfg, catg, "rowlimit")))
if ((tmp = ast_variable_retrieve(cfg, catg, "minargs"))) {
sscanf(tmp, "%30d", &((*query)->minargs));
}
(*query)->acf = ast_calloc(1, sizeof(struct ast_custom_function));
Joshua Colp
committed
free_acf_query(*query);
return ENOMEM;
}
if (ast_string_field_init((*query)->acf, 128)) {
Joshua Colp
committed
free_acf_query(*query);
*query = NULL;
return ENOMEM;
}
if ((tmp = ast_variable_retrieve(cfg, catg, "prefix")) && !ast_strlen_zero(tmp)) {
if (ast_asprintf((char **)&((*query)->acf->name), "%s_%s", tmp, catg) < 0) {
(*query)->acf->name = NULL;
Kevin P. Fleming
committed
}
} else {
if (ast_asprintf((char **)&((*query)->acf->name), "ODBC_%s", catg) < 0) {
(*query)->acf->name = NULL;
Kevin P. Fleming
committed
}
}
if (!(*query)->acf->name) {
Joshua Colp
committed
free_acf_query(*query);
return ENOMEM;
}
if ((tmp = ast_variable_retrieve(cfg, catg, "syntax")) && !ast_strlen_zero(tmp)) {
ast_string_field_build((*query)->acf, syntax, "%s(%s)", (*query)->acf->name, tmp);
} else {
ast_string_field_build((*query)->acf, syntax, "%s(<arg1>[...[,<argN>]])", (*query)->acf->name);
if (ast_strlen_zero((*query)->acf->syntax)) {
Joshua Colp
committed
free_acf_query(*query);
return ENOMEM;
}
if ((tmp = ast_variable_retrieve(cfg, catg, "synopsis")) && !ast_strlen_zero(tmp)) {
ast_string_field_set((*query)->acf, synopsis, tmp);
} else {
ast_string_field_set((*query)->acf, synopsis, "Runs the referenced query with the specified arguments");
}
if (ast_strlen_zero((*query)->acf->synopsis)) {
Joshua Colp
committed
free_acf_query(*query);
*query = NULL;
return ENOMEM;
}
if ((*query)->sql_read && (*query)->sql_write) {
ast_string_field_build((*query)->acf, desc,
"Runs the following query, as defined in func_odbc.conf, performing\n"
"substitution of the arguments into the query as specified by ${ARG1},\n"
"${ARG2}, ... ${ARGn}. When setting the function, the values are provided\n"
"either in whole as ${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
Tilghman Lesher
committed
"%s"
"\nRead:\n%s\n\nWrite:\n%s%s%s",
(*query)->sql_insert ?
Tilghman Lesher
committed
"If the write query affects no rows, the insert query will be\n"
(*query)->sql_read,
Tilghman Lesher
committed
(*query)->sql_write,
(*query)->sql_insert ? "\n\nInsert:\n" : "",
(*query)->sql_insert ? (*query)->sql_insert : "");
} else if ((*query)->sql_read) {
ast_string_field_build((*query)->acf, desc,
"Runs the following query, as defined in func_odbc.conf, performing\n"
"substitution of the arguments into the query as specified by ${ARG1},\n"
"${ARG2}, ... ${ARGn}. This function may only be read, not set.\n\nSQL:\n%s",
(*query)->sql_read);
} else if ((*query)->sql_write) {
ast_string_field_build((*query)->acf, desc,
"Runs the following query, as defined in func_odbc.conf, performing\n"
"substitution of the arguments into the query as specified by ${ARG1},\n"
"${ARG2}, ... ${ARGn}. The values are provided either in whole as\n"
"${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
"This function may only be set.\n%s\nSQL:\n%s%s%s",
(*query)->sql_insert ?
Tilghman Lesher
committed
"If the write query affects no rows, the insert query will be\n"
Tilghman Lesher
committed
(*query)->sql_write,
(*query)->sql_insert ? "\n\nInsert:\n" : "",
(*query)->sql_insert ? (*query)->sql_insert : "");
} else {
Joshua Colp
committed
free_acf_query(*query);
*query = NULL;
Tilghman Lesher
committed
ast_log(LOG_WARNING, "Section '%s' was found, but there was no SQL to execute. Ignoring.\n", catg);
return EINVAL;
}
if (ast_strlen_zero((*query)->acf->desc)) {
Joshua Colp
committed
free_acf_query(*query);
return ENOMEM;
if ((*query)->sql_read) {
(*query)->acf->read = acf_odbc_read;
}
if ((*query)->sql_write) {
(*query)->acf->write = acf_odbc_write;
}
return 0;
}
static char *cli_odbc_read(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(field)[100];
);
Eliel C. Sardanons
committed
struct ast_str *sql;
char *char_args, varname[15];
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
struct acf_odbc_query *query;
struct ast_channel *chan;
int i;
switch (cmd) {
case CLI_INIT:
e->command = "odbc read";
e->usage =
"Usage: odbc read <name> <args> [exec]\n"
" Evaluates the SQL provided in the ODBC function <name>, and\n"
" optionally executes the function. This function is intended for\n"
" testing purposes. Remember to quote arguments containing spaces.\n";
return NULL;
case CLI_GENERATE:
if (a->pos == 2) {
int wordlen = strlen(a->word), which = 0;
/* Complete function name */
AST_RWLIST_RDLOCK(&queries);
AST_RWLIST_TRAVERSE(&queries, query, list) {
if (!strncasecmp(query->acf->name, a->word, wordlen)) {
if (++which > a->n) {
char *res = ast_strdup(query->acf->name);
AST_RWLIST_UNLOCK(&queries);
return res;
}
}
}
AST_RWLIST_UNLOCK(&queries);
return NULL;
} else if (a->pos == 4) {
static const char * const completions[] = { "exec", NULL };
return ast_cli_complete(a->word, completions, a->n);
} else {
return NULL;
}
}
if (a->argc < 4 || a->argc > 5) {
return CLI_SHOWUSAGE;
}
Eliel C. Sardanons
committed
sql = ast_str_thread_get(&sql_buf, 16);
if (!sql) {
return CLI_FAILURE;
}
AST_RWLIST_RDLOCK(&queries);
AST_RWLIST_TRAVERSE(&queries, query, list) {
if (!strcmp(query->acf->name, a->argv[2])) {
break;
}
}
if (!query) {
ast_cli(a->fd, "No such query '%s'\n", a->argv[2]);
AST_RWLIST_UNLOCK(&queries);
return CLI_SHOWUSAGE;
}
if (!query->sql_read) {
ast_cli(a->fd, "The function %s has no readsql parameter.\n", a->argv[2]);
AST_RWLIST_UNLOCK(&queries);
return CLI_SUCCESS;
}
ast_str_make_space(&sql, strlen(query->sql_read) * 2 + 300);
/* Evaluate function */
char_args = ast_strdupa(a->argv[3]);
chan = ast_dummy_channel_alloc();
if (!chan) {
AST_RWLIST_UNLOCK(&queries);
return CLI_FAILURE;
}
AST_STANDARD_APP_ARGS(args, char_args);
for (i = 0; i < args.argc; i++) {
snprintf(varname, sizeof(varname), "ARG%d", i + 1);
pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
}
ast_str_substitute_variables(&sql, 0, chan, query->sql_read);
chan = ast_channel_unref(chan);
if (a->argc == 5 && !strcmp(a->argv[4], "exec")) {
/* Execute the query */
struct odbc_obj *obj = NULL;
struct dsn *dsn = NULL;
int dsn_num, executed = 0;
SQLHSTMT stmt;
int rows = 0, res, x;
SQLSMALLINT colcount = 0, collength;
SQLLEN indicator, octetlength;
struct ast_str *coldata = ast_str_thread_get(&coldata_buf, 16);
char colname[256];
if (!coldata) {
AST_RWLIST_UNLOCK(&queries);
return CLI_SUCCESS;
}
for (dsn_num = 0; dsn_num < 5; dsn_num++) {
if (ast_strlen_zero(query->readhandle[dsn_num])) {
continue;
}
obj = get_odbc_obj(query->readhandle[dsn_num], &dsn);
if (!obj) {
continue;
}
ast_debug(1, "Found handle %s\n", query->readhandle[dsn_num]);
if (!(stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(sql)))) {
release_obj_or_dsn (&obj, &dsn);
continue;
}
executed = 1;
res = SQLNumResultCols(stmt, &colcount);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_cli(a->fd, "SQL Column Count error!\n[%s]\n\n", ast_str_buffer(sql));
SQLCloseCursor(stmt);
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
release_obj_or_dsn (&obj, &dsn);
AST_RWLIST_UNLOCK(&queries);
return CLI_SUCCESS;
}
if (colcount <= 0) {
SQLCloseCursor(stmt);
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
release_obj_or_dsn (&obj, &dsn);
ast_cli(a->fd, "Returned %d columns. Query executed on handle %d:%s [%s]\n", colcount, dsn_num, query->readhandle[dsn_num], ast_str_buffer(sql));
AST_RWLIST_UNLOCK(&queries);
return CLI_SUCCESS;
}
res = SQLFetch(stmt);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
SQLCloseCursor(stmt);
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
release_obj_or_dsn (&obj, &dsn);
if (res == SQL_NO_DATA) {
ast_cli(a->fd, "Returned %d rows. Query executed on handle %d:%s [%s]\n", rows, dsn_num, query->readhandle[dsn_num], ast_str_buffer(sql));
break;
} else {
ast_cli(a->fd, "Error %d in FETCH [%s]\n", res, ast_str_buffer(sql));
}
AST_RWLIST_UNLOCK(&queries);
return CLI_SUCCESS;
}
for (;;) {
for (x = 0; x < colcount; x++) {
res = SQLDescribeCol(stmt, x + 1, (unsigned char *)colname, sizeof(colname), &collength, NULL, NULL, NULL, NULL);
if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || collength == 0) {
snprintf(colname, sizeof(colname), "field%d", x);
}
octetlength = 0;
SQLColAttribute(stmt, x + 1, SQL_DESC_OCTET_LENGTH, NULL, 0, NULL, &octetlength);
res = ast_odbc_ast_str_SQLGetData(&coldata, octetlength + 1, stmt, x + 1, SQL_CHAR, &indicator);
if (indicator == SQL_NULL_DATA) {
ast_str_set(&coldata, 0, "(nil)");
res = SQL_SUCCESS;
}
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_cli(a->fd, "SQL Get Data error %d!\n[%s]\n\n", res, ast_str_buffer(sql));
SQLCloseCursor(stmt);
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
release_obj_or_dsn (&obj, &dsn);
AST_RWLIST_UNLOCK(&queries);
return CLI_SUCCESS;
ast_cli(a->fd, "%-20.20s %s\n", colname, ast_str_buffer(coldata));
rows++;
/* Get next row */
res = SQLFetch(stmt);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
break;
ast_cli(a->fd, "%-20.20s %s\n", "----------", "----------");
SQLCloseCursor(stmt);
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
release_obj_or_dsn (&obj, &dsn);
ast_cli(a->fd, "Returned %d row%s. Query executed on handle %d [%s]\n", rows, rows == 1 ? "" : "s", dsn_num, query->readhandle[dsn_num]);
break;
release_obj_or_dsn (&obj, &dsn);
ast_cli(a->fd, "Failed to execute query. [%s]\n", ast_str_buffer(sql));
} else { /* No execution, just print out the resulting SQL */
ast_cli(a->fd, "%s\n", ast_str_buffer(sql));
}
AST_RWLIST_UNLOCK(&queries);
return CLI_SUCCESS;
}
static char *cli_odbc_write(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
AST_DECLARE_APP_ARGS(values,
AST_APP_ARG(field)[100];
);
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(field)[100];
);
Eliel C. Sardanons
committed
struct ast_str *sql;
char *char_args, *char_values, varname[15];
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
struct acf_odbc_query *query;
struct ast_channel *chan;
int i;
switch (cmd) {
case CLI_INIT:
e->command = "odbc write";
e->usage =
"Usage: odbc write <name> <args> <value> [exec]\n"
" Evaluates the SQL provided in the ODBC function <name>, and\n"
" optionally executes the function. This function is intended for\n"
" testing purposes. Remember to quote arguments containing spaces.\n";
return NULL;
case CLI_GENERATE:
if (a->pos == 2) {
int wordlen = strlen(a->word), which = 0;
/* Complete function name */
AST_RWLIST_RDLOCK(&queries);
AST_RWLIST_TRAVERSE(&queries, query, list) {
if (!strncasecmp(query->acf->name, a->word, wordlen)) {
if (++which > a->n) {
char *res = ast_strdup(query->acf->name);
AST_RWLIST_UNLOCK(&queries);
return res;
}
}
}
AST_RWLIST_UNLOCK(&queries);
return NULL;
} else if (a->pos == 5) {
static const char * const completions[] = { "exec", NULL };
return ast_cli_complete(a->word, completions, a->n);
} else {
return NULL;
}
}
if (a->argc < 5 || a->argc > 6) {
return CLI_SHOWUSAGE;
}
Eliel C. Sardanons
committed
sql = ast_str_thread_get(&sql_buf, 16);
if (!sql) {
return CLI_FAILURE;
}
AST_RWLIST_RDLOCK(&queries);
AST_RWLIST_TRAVERSE(&queries, query, list) {
if (!strcmp(query->acf->name, a->argv[2])) {
break;
}
}
if (!query) {
ast_cli(a->fd, "No such query '%s'\n", a->argv[2]);
AST_RWLIST_UNLOCK(&queries);
return CLI_SHOWUSAGE;
}
if (!query->sql_write) {
ast_cli(a->fd, "The function %s has no writesql parameter.\n", a->argv[2]);
AST_RWLIST_UNLOCK(&queries);
return CLI_SUCCESS;
}
/* FIXME: The code below duplicates code found in acf_odbc_write but
* lacks the newer sql_insert additions. */
ast_str_make_space(&sql, strlen(query->sql_write) * 2 + 300);
/* Evaluate function */
char_args = ast_strdupa(a->argv[3]);
char_values = ast_strdupa(a->argv[4]);
chan = ast_dummy_channel_alloc();
if (!chan) {
AST_RWLIST_UNLOCK(&queries);
return CLI_FAILURE;
}
AST_STANDARD_APP_ARGS(args, char_args);
for (i = 0; i < args.argc; i++) {
snprintf(varname, sizeof(varname), "ARG%d", i + 1);
pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
}
/* Parse values, just like arguments */
AST_STANDARD_APP_ARGS(values, char_values);
for (i = 0; i < values.argc; i++) {
snprintf(varname, sizeof(varname), "VAL%d", i + 1);
pbx_builtin_pushvar_helper(chan, varname, values.field[i]);
}
/* Additionally set the value as a whole (but push an empty string if value is NULL) */
pbx_builtin_pushvar_helper(chan, "VALUE", S_OR(a->argv[4], ""));
ast_str_substitute_variables(&sql, 0, chan, query->sql_write);
ast_debug(1, "SQL is %s\n", ast_str_buffer(sql));
chan = ast_channel_unref(chan);
if (a->argc == 6 && !strcmp(a->argv[5], "exec")) {
/* Execute the query */
struct odbc_obj *obj = NULL;
struct dsn *dsn = NULL;
SQLHSTMT stmt;
SQLLEN rows = -1;
for (dsn_num = 0; dsn_num < 5; dsn_num++) {
if (ast_strlen_zero(query->writehandle[dsn_num])) {
continue;
obj = get_odbc_obj(query->writehandle[dsn_num], &dsn);
if (!obj) {
continue;
}
if (!(stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(sql)))) {
release_obj_or_dsn (&obj, &dsn);
continue;
}
SQLRowCount(stmt, &rows);
SQLCloseCursor(stmt);
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
release_obj_or_dsn (&obj, &dsn);
ast_cli(a->fd, "Affected %d rows. Query executed on handle %d [%s]\n", (int)rows, dsn_num, query->writehandle[dsn_num]);
executed = 1;
break;
}
if (!executed) {
ast_cli(a->fd, "Failed to execute query.\n");
}
} else { /* No execution, just print out the resulting SQL */
ast_cli(a->fd, "%s\n", ast_str_buffer(sql));
}
AST_RWLIST_UNLOCK(&queries);
return CLI_SUCCESS;
}
static struct ast_cli_entry cli_func_odbc[] = {
AST_CLI_DEFINE(cli_odbc_write, "Test setting a func_odbc function"),
AST_CLI_DEFINE(cli_odbc_read, "Test reading a func_odbc function"),
};
static int load_module(void)
{
int res = 0;
struct ast_config *cfg;
char *catg;
const char *s;
struct ast_flags config_flags = { 0 };
res |= ast_custom_function_register(&fetch_function);
res |= ast_register_application_xml(app_odbcfinish, exec_odbcfinish);
cfg = ast_config_load(config, config_flags);
Tilghman Lesher
committed
if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
Mark Spencer
committed
ast_log(LOG_NOTICE, "Unable to load config for func_odbc: %s\n", config);
return AST_MODULE_LOAD_DECLINE;
ast_rwlock_wrlock(&single_db_connection_lock);
if ((s = ast_variable_retrieve(cfg, "general", "single_db_connection"))) {
single_db_connection = ast_true(s);
} else {
single_db_connection = DEFAULT_SINGLE_DB_CONNECTION;
}
dsns = NULL;
if (single_db_connection) {
dsns = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, DSN_BUCKETS,
dsn_hash, NULL, dsn_cmp);
if (!dsns) {
ast_log(LOG_ERROR, "Could not initialize DSN container\n");
ast_rwlock_unlock(&single_db_connection_lock);
return AST_MODULE_LOAD_DECLINE;
}
}
ast_rwlock_unlock(&single_db_connection_lock);
AST_RWLIST_WRLOCK(&queries);
for (catg = ast_category_browse(cfg, NULL);
catg;
catg = ast_category_browse(cfg, catg)) {
struct acf_odbc_query *query = NULL;
int err;
if (!strcasecmp(catg, "general")) {
continue;
}
if ((err = init_acf_query(cfg, catg, &query))) {
if (err == ENOMEM)
ast_log(LOG_ERROR, "Out of memory\n");
else if (err == EINVAL)
ast_log(LOG_ERROR, "Invalid parameters for category %s\n", catg);
else
ast_log(LOG_ERROR, "%s (%d)\n", strerror(err), err);
Tilghman Lesher
committed
AST_RWLIST_INSERT_HEAD(&queries, query, list);
ast_custom_function_register(query->acf);
}
}
ast_config_destroy(cfg);
res |= ast_custom_function_register(&escape_function);
res |= ast_custom_function_register(&escape_backslashes_function);
ast_cli_register_multiple(cli_func_odbc, ARRAY_LEN(cli_func_odbc));
Tilghman Lesher
committed
AST_RWLIST_UNLOCK(&queries);
return res;
}
static int unload_module(void)
struct acf_odbc_query *query;
int res = 0;
Tilghman Lesher
committed
AST_RWLIST_WRLOCK(&queries);
while (!AST_RWLIST_EMPTY(&queries)) {
query = AST_RWLIST_REMOVE_HEAD(&queries, list);
ast_custom_function_unregister(query->acf);
free_acf_query(query);
res |= ast_custom_function_unregister(&escape_function);
res |= ast_custom_function_unregister(&escape_backslashes_function);
res |= ast_custom_function_unregister(&fetch_function);
res |= ast_unregister_application(app_odbcfinish);
ast_cli_unregister_multiple(cli_func_odbc, ARRAY_LEN(cli_func_odbc));
/* Allow any threads waiting for this lock to pass (avoids a race) */
Tilghman Lesher
committed
AST_RWLIST_UNLOCK(&queries);
usleep(1);
Tilghman Lesher
committed
AST_RWLIST_WRLOCK(&queries);
Tilghman Lesher
committed
AST_RWLIST_UNLOCK(&queries);
if (dsns) {
ao2_ref(dsns, -1);
}
return res;
static int reload(void)
{
int res = 0;
struct ast_config *cfg;
struct acf_odbc_query *oldquery;
const char *s;
struct ast_flags config_flags = { CONFIG_FLAG_FILEUNCHANGED };
cfg = ast_config_load(config, config_flags);
Tilghman Lesher
committed
if (cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID)
ast_rwlock_wrlock(&single_db_connection_lock);
if (dsns) {
ao2_ref(dsns, -1);
dsns = NULL;
}
if (cfg && (s = ast_variable_retrieve(cfg, "general", "single_db_connection"))) {
single_db_connection = ast_true(s);
} else {
single_db_connection = DEFAULT_SINGLE_DB_CONNECTION;
}
if (single_db_connection) {
dsns = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, DSN_BUCKETS,
dsn_hash, NULL, dsn_cmp);
if (!dsns) {
ast_log(LOG_ERROR, "Could not initialize DSN container\n");
ast_rwlock_unlock(&single_db_connection_lock);
return 0;
}
}
ast_rwlock_unlock(&single_db_connection_lock);
Tilghman Lesher
committed
AST_RWLIST_WRLOCK(&queries);
Tilghman Lesher
committed
while (!AST_RWLIST_EMPTY(&queries)) {
oldquery = AST_RWLIST_REMOVE_HEAD(&queries, list);
ast_custom_function_unregister(oldquery->acf);
free_acf_query(oldquery);
}
if (!cfg) {
ast_log(LOG_WARNING, "Unable to load config for func_odbc: %s\n", config);
goto reload_out;
}
for (catg = ast_category_browse(cfg, NULL);
catg;
catg = ast_category_browse(cfg, catg)) {
struct acf_odbc_query *query = NULL;
if (!strcasecmp(catg, "general")) {
continue;
}
if (init_acf_query(cfg, catg, &query)) {
ast_log(LOG_ERROR, "Cannot initialize query %s\n", catg);
Tilghman Lesher
committed
AST_RWLIST_INSERT_HEAD(&queries, query, list);
ast_custom_function_register(query->acf);
}
}
ast_config_destroy(cfg);
reload_out: