/* * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application * Copyright (C) 2005-2014, Anthony Minessale II * * Version: MPL 1.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application * * The Initial Developer of the Original Code is * Anthony Minessale II * Portions created by the Initial Developer are Copyright (C) * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Anthony Minessale II * Michael Jerris * Paul D. Tinsley * Emmanuel Schmidbauer * Andrey Volk * * * switch_core_sqldb.c -- Main Core Library (statistics tracker) * */ #include #include "private/switch_core_pvt.h" #define SWITCH_SQL_QUEUE_LEN 100000 #define SWITCH_SQL_QUEUE_PAUSE_LEN 90000 struct switch_cache_db_handle { char name[CACHE_DB_LEN]; switch_cache_db_handle_type_t type; switch_cache_db_native_handle_t native_handle; time_t last_used; switch_mutex_t *mutex; switch_memory_pool_t *pool; int32_t flags; unsigned long hash; unsigned long thread_hash; char creator[CACHE_DB_LEN]; char last_user[CACHE_DB_LEN]; uint32_t use_count; uint64_t total_used_count; struct switch_cache_db_handle *next; }; static struct { switch_memory_pool_t *memory_pool; switch_thread_t *db_thread; int db_thread_running; switch_bool_t manage; switch_mutex_t *dbh_mutex; switch_mutex_t *ctl_mutex; switch_cache_db_handle_t *handle_pool; uint32_t total_handles; uint32_t total_used_handles; switch_cache_db_handle_t *dbh; switch_sql_queue_manager_t *qm; int paused; } sql_manager; static void switch_core_sqldb_start_thread(void); static void switch_core_sqldb_stop_thread(void); #define database_interface_handle_callback_exec(database_interface, dih, sql, callback, pdata, err) database_interface->callback_exec_detailed(__FILE__, (char *)__SWITCH_FUNC__, __LINE__, dih, sql, callback, pdata, err) #define database_interface_handle_exec(database_interface, dih, sql, err) database_interface->exec_detailed(__FILE__, (char *)__SWITCH_FUNC__, __LINE__, dih, sql, err) static switch_cache_db_handle_t *create_handle(switch_cache_db_handle_type_t type) { switch_cache_db_handle_t *new_dbh = NULL; switch_memory_pool_t *pool = NULL; switch_core_new_memory_pool(&pool); new_dbh = switch_core_alloc(pool, sizeof(*new_dbh)); new_dbh->pool = pool; new_dbh->type = type; switch_mutex_init(&new_dbh->mutex, SWITCH_MUTEX_NESTED, new_dbh->pool); return new_dbh; } static void destroy_handle(switch_cache_db_handle_t **dbh) { if (dbh && *dbh && (*dbh)->pool) { switch_core_destroy_memory_pool(&(*dbh)->pool); *dbh = NULL; } } static void add_handle(switch_cache_db_handle_t *dbh, const char *db_str, const char *db_callsite_str, const char *thread_str) { switch_ssize_t hlen = -1; switch_mutex_lock(sql_manager.dbh_mutex); switch_mutex_lock(dbh->mutex); switch_set_string(dbh->creator, db_callsite_str); switch_set_string(dbh->name, db_str); dbh->hash = switch_ci_hashfunc_default(db_str, &hlen); dbh->thread_hash = switch_ci_hashfunc_default(thread_str, &hlen); dbh->use_count++; dbh->total_used_count++; dbh->next = sql_manager.handle_pool; sql_manager.handle_pool = dbh; sql_manager.total_handles++; sql_manager.total_used_handles++; switch_mutex_unlock(sql_manager.dbh_mutex); } static void del_handle(switch_cache_db_handle_t *dbh) { switch_cache_db_handle_t *dbh_ptr, *last = NULL; switch_mutex_lock(sql_manager.dbh_mutex); for (dbh_ptr = sql_manager.handle_pool; dbh_ptr; dbh_ptr = dbh_ptr->next) { if (dbh_ptr == dbh) { if (last) { last->next = dbh_ptr->next; } else { sql_manager.handle_pool = dbh_ptr->next; } sql_manager.total_handles--; break; } last = dbh_ptr; } switch_mutex_unlock(sql_manager.dbh_mutex); } SWITCH_DECLARE(void) switch_cache_db_database_interface_flush_handles(switch_database_interface_t *database_interface) { switch_cache_db_handle_t *dbh_ptr = NULL; switch_mutex_lock(sql_manager.dbh_mutex); top: for (dbh_ptr = sql_manager.handle_pool; dbh_ptr; dbh_ptr = dbh_ptr->next) { if (switch_mutex_trylock(dbh_ptr->mutex) == SWITCH_STATUS_SUCCESS) { if (dbh_ptr->type != SCDB_TYPE_DATABASE_INTERFACE) { switch_mutex_unlock(dbh_ptr->mutex); continue; } if (dbh_ptr->native_handle.database_interface_dbh->connection_options.database_interface != database_interface) { switch_mutex_unlock(dbh_ptr->mutex); continue; } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG10, "Dropping DB connection %s\n", dbh_ptr->name); database_interface->handle_destroy(&dbh_ptr->native_handle.database_interface_dbh); del_handle(dbh_ptr); switch_mutex_unlock(dbh_ptr->mutex); destroy_handle(&dbh_ptr); goto top; } } switch_mutex_unlock(sql_manager.dbh_mutex); } static switch_cache_db_handle_t *get_handle(const char *db_str, const char *user_str, const char *thread_str) { switch_ssize_t hlen = -1; unsigned long hash = 0, thread_hash = 0; switch_cache_db_handle_t *dbh_ptr, *r = NULL; hash = switch_ci_hashfunc_default(db_str, &hlen); thread_hash = switch_ci_hashfunc_default(thread_str, &hlen); switch_mutex_lock(sql_manager.dbh_mutex); /* First loop allows a thread to use a handle multiple times sumiltaneously but only if that handle is in use by the same thread. In that case use_count will be incremented. This allows SQLite to read and write within a single thread, giving the same handle for both operations. */ for (dbh_ptr = sql_manager.handle_pool; dbh_ptr; dbh_ptr = dbh_ptr->next) { if (dbh_ptr->thread_hash == thread_hash && dbh_ptr->hash == hash && !switch_test_flag(dbh_ptr, CDF_PRUNE) && switch_mutex_trylock(dbh_ptr->mutex) == SWITCH_STATUS_SUCCESS) { r = dbh_ptr; break; } } if (!r) { /* If a handle idles, take it and associate with the thread. If a handle is in use, skip and create new one. */ for (dbh_ptr = sql_manager.handle_pool; dbh_ptr; dbh_ptr = dbh_ptr->next) { if (dbh_ptr->hash == hash && !dbh_ptr->use_count && !switch_test_flag(dbh_ptr, CDF_PRUNE) && switch_mutex_trylock(dbh_ptr->mutex) == SWITCH_STATUS_SUCCESS) { r = dbh_ptr; r->thread_hash = thread_hash; break; } } } if (r) { r->use_count++; r->total_used_count++; sql_manager.total_used_handles++; switch_set_string(r->last_user, user_str); } switch_mutex_unlock(sql_manager.dbh_mutex); return r; } /*! \brief Open the default system database */ SWITCH_DECLARE(switch_status_t) _switch_core_db_handle(switch_cache_db_handle_t **dbh, const char *file, const char *func, int line) { switch_status_t r; char *dsn; if (!sql_manager.manage) { return SWITCH_STATUS_FALSE; } if (!zstr(runtime.odbc_dsn)) { dsn = runtime.odbc_dsn; } else if (!zstr(runtime.dbname)) { dsn = runtime.dbname; } else { dsn = "core"; } if ((r = _switch_cache_db_get_db_handle_dsn_ex(dbh, dsn, SWITCH_TRUE, file, func, line)) != SWITCH_STATUS_SUCCESS) { *dbh = NULL; } return r; } #define SQL_CACHE_TIMEOUT 30 #define SQL_REG_TIMEOUT 15 static void sql_close(time_t prune) { switch_cache_db_handle_t *dbh = NULL; int locked = 0; int sanity = 10000; switch_mutex_lock(sql_manager.dbh_mutex); top: locked = 0; for (dbh = sql_manager.handle_pool; dbh; dbh = dbh->next) { time_t diff = 0; if (prune > 0 && prune > dbh->last_used) { diff = (time_t) prune - dbh->last_used; } if (prune > 0 && (dbh->use_count || switch_test_flag(dbh, CDF_NONEXPIRING) || (diff < SQL_CACHE_TIMEOUT && !switch_test_flag(dbh, CDF_PRUNE)))) { continue; } if (switch_mutex_trylock(dbh->mutex) == SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG10, "Dropping idle DB connection %s\n", dbh->name); switch (dbh->type) { case SCDB_TYPE_DATABASE_INTERFACE: { switch_database_interface_t *database_interface = dbh->native_handle.database_interface_dbh->connection_options.database_interface; database_interface->handle_destroy(&dbh->native_handle.database_interface_dbh); } break; case SCDB_TYPE_ODBC: { switch_odbc_handle_destroy(&dbh->native_handle.odbc_dbh); } break; case SCDB_TYPE_CORE_DB: { switch_core_db_close(dbh->native_handle.core_db_dbh->handle); dbh->native_handle.core_db_dbh->handle = NULL; } break; } del_handle(dbh); switch_mutex_unlock(dbh->mutex); destroy_handle(&dbh); goto top; } else { if (!prune) { if (!sanity) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "SANITY CHECK FAILED! Handle %s (%s;%s) was not properly released.\n", dbh->name, dbh->creator, dbh->last_user); } else { locked++; } } continue; } } if (locked) { if (!prune) { switch_cond_next(); if (sanity) sanity--; } goto top; } switch_mutex_unlock(sql_manager.dbh_mutex); } SWITCH_DECLARE(switch_cache_db_handle_type_t) switch_cache_db_get_type(switch_cache_db_handle_t *dbh) { return dbh->type; } SWITCH_DECLARE(void) switch_cache_db_flush_handles(void) { sql_close(switch_epoch_time_now(NULL) + SQL_CACHE_TIMEOUT + 1); } SWITCH_DECLARE(void) switch_cache_db_release_db_handle(switch_cache_db_handle_t **dbh) { if (dbh && *dbh) { switch((*dbh)->type) { case SCDB_TYPE_DATABASE_INTERFACE: { switch_database_interface_t *database_interface = (*dbh)->native_handle.database_interface_dbh->connection_options.database_interface; database_interface->flush((*dbh)->native_handle.database_interface_dbh); } break; default: break; } (*dbh)->last_used = switch_epoch_time_now(NULL); if ((*dbh)->use_count) { --(*dbh)->use_count; } switch_mutex_unlock((*dbh)->mutex); *dbh = NULL; switch_mutex_lock(sql_manager.dbh_mutex); sql_manager.total_used_handles--; switch_mutex_unlock(sql_manager.dbh_mutex); } } SWITCH_DECLARE(void) switch_cache_db_dismiss_db_handle(switch_cache_db_handle_t **dbh) { switch_cache_db_release_db_handle(dbh); } #ifndef MIN #define MIN(a,b) (((a) < (b)) ? (a) : (b)) #endif SWITCH_DECLARE(switch_status_t) switch_database_available(char* dsn) { switch_status_t status = SWITCH_STATUS_FALSE; switch_database_interface_t *database_interface; if (!dsn) { status = SWITCH_STATUS_SUCCESS; } else { char *colon_slashes = NULL; if (NULL != (colon_slashes = strstr(dsn, "://"))) { char prefix[16] = ""; strncpy(prefix, dsn, MIN(colon_slashes - dsn, 15)); if (!strncasecmp(prefix, "odbc", 4)) { if (switch_odbc_available()) status = SWITCH_STATUS_SUCCESS; } else if (!strncasecmp(prefix, "sqlite", 6)) { status = SWITCH_STATUS_SUCCESS; } else if ((database_interface = switch_loadable_module_get_database_interface(prefix, NULL))) { status = SWITCH_STATUS_SUCCESS; UNPROTECT_INTERFACE(database_interface); } } else if (strchr(dsn + 2, ':')) { status = SWITCH_STATUS_SUCCESS; } } return status; } SWITCH_DECLARE(switch_status_t) switch_core_check_core_db_dsn(void) { return switch_database_available(runtime.odbc_dsn); } SWITCH_DECLARE(switch_status_t) _switch_cache_db_get_db_handle_dsn(switch_cache_db_handle_t **dbh, const char *dsn, const char *file, const char *func, int line) { return _switch_cache_db_get_db_handle_dsn_ex(dbh, dsn, SWITCH_FALSE, file, func, line); } SWITCH_DECLARE(switch_status_t) _switch_cache_db_get_db_handle_dsn_ex(switch_cache_db_handle_t **dbh, const char *dsn, switch_bool_t make_module_no_unloadable, const char *file, const char *func, int line) { switch_cache_db_connection_options_t connection_options = { {0} }; switch_cache_db_handle_type_t type = SCDB_TYPE_CORE_DB; switch_database_interface_t *database_interface = NULL; char tmp[256] = ""; char *p; switch_status_t status = SWITCH_STATUS_FALSE; int i; char *colon_slashes = NULL; if ( NULL != (colon_slashes = strstr(dsn, "://")) ) { char prefix[16] = ""; strncpy(prefix, dsn, MIN(colon_slashes - dsn, 15)); if ((database_interface = switch_loadable_module_get_database_interface(prefix, NULL))) { type = SCDB_TYPE_DATABASE_INTERFACE; connection_options.database_interface_options.make_module_no_unloadable = make_module_no_unloadable; connection_options.database_interface_options.database_interface = database_interface; connection_options.database_interface_options.original_dsn = dsn; connection_options.database_interface_options.connection_string = colon_slashes + 3; strcpy(connection_options.database_interface_options.prefix, prefix); UNPROTECT_INTERFACE(database_interface); } } if (!connection_options.database_interface_options.connection_string) { if (!strncasecmp(dsn, "sqlite://", 9)) { type = SCDB_TYPE_CORE_DB; connection_options.core_db_options.db_path = (char *)(dsn + 9); if (!strncasecmp(connection_options.core_db_options.db_path, "memory://", 9)) { connection_options.core_db_options.in_memory = SWITCH_TRUE; connection_options.core_db_options.db_path = (char *)(connection_options.core_db_options.db_path + 9); } } else if ((!(i = strncasecmp(dsn, "odbc://", 7))) || (strchr(dsn + 2, ':') && !colon_slashes)) { type = SCDB_TYPE_ODBC; if (i) { switch_set_string(tmp, dsn); } else { switch_set_string(tmp, dsn + 7); } connection_options.odbc_options.dsn = tmp; if ((p = strchr(tmp, ':'))) { *p++ = '\0'; connection_options.odbc_options.user = p; if ((p = strchr(connection_options.odbc_options.user, ':'))) { *p++ = '\0'; connection_options.odbc_options.pass = p; } } } else { type = SCDB_TYPE_CORE_DB; connection_options.core_db_options.db_path = (char *)dsn; } } status = _switch_cache_db_get_db_handle(dbh, type, &connection_options, file, func, line); if (status != SWITCH_STATUS_SUCCESS) *dbh = NULL; return status; } SWITCH_DECLARE(switch_status_t) _switch_cache_db_get_db_handle(switch_cache_db_handle_t **dbh, switch_cache_db_handle_type_t type, switch_cache_db_connection_options_t *connection_options, const char *file, const char *func, int line) { switch_thread_id_t self = switch_thread_self(); char thread_str[CACHE_DB_LEN] = ""; char db_str[CACHE_DB_LEN] = ""; char db_callsite_str[CACHE_DB_LEN] = ""; switch_cache_db_handle_t *new_dbh = NULL; int waiting = 0; uint32_t yield_len = 100000, total_yield = 0; const char *db_name = NULL; const char *odbc_user = NULL; const char *odbc_pass = NULL; const char *db_type = NULL; while(runtime.max_db_handles && sql_manager.total_handles >= runtime.max_db_handles && sql_manager.total_used_handles >= sql_manager.total_handles) { if (!waiting++) { switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, NULL, SWITCH_LOG_WARNING, "Max handles %u exceeded, blocking....\n", runtime.max_db_handles); } switch_yield(yield_len); total_yield += yield_len; if (runtime.db_handle_timeout && total_yield > runtime.db_handle_timeout) { switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, NULL, SWITCH_LOG_ERROR, "Error connecting\n"); *dbh = NULL; return SWITCH_STATUS_FALSE; } } switch (type) { case SCDB_TYPE_DATABASE_INTERFACE: { db_name = connection_options->database_interface_options.connection_string; odbc_user = NULL; odbc_pass = NULL; db_type = "database_interface"; } break; case SCDB_TYPE_ODBC: { db_name = connection_options->odbc_options.dsn; odbc_user = connection_options->odbc_options.user; odbc_pass = connection_options->odbc_options.pass; db_type = "odbc"; } break; case SCDB_TYPE_CORE_DB: { db_name = connection_options->core_db_options.db_path; odbc_user = NULL; odbc_pass = NULL; db_type = "core_db"; } break; } if (!db_name) { return SWITCH_STATUS_FALSE; } if (odbc_user || odbc_pass) { snprintf(db_str, sizeof(db_str) - 1, "db=\"%s\";type=\"%s\"user=\"%s\";pass=\"%s\"", db_name, db_type, odbc_user, odbc_pass); } else { snprintf(db_str, sizeof(db_str) - 1, "db=\"%s\",type=\"%s\"", db_name, db_type); } snprintf(db_callsite_str, sizeof(db_callsite_str) - 1, "%s:%d", file, line); snprintf(thread_str, sizeof(thread_str) - 1, "thread=\"%lu\"", (unsigned long) (intptr_t) self); if ((new_dbh = get_handle(db_str, db_callsite_str, thread_str))) { if (type == SCDB_TYPE_DATABASE_INTERFACE) { switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, NULL, SWITCH_LOG_DEBUG10, "Reuse Unused Cached DB handle %s [Database interface prefix: %s]\n", new_dbh->name, connection_options->database_interface_options.prefix); } else { switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, NULL, SWITCH_LOG_DEBUG10, "Reuse Unused Cached DB handle %s [%s]\n", new_dbh->name, switch_cache_db_type_name(new_dbh->type)); } } else { switch_core_db_t *db = NULL; switch_odbc_handle_t *odbc_dbh = NULL; switch_database_interface_handle_t *database_interface_dbh = NULL; switch (type) { case SCDB_TYPE_DATABASE_INTERFACE: { switch_database_interface_t *database_interface = connection_options->database_interface_options.database_interface; if (SWITCH_STATUS_SUCCESS != database_interface->handle_new(connection_options->database_interface_options, &database_interface_dbh)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failure! Can't create new handle! Can't connect to DSN %s\n", connection_options->database_interface_options.original_dsn); goto end; } if (database_interface_dbh) { database_interface_dbh->connection_options = connection_options->database_interface_options; if (connection_options->database_interface_options.make_module_no_unloadable == SWITCH_TRUE) { PROTECT_INTERFACE(database_interface) switch_loadable_module_protect(database_interface->parent->module_name); UNPROTECT_INTERFACE(database_interface) } } } break; case SCDB_TYPE_ODBC: { if (!switch_odbc_available()) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failure! ODBC NOT AVAILABLE! Can't connect to DSN %s\n", connection_options->odbc_options.dsn); goto end; } if ((odbc_dbh = switch_odbc_handle_new(connection_options->odbc_options.dsn, connection_options->odbc_options.user, connection_options->odbc_options.pass))) { if (switch_odbc_handle_connect(odbc_dbh) != SWITCH_ODBC_SUCCESS) { switch_odbc_handle_destroy(&odbc_dbh); } } } break; case SCDB_TYPE_CORE_DB: { if (!connection_options->core_db_options.in_memory) { db = switch_core_db_open_file(connection_options->core_db_options.db_path); } else { db = switch_core_db_open_in_memory(connection_options->core_db_options.db_path); } } break; default: goto end; } if (!db && !odbc_dbh && !database_interface_dbh) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failure to connect to %s %s!\n", switch_cache_db_type_name(type), db_name); goto end; } new_dbh = create_handle(type); switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, NULL, SWITCH_LOG_DEBUG10, "Create Cached DB handle %s [%s] %s:%d\n", new_dbh->name, switch_cache_db_type_name(type), file, line); if (database_interface_dbh) { new_dbh->native_handle.database_interface_dbh = database_interface_dbh; } else if (db) { if (!(new_dbh->native_handle.core_db_dbh = switch_core_alloc(new_dbh->pool, sizeof(*new_dbh->native_handle.core_db_dbh)))) { destroy_handle(&new_dbh); switch_core_db_close(db); goto end; } new_dbh->native_handle.core_db_dbh->handle = db; new_dbh->native_handle.core_db_dbh->in_memory = connection_options->core_db_options.in_memory; } else if (odbc_dbh) { new_dbh->native_handle.odbc_dbh = odbc_dbh; } add_handle(new_dbh, db_str, db_callsite_str, thread_str); } end: if (new_dbh) { new_dbh->last_used = switch_epoch_time_now(NULL); } *dbh = new_dbh; return *dbh ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE; } static switch_status_t switch_cache_db_execute_sql_real(switch_cache_db_handle_t *dbh, const char *sql, char **err) { switch_status_t status = SWITCH_STATUS_FALSE; char *errmsg = NULL; char *tmp = NULL; char *type = NULL; if (err) { *err = NULL; } switch (dbh->type) { case SCDB_TYPE_DATABASE_INTERFACE: { switch_database_interface_t *database_interface = dbh->native_handle.database_interface_dbh->connection_options.database_interface; type = (char *)dbh->native_handle.database_interface_dbh->connection_options.prefix; status = database_interface_handle_exec(database_interface, dbh->native_handle.database_interface_dbh, sql, &errmsg); } break; case SCDB_TYPE_ODBC: { type = "ODBC"; status = switch_odbc_handle_exec(dbh->native_handle.odbc_dbh, sql, NULL, &errmsg) == SWITCH_ODBC_SUCCESS ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE; } break; case SCDB_TYPE_CORE_DB: { int ret = switch_core_db_exec(dbh->native_handle.core_db_dbh->handle, sql, NULL, NULL, &errmsg); type = "NATIVE"; if (ret == SWITCH_CORE_DB_OK) { status = SWITCH_STATUS_SUCCESS; } if (errmsg) { switch_strdup(tmp, errmsg); switch_core_db_free(errmsg); errmsg = tmp; } } break; } if (errmsg) { if (!switch_stristr("already exists", errmsg) && !switch_stristr("duplicate key name", errmsg)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "[%s] %s SQL ERR [%s]\n%s\n", dbh->name, (type ? type : "Unknown"), errmsg, sql); } if (err) { *err = errmsg; } else { switch_safe_free(errmsg); } } return status; } /** OMFG you cruel bastards. Who chooses 64k as a max buffer len for a sql statement, have you ever heard of transactions? **/ static switch_status_t switch_cache_db_execute_sql_chunked(switch_cache_db_handle_t *dbh, char *sql, uint32_t chunk_size, char **err) { switch_status_t status = SWITCH_STATUS_FALSE; char *p, *s, *e; switch_size_t len; switch_assert(chunk_size); if (err) *err = NULL; len = strlen(sql); if (chunk_size > len) { return switch_cache_db_execute_sql_real(dbh, sql, err); } if (!(strlen(sql) / chunk_size)) { return SWITCH_STATUS_FALSE; } e = end_of_p(sql); s = sql; while (s && s < e) { p = s + chunk_size; if (p > e) { p = e; } while (p > s) { if (*p == '\n' && *(p - 1) == ';') { *p = '\0'; *(p - 1) = '\0'; p++; break; } p--; } status = switch_cache_db_execute_sql_real(dbh, s, err); if (p <= s || status != SWITCH_STATUS_SUCCESS || (err && *err)) { break; } s = p; } return status; } SWITCH_DECLARE(switch_status_t) switch_cache_db_execute_sql(switch_cache_db_handle_t *dbh, char *sql, char **err) { switch_status_t status = SWITCH_STATUS_FALSE; switch (dbh->type) { default: { status = switch_cache_db_execute_sql_chunked(dbh, (char *) sql, 32768, err); } break; } return status; } SWITCH_DECLARE(int) switch_cache_db_affected_rows(switch_cache_db_handle_t *dbh) { switch (dbh->type) { case SCDB_TYPE_CORE_DB: { return switch_core_db_changes(dbh->native_handle.core_db_dbh->handle); } break; case SCDB_TYPE_ODBC: { return switch_odbc_handle_affected_rows(dbh->native_handle.odbc_dbh); } break; case SCDB_TYPE_DATABASE_INTERFACE: { switch_database_interface_t *database_interface = dbh->native_handle.database_interface_dbh->connection_options.database_interface; int affected_rows = 0; database_interface->affected_rows(dbh->native_handle.database_interface_dbh, &affected_rows); return affected_rows; } break; } return 0; } SWITCH_DECLARE(int) switch_cache_db_load_extension(switch_cache_db_handle_t *dbh, const char *extension) { switch (dbh->type) { case SCDB_TYPE_CORE_DB: { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "try to load extension [%s]!\n", extension); return switch_core_db_load_extension(dbh->native_handle.core_db_dbh->handle, extension); } break; case SCDB_TYPE_ODBC: { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "load extension not supported by type ODBC!\n"); } break; case SCDB_TYPE_DATABASE_INTERFACE: { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "load extension not supported by type DATABASE_INTERFACE!\n"); } break; } return 0; } SWITCH_DECLARE(char *) switch_cache_db_execute_sql2str(switch_cache_db_handle_t *dbh, char *sql, char *str, size_t len, char **err) { switch_status_t status = SWITCH_STATUS_FALSE; memset(str, 0, len); switch (dbh->type) { case SCDB_TYPE_CORE_DB: { switch_core_db_stmt_t *stmt; if (switch_core_db_prepare(dbh->native_handle.core_db_dbh->handle, sql, -1, &stmt, 0)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Statement Error [%s]!\n", sql); goto end; } else { int running = 1; while (running < 5000) { int result = switch_core_db_step(stmt); const unsigned char *txt; if (result == SWITCH_CORE_DB_ROW) { if (switch_core_db_column_count(stmt) > 0) { if ((txt = switch_core_db_column_text(stmt, 0))) { switch_copy_string(str, (char *) txt, len); status = SWITCH_STATUS_SUCCESS; } } break; } else if (result == SWITCH_CORE_DB_BUSY) { running++; switch_cond_next(); continue; } break; } switch_core_db_finalize(stmt); } } break; case SCDB_TYPE_ODBC: { status = switch_odbc_handle_exec_string(dbh->native_handle.odbc_dbh, sql, str, len, err) == SWITCH_ODBC_SUCCESS ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE; } break; case SCDB_TYPE_DATABASE_INTERFACE: { switch_database_interface_t *database_interface = dbh->native_handle.database_interface_dbh->connection_options.database_interface; status = database_interface->exec_string(dbh->native_handle.database_interface_dbh, sql, str, len, err); } break; } end: return status == SWITCH_STATUS_SUCCESS ? str : NULL; } SWITCH_DECLARE(switch_status_t) switch_cache_db_persistant_execute(switch_cache_db_handle_t *dbh, const char *sql, uint32_t retries) { char *errmsg = NULL; switch_status_t status = SWITCH_STATUS_FALSE; uint8_t forever = 0; if (!retries) { forever = 1; retries = 1000; } while (retries > 0) { switch_cache_db_execute_sql_real(dbh, sql, &errmsg); if (errmsg) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SQL ERR [%s]\n", errmsg); switch_safe_free(errmsg); switch_yield(100000); retries--; if (retries == 0 && forever) { retries = 1000; continue; } } else { status = SWITCH_STATUS_SUCCESS; break; } } return status; } SWITCH_DECLARE(switch_status_t) switch_cache_db_persistant_execute_trans_full(switch_cache_db_handle_t *dbh, char *sql, uint32_t retries, const char *pre_trans_execute, const char *post_trans_execute, const char *inner_pre_trans_execute, const char *inner_post_trans_execute) { char *errmsg = NULL; switch_status_t status = SWITCH_STATUS_FALSE; uint8_t forever = 0; unsigned begin_retries = 100; uint8_t again = 0; if (!retries) { forever = 1; retries = 1000; } if (!zstr(pre_trans_execute)) { switch_cache_db_execute_sql_real(dbh, pre_trans_execute, &errmsg); if (errmsg) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "SQL PRE TRANS EXEC %s [%s]\n", pre_trans_execute, errmsg); switch_safe_free(errmsg); } } again: while (begin_retries > 0) { again = 0; switch(dbh->type) { case SCDB_TYPE_CORE_DB: { switch_cache_db_execute_sql_real(dbh, "BEGIN EXCLUSIVE", &errmsg); } break; case SCDB_TYPE_ODBC: { switch_odbc_status_t result; if ((result = switch_odbc_SQLSetAutoCommitAttr(dbh->native_handle.odbc_dbh, 0)) != SWITCH_ODBC_SUCCESS) { char tmp[100]; switch_snprintfv(tmp, sizeof(tmp), "%q-%i", "Unable to Set AutoCommit Off", result); errmsg = strdup(tmp); } } break; case SCDB_TYPE_DATABASE_INTERFACE: { switch_database_interface_t *database_interface = dbh->native_handle.database_interface_dbh->connection_options.database_interface; switch_status_t result; if ((result = database_interface->sql_set_auto_commit_attr(dbh->native_handle.database_interface_dbh, 0)) != SWITCH_STATUS_SUCCESS) { char tmp[100]; switch_snprintfv(tmp, sizeof(tmp), "%q-%i", "Unable to Set AutoCommit Off", result); errmsg = strdup(tmp); } } break; } if (errmsg) { begin_retries--; if (strstr(errmsg, "cannot start a transaction within a transaction")) { again = 1; } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "SQL Retry [%s]\n", errmsg); } switch_safe_free(errmsg); if (again) { switch(dbh->type) { case SCDB_TYPE_CORE_DB: { switch_cache_db_execute_sql_real(dbh, "COMMIT", NULL); } break; case SCDB_TYPE_ODBC: { switch_odbc_SQLEndTran(dbh->native_handle.odbc_dbh, 1); switch_odbc_SQLSetAutoCommitAttr(dbh->native_handle.odbc_dbh, 1); } break; case SCDB_TYPE_DATABASE_INTERFACE: { switch_database_interface_t *database_interface = dbh->native_handle.database_interface_dbh->connection_options.database_interface; switch_status_t result; if ((result = database_interface->commit(dbh->native_handle.database_interface_dbh)) != SWITCH_STATUS_SUCCESS) { char tmp[100]; switch_snprintfv(tmp, sizeof(tmp), "%q-%i", "Unable to commit transaction", result); } } break; } goto again; } switch_yield(100000); if (begin_retries == 0) { goto done; } continue; } break; } if (!zstr(inner_pre_trans_execute)) { switch_cache_db_execute_sql_real(dbh, inner_pre_trans_execute, &errmsg); if (errmsg) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "SQL PRE TRANS EXEC %s [%s]\n", inner_pre_trans_execute, errmsg); switch_safe_free(errmsg); } } while (retries > 0) { switch_cache_db_execute_sql(dbh, sql, &errmsg); if (errmsg) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SQL ERR [%s]\n", errmsg); switch_safe_free(errmsg); errmsg = NULL; switch_yield(100000); retries--; if (retries == 0 && forever) { retries = 1000; continue; } } else { status = SWITCH_STATUS_SUCCESS; break; } } if (!zstr(inner_post_trans_execute)) { switch_cache_db_execute_sql_real(dbh, inner_post_trans_execute, &errmsg); if (errmsg) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "SQL POST TRANS EXEC %s [%s]\n", inner_post_trans_execute, errmsg); switch_safe_free(errmsg); } } done: switch(dbh->type) { case SCDB_TYPE_CORE_DB: { switch_cache_db_execute_sql_real(dbh, "COMMIT", NULL); } break; case SCDB_TYPE_ODBC: { switch_odbc_SQLEndTran(dbh->native_handle.odbc_dbh, 1); switch_odbc_SQLSetAutoCommitAttr(dbh->native_handle.odbc_dbh, 1); } break; case SCDB_TYPE_DATABASE_INTERFACE: { switch_database_interface_t *database_interface = dbh->native_handle.database_interface_dbh->connection_options.database_interface; switch_status_t result; if ((result = database_interface->commit(dbh->native_handle.database_interface_dbh)) != SWITCH_STATUS_SUCCESS) { char tmp[100]; switch_snprintfv(tmp, sizeof(tmp), "%q-%i", "Unable to commit transaction", result); } } break; } if (!zstr(post_trans_execute)) { switch_cache_db_execute_sql_real(dbh, post_trans_execute, &errmsg); if (errmsg) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "SQL POST TRANS EXEC %s [%s]\n", post_trans_execute, errmsg); switch_safe_free(errmsg); } } return status; } struct helper { switch_core_db_event_callback_func_t callback; void *pdata; }; static int helper_callback(void *pArg, int argc, char **argv, char **columnNames) { struct helper *h = (struct helper *) pArg; int r = 0; switch_event_t *event; switch_event_create_array_pair(&event, columnNames, argv, argc); r = h->callback(h->pdata, event); switch_event_destroy(&event); return r; } SWITCH_DECLARE(switch_status_t) switch_cache_db_execute_sql_event_callback(switch_cache_db_handle_t *dbh, const char *sql, switch_core_db_event_callback_func_t callback, void *pdata, char **err) { switch_status_t status = SWITCH_STATUS_FALSE; char *errmsg = NULL; struct helper h = {0}; if (err) { *err = NULL; } h.callback = callback; h.pdata = pdata; switch (dbh->type) { case SCDB_TYPE_DATABASE_INTERFACE: { switch_database_interface_t *database_interface = dbh->native_handle.database_interface_dbh->connection_options.database_interface; if ((status = database_interface_handle_callback_exec(database_interface, dbh->native_handle.database_interface_dbh, sql, helper_callback, &h, err)) != SWITCH_STATUS_SUCCESS) { char tmp[100]; switch_snprintfv(tmp, sizeof(tmp), "%q-%i", "Unable to execute_sql_event_callback", status); } } break; case SCDB_TYPE_ODBC: { status = switch_odbc_handle_callback_exec(dbh->native_handle.odbc_dbh, sql, helper_callback, &h, err) == SWITCH_ODBC_SUCCESS ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE; } break; case SCDB_TYPE_CORE_DB: { int ret = switch_core_db_exec(dbh->native_handle.core_db_dbh->handle, sql, helper_callback, &h, &errmsg); if (ret == SWITCH_CORE_DB_OK || ret == SWITCH_CORE_DB_ABORT) { status = SWITCH_STATUS_SUCCESS; } if (errmsg) { dbh->last_used = switch_epoch_time_now(NULL) - (SQL_CACHE_TIMEOUT * 2); if (!strstr(errmsg, "query abort")) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SQL ERR: [%s] %s\n", sql, errmsg); } switch_core_db_free(errmsg); } } break; } return status; } SWITCH_DECLARE(switch_status_t) switch_cache_db_execute_sql_event_callback_err(switch_cache_db_handle_t *dbh, const char *sql, switch_core_db_event_callback_func_t callback, switch_core_db_err_callback_func_t err_callback, void *pdata, char **err) { switch_status_t status = SWITCH_STATUS_FALSE; char *errmsg = NULL; struct helper h; if (err) { *err = NULL; } h.callback = callback; h.pdata = pdata; switch (dbh->type) { case SCDB_TYPE_DATABASE_INTERFACE: { switch_database_interface_t *database_interface = dbh->native_handle.database_interface_dbh->connection_options.database_interface; if ((status = database_interface_handle_callback_exec(database_interface, dbh->native_handle.database_interface_dbh, sql, helper_callback, &h, err)) != SWITCH_STATUS_SUCCESS) { char tmp[100]; switch_snprintfv(tmp, sizeof(tmp), "%q-%i", "Unable to execute_sql_event_callback_err", status); } if (err && *err) { (*err_callback)(pdata, (const char*)*err); } } break; case SCDB_TYPE_ODBC: { status = switch_odbc_handle_callback_exec(dbh->native_handle.odbc_dbh, sql, helper_callback, &h, err) == SWITCH_ODBC_SUCCESS ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE; if (err && *err) { (*err_callback)(pdata, (const char*)*err); } } break; case SCDB_TYPE_CORE_DB: { int ret = switch_core_db_exec(dbh->native_handle.core_db_dbh->handle, sql, helper_callback, &h, &errmsg); if (ret == SWITCH_CORE_DB_OK || ret == SWITCH_CORE_DB_ABORT) { status = SWITCH_STATUS_SUCCESS; } if (errmsg) { dbh->last_used = switch_epoch_time_now(NULL) - (SQL_CACHE_TIMEOUT * 2); if (!strstr(errmsg, "query abort")) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SQL ERR: [%s] %s\n", sql, errmsg); } } if ((ret == SWITCH_CORE_DB_ABORT || errmsg) && err_callback) { (*err_callback)(pdata, errmsg); } if (errmsg) { switch_core_db_free(errmsg); } } break; } return status; } SWITCH_DECLARE(switch_status_t) switch_cache_db_execute_sql_callback(switch_cache_db_handle_t *dbh, const char *sql, switch_core_db_callback_func_t callback, void *pdata, char **err) { switch_status_t status = SWITCH_STATUS_FALSE; char *errmsg = NULL; if (err) { *err = NULL; } switch (dbh->type) { case SCDB_TYPE_DATABASE_INTERFACE: { switch_database_interface_t *database_interface = dbh->native_handle.database_interface_dbh->connection_options.database_interface; if ((status = database_interface_handle_callback_exec(database_interface, dbh->native_handle.database_interface_dbh, sql, callback, pdata, err)) != SWITCH_STATUS_SUCCESS) { char tmp[100]; switch_snprintfv(tmp, sizeof(tmp), "%q-%i", "Unable to execute_sql_callback", status); } } break; case SCDB_TYPE_ODBC: { status = switch_odbc_handle_callback_exec(dbh->native_handle.odbc_dbh, sql, callback, pdata, err) == SWITCH_ODBC_SUCCESS ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE; } break; case SCDB_TYPE_CORE_DB: { int ret = switch_core_db_exec(dbh->native_handle.core_db_dbh->handle, sql, callback, pdata, &errmsg); if (ret == SWITCH_CORE_DB_OK || ret == SWITCH_CORE_DB_ABORT) { status = SWITCH_STATUS_SUCCESS; } if (errmsg) { dbh->last_used = switch_epoch_time_now(NULL) - (SQL_CACHE_TIMEOUT * 2); if (!strstr(errmsg, "query abort")) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SQL ERR: [%s] %s\n", sql, errmsg); } switch_core_db_free(errmsg); } } break; } return status; } SWITCH_DECLARE(switch_status_t) switch_cache_db_execute_sql_callback_err(switch_cache_db_handle_t *dbh, const char *sql, switch_core_db_callback_func_t callback, switch_core_db_err_callback_func_t err_callback, void *pdata, char **err) { switch_status_t status = SWITCH_STATUS_FALSE; char *errmsg = NULL; if (err) { *err = NULL; } switch (dbh->type) { case SCDB_TYPE_DATABASE_INTERFACE: { switch_database_interface_t *database_interface = dbh->native_handle.database_interface_dbh->connection_options.database_interface; if ((status = database_interface_handle_callback_exec(database_interface, dbh->native_handle.database_interface_dbh, sql, callback, pdata, err)) != SWITCH_STATUS_SUCCESS) { char tmp[100]; switch_snprintfv(tmp, sizeof(tmp), "%q-%i", "Unable to execute_sql_callback_err", status); } if (err && *err) { (*err_callback)(pdata, (const char*)*err); } } break; case SCDB_TYPE_ODBC: { status = switch_odbc_handle_callback_exec(dbh->native_handle.odbc_dbh, sql, callback, pdata, err) == SWITCH_ODBC_SUCCESS ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE; if (err && *err) { (*err_callback)(pdata, (const char*)*err); } } break; case SCDB_TYPE_CORE_DB: { int ret = switch_core_db_exec(dbh->native_handle.core_db_dbh->handle, sql, callback, pdata, &errmsg); if (ret == SWITCH_CORE_DB_OK || ret == SWITCH_CORE_DB_ABORT) { status = SWITCH_STATUS_SUCCESS; } if (errmsg) { dbh->last_used = switch_epoch_time_now(NULL) - (SQL_CACHE_TIMEOUT * 2); if (!strstr(errmsg, "query abort")) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SQL ERR: [%s] %s\n", sql, errmsg); } } if ((ret == SWITCH_CORE_DB_ABORT || errmsg) && err_callback) { (*err_callback)(pdata, errmsg); } if (errmsg) { switch_core_db_free(errmsg); } } break; } return status; } SWITCH_DECLARE(switch_status_t) switch_cache_db_create_schema(switch_cache_db_handle_t *dbh, char *sql, char **err) { switch_status_t r = SWITCH_STATUS_SUCCESS; switch_assert(sql != NULL); if (switch_test_flag((&runtime), SCF_AUTO_SCHEMAS)) { r = switch_cache_db_execute_sql(dbh, sql, err); } return r; } /*! * \brief Performs test_sql and if it fails performs drop_sql and reactive_sql. * * If auto-clear-sql is disabled, then this function will do nothing and it is * assumed that the queries are not needed. If auto-create-schemas is disabled, * then just test_sql is executed, but drop_sql and reactive_sql are not. * * Otherwise, test_sql gets executed. If that succeeds, then there is nothing to * do. Otherwise drop_sql is executed (its result is ignored) and then finally * reactive_sql is executed. * * \return If auto-create-schemas is enabled, SWITCH_TRUE is returned if * test_sql succeeds, SWITCH_FALSE otherwise. If reactive_sql is executed * successfully SWITCH_TRUE is returned, otherwise SWITCH_FALSE is returned. */ SWITCH_DECLARE(switch_bool_t) switch_cache_db_test_reactive(switch_cache_db_handle_t *dbh, const char *test_sql, const char *drop_sql, const char *reactive_sql) { return switch_cache_db_test_reactive_ex(dbh, test_sql, drop_sql, reactive_sql, NULL); } SWITCH_DECLARE(switch_bool_t) switch_cache_db_test_reactive_ex(switch_cache_db_handle_t *dbh, const char *test_sql, const char *drop_sql, const char *reactive_sql, const char *row_size_limited_reactive_sql) { switch_bool_t r = SWITCH_TRUE; switch_assert(test_sql != NULL); switch_assert(reactive_sql != NULL); if (!switch_test_flag((&runtime), SCF_CLEAR_SQL)) { return SWITCH_TRUE; } if (!switch_test_flag((&runtime), SCF_AUTO_SCHEMAS)) { switch_status_t status = switch_cache_db_execute_sql(dbh, (char *)test_sql, NULL); return (status == SWITCH_STATUS_SUCCESS) ? SWITCH_TRUE : SWITCH_FALSE; } switch (dbh->type) { case SCDB_TYPE_DATABASE_INTERFACE: { switch_database_interface_t *database_interface = dbh->native_handle.database_interface_dbh->connection_options.database_interface; switch_status_t result; if ((result = database_interface_handle_exec(database_interface, dbh->native_handle.database_interface_dbh, test_sql, NULL)) != SWITCH_STATUS_SUCCESS) { char tmp[100]; switch_snprintfv(tmp, sizeof(tmp), "%q-%i", "Unable to test_reactive with test_sql", result); if (drop_sql) { if ((result = database_interface_handle_exec(database_interface, dbh->native_handle.database_interface_dbh, drop_sql, NULL)) != SWITCH_STATUS_SUCCESS) { char tmp[100]; switch_snprintfv(tmp, sizeof(tmp), "%q-%i", "Unable to test_reactive with drop_sql", result); } } if ((result = database_interface_handle_exec(database_interface, dbh->native_handle.database_interface_dbh, reactive_sql, NULL)) != SWITCH_STATUS_SUCCESS) { char tmp[100]; switch_snprintfv(tmp, sizeof(tmp), "%q-%i", "Unable to test_reactive with reactive_sql", result); if (row_size_limited_reactive_sql && switch_test_flag(database_interface, SWITCH_DATABASE_FLAG_ROW_SIZE_LIMIT)) { if ((result = database_interface_handle_exec(database_interface, dbh->native_handle.database_interface_dbh, row_size_limited_reactive_sql, NULL)) != SWITCH_STATUS_SUCCESS) { switch_snprintfv(tmp, sizeof(tmp), "%q-%i", "Unable to test_reactive with row_size_limited_reactive_sql", result); } } } r = (result == SWITCH_STATUS_SUCCESS); } } break; case SCDB_TYPE_ODBC: { if (switch_odbc_handle_exec(dbh->native_handle.odbc_dbh, test_sql, NULL, NULL) != SWITCH_ODBC_SUCCESS) { if (drop_sql) { switch_odbc_handle_exec(dbh->native_handle.odbc_dbh, drop_sql, NULL, NULL); } r = switch_odbc_handle_exec(dbh->native_handle.odbc_dbh, reactive_sql, NULL, NULL) == SWITCH_ODBC_SUCCESS; } } break; case SCDB_TYPE_CORE_DB: { char *errmsg = NULL; switch_core_db_exec(dbh->native_handle.core_db_dbh->handle, test_sql, NULL, NULL, &errmsg); if (errmsg) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "SQL ERR [%s]\n[%s]\nAuto Generating Table!\n", errmsg, test_sql); switch_core_db_free(errmsg); errmsg = NULL; if (drop_sql) { switch_core_db_exec(dbh->native_handle.core_db_dbh->handle, drop_sql, NULL, NULL, &errmsg); } if (errmsg) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Ignoring SQL ERR [%s]\n[%s]\n", errmsg, drop_sql); switch_core_db_free(errmsg); errmsg = NULL; } switch_core_db_exec(dbh->native_handle.core_db_dbh->handle, reactive_sql, NULL, NULL, &errmsg); if (errmsg) { r = SWITCH_FALSE; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "SQL ERR [%s]\n[%s]\n", errmsg, reactive_sql); switch_core_db_free(errmsg); errmsg = NULL; } else { r = SWITCH_TRUE; } } } break; } return r; } static void *SWITCH_THREAD_FUNC switch_core_sql_db_thread(switch_thread_t *thread, void *obj) { int sec = 0, reg_sec = 0;; sql_manager.db_thread_running = 1; while (sql_manager.db_thread_running == 1) { if (++sec == SQL_CACHE_TIMEOUT) { sql_close(switch_epoch_time_now(NULL)); sec = 0; } if (switch_test_flag((&runtime), SCF_USE_SQL) && ++reg_sec == SQL_REG_TIMEOUT) { switch_core_expire_registration(0); reg_sec = 0; } switch_yield(1000000); } return NULL; } static void *SWITCH_THREAD_FUNC switch_user_sql_thread(switch_thread_t *thread, void *obj); struct switch_sql_queue_manager { const char *name; switch_cache_db_handle_t *event_db; switch_queue_t **sql_queue; uint32_t *pre_written; uint32_t *written; uint32_t numq; char *dsn; switch_thread_t *thread; int thread_initiated; int thread_running; switch_thread_cond_t *cond; switch_mutex_t *cond_mutex; switch_mutex_t *cond2_mutex; switch_mutex_t *mutex; char *pre_trans_execute; char *post_trans_execute; char *inner_pre_trans_execute; char *inner_post_trans_execute; switch_memory_pool_t *pool; uint32_t max_trans; uint32_t confirm; uint8_t paused; int skip_wait; }; static int qm_wake(switch_sql_queue_manager_t *qm) { switch_status_t status; int tries = 0; top: status = switch_mutex_trylock(qm->cond_mutex); if (status == SWITCH_STATUS_SUCCESS) { switch_thread_cond_signal(qm->cond); switch_mutex_unlock(qm->cond_mutex); return 1; } else { if (switch_mutex_trylock(qm->cond2_mutex) == SWITCH_STATUS_SUCCESS) { qm->skip_wait++; switch_mutex_unlock(qm->cond2_mutex); } else { if (++tries < 10) { switch_cond_next(); goto top; } } } return 0; } static uint32_t qm_ttl(switch_sql_queue_manager_t *qm) { uint32_t ttl = 0; uint32_t i; for (i = 0; i < qm->numq; i++) { ttl += switch_queue_size(qm->sql_queue[i]); } return ttl; } struct db_job { switch_sql_queue_manager_t *qm; char *sql; switch_core_db_callback_func_t callback; switch_core_db_err_callback_func_t err_callback; switch_core_db_event_callback_func_t event_callback; switch_core_db_err_callback_func_t event_err_callback; void *pdata; int event; switch_memory_pool_t *pool; }; static void *SWITCH_THREAD_FUNC sql_in_thread (switch_thread_t *thread, void *obj) { struct db_job *job = (struct db_job *) obj; switch_memory_pool_t *pool = job->pool; char *err = NULL; switch_cache_db_handle_t *dbh; if (switch_cache_db_get_db_handle_dsn(&dbh, job->qm->dsn) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot connect DSN %s\n", job->qm->dsn); return NULL; } if (job->callback && !job->err_callback) { switch_cache_db_execute_sql_callback(dbh, job->sql, job->callback, job->pdata, &err); } else if (job->callback && job->err_callback) { switch_cache_db_execute_sql_callback_err(dbh, job->sql, job->callback, job->err_callback, job->pdata, &err); } else if (job->event_callback && !job->event_err_callback) { switch_cache_db_execute_sql_event_callback(dbh, job->sql, job->event_callback, job->pdata, &err); } else if (job->event_callback && job->event_err_callback) { switch_cache_db_execute_sql_event_callback_err(dbh, job->sql, job->event_callback, job->event_err_callback, job->pdata, &err); } if (err) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SQL ERR: [%s] %s\n", job->sql, err); switch_safe_free(err); } switch_cache_db_release_db_handle(&dbh); if (pool) { switch_core_destroy_memory_pool(&pool); } return NULL; } static switch_thread_data_t *new_job(switch_sql_queue_manager_t *qm, const char *sql, switch_core_db_callback_func_t callback, switch_core_db_err_callback_func_t err_callback, switch_core_db_event_callback_func_t event_callback, switch_core_db_err_callback_func_t event_err_callback, void *pdata) { switch_memory_pool_t *pool; switch_thread_data_t *td; struct db_job *job; switch_core_new_memory_pool(&pool); td = switch_core_alloc(pool, sizeof(*td)); job = switch_core_alloc(pool, sizeof(*job)); td->func = sql_in_thread; td->obj = job; job->sql = switch_core_strdup(pool, sql); job->qm = qm; if (callback) { job->callback = callback; job->err_callback = err_callback; } else if (event_callback) { job->event_callback = event_callback; job->event_err_callback = event_err_callback; } job->pdata = pdata; job->pool = pool; return td; } SWITCH_DECLARE(void) switch_sql_queue_manager_execute_sql_callback(switch_sql_queue_manager_t *qm, const char *sql, switch_core_db_callback_func_t callback, void *pdata) { switch_thread_data_t *td; if ((td = new_job(qm, sql, callback, NULL, NULL, NULL, pdata))) { switch_thread_pool_launch_thread(&td); } } SWITCH_DECLARE(void) switch_sql_queue_manager_execute_sql_callback_err(switch_sql_queue_manager_t *qm, const char *sql, switch_core_db_callback_func_t callback, switch_core_db_err_callback_func_t err_callback, void *pdata) { switch_thread_data_t *td; if ((td = new_job(qm, sql, callback, err_callback, NULL, NULL, pdata))) { switch_thread_pool_launch_thread(&td); } } SWITCH_DECLARE(void) switch_sql_queue_manager_execute_sql_event_callback(switch_sql_queue_manager_t *qm, const char *sql, switch_core_db_event_callback_func_t callback, void *pdata) { switch_thread_data_t *td; if ((td = new_job(qm, sql, NULL, NULL, callback, NULL, pdata))) { switch_thread_pool_launch_thread(&td); } } SWITCH_DECLARE(void) switch_sql_queue_manager_execute_sql_event_callback_err(switch_sql_queue_manager_t *qm, const char *sql, switch_core_db_event_callback_func_t callback, switch_core_db_err_callback_func_t err_callback, void *pdata) { switch_thread_data_t *td; if ((td = new_job(qm, sql, NULL, NULL, callback, err_callback, pdata))) { switch_thread_pool_launch_thread(&td); } } static void do_flush(switch_sql_queue_manager_t *qm, int i, switch_cache_db_handle_t *dbh) { void *pop = NULL; switch_queue_t *q = qm->sql_queue[i]; switch_mutex_lock(qm->mutex); while (switch_queue_trypop(q, &pop) == SWITCH_STATUS_SUCCESS) { if (pop) { if (dbh) { switch_cache_db_execute_sql(dbh, (char *) pop, NULL); } switch_safe_free(pop); } } switch_mutex_unlock(qm->mutex); } SWITCH_DECLARE(void) switch_sql_queue_manager_resume(switch_sql_queue_manager_t *qm) { switch_mutex_lock(qm->mutex); qm->paused = 0; switch_mutex_unlock(qm->mutex); qm_wake(qm); } SWITCH_DECLARE(void) switch_sql_queue_manager_pause(switch_sql_queue_manager_t *qm, switch_bool_t flush) { uint32_t i; switch_mutex_lock(qm->mutex); qm->paused = 1; switch_mutex_unlock(qm->mutex); if (flush) { for(i = 0; i < qm->numq; i++) { do_flush(qm, i, NULL); } } } SWITCH_DECLARE(int) switch_sql_queue_manager_size(switch_sql_queue_manager_t *qm, uint32_t index) { int size = 0; switch_mutex_lock(qm->mutex); if (index < qm->numq) { size = switch_queue_size(qm->sql_queue[index]); } switch_mutex_unlock(qm->mutex); return size; } SWITCH_DECLARE(switch_status_t) switch_sql_queue_manager_stop(switch_sql_queue_manager_t *qm) { switch_status_t status = SWITCH_STATUS_FALSE; uint32_t i, sanity = 100; if (qm->thread_running == 1) { qm->thread_running = -1; while(--sanity && qm->thread_running == -1) { for(i = 0; i < qm->numq; i++) { switch_queue_push(qm->sql_queue[i], NULL); switch_queue_interrupt_all(qm->sql_queue[i]); } qm_wake(qm); if (qm->thread_running == -1) { switch_yield(100000); } } status = SWITCH_STATUS_SUCCESS; } if (qm->thread) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s Stopping SQL thread.\n", qm->name); qm_wake(qm); switch_thread_join(&status, qm->thread); qm->thread = NULL; status = SWITCH_STATUS_SUCCESS; } return status; } SWITCH_DECLARE(switch_status_t) switch_sql_queue_manager_start(switch_sql_queue_manager_t *qm) { switch_threadattr_t *thd_attr; if (!qm->thread_running) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s Starting SQL thread.\n", qm->name); switch_threadattr_create(&thd_attr, qm->pool); switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE); switch_threadattr_priority_set(thd_attr, SWITCH_PRI_NORMAL); if (switch_thread_create(&qm->thread, thd_attr, switch_user_sql_thread, qm, qm->pool) == SWITCH_STATUS_SUCCESS) { while (!qm->thread_initiated) { switch_cond_next(); } if (qm->event_db) { return SWITCH_STATUS_SUCCESS; } } } return SWITCH_STATUS_FALSE; } SWITCH_DECLARE(switch_status_t) switch_sql_queue_manager_destroy(switch_sql_queue_manager_t **qmp) { switch_sql_queue_manager_t *qm; switch_status_t status = SWITCH_STATUS_SUCCESS; switch_memory_pool_t *pool; uint32_t i; switch_assert(qmp); qm = *qmp; if (!qm) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "No SQL queue to destroy.\n"); return SWITCH_STATUS_NOOP; } *qmp = NULL; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s Destroying SQL queue.\n", qm->name); switch_sql_queue_manager_stop(qm); for(i = 0; i < qm->numq; i++) { do_flush(qm, i, NULL); } pool = qm->pool; switch_core_destroy_memory_pool(&pool); return status; } SWITCH_DECLARE(switch_status_t) switch_sql_queue_manager_push(switch_sql_queue_manager_t *qm, const char *sql, uint32_t pos, switch_bool_t dup) { char *sqlptr = NULL; switch_status_t status; int x = 0; if (sql_manager.paused || qm->thread_running != 1) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "DROP [%s]\n", sql); if (!dup) free((char *)sql); qm_wake(qm); return SWITCH_STATUS_SUCCESS; } if (qm->thread_running != 1) { if (!dup) free((char *)sql); return SWITCH_STATUS_FALSE; } if (pos > qm->numq - 1) { pos = 0; } sqlptr = dup ? strdup(sql) : (char *)sql; do { switch_mutex_lock(qm->mutex); status = switch_queue_trypush(qm->sql_queue[pos], sqlptr); switch_mutex_unlock(qm->mutex); if (status != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "Delay %d sending sql\n", x); if (x++) { switch_yield(1000000 * x); } } } while(status != SWITCH_STATUS_SUCCESS); qm_wake(qm); return SWITCH_STATUS_SUCCESS; } SWITCH_DECLARE(switch_status_t) switch_sql_queue_manager_push_confirm(switch_sql_queue_manager_t *qm, const char *sql, uint32_t pos, switch_bool_t dup) { #define EXEC_NOW #ifdef EXEC_NOW switch_cache_db_handle_t *dbh; if (sql_manager.paused || qm->thread_running != 1) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "DROP [%s]\n", sql); if (!dup) free((char *)sql); qm_wake(qm); return SWITCH_STATUS_SUCCESS; } if (switch_cache_db_get_db_handle_dsn(&dbh, qm->dsn) == SWITCH_STATUS_SUCCESS) { switch_cache_db_execute_sql(dbh, (char *)sql, NULL); switch_cache_db_release_db_handle(&dbh); } if (!dup) free((char *)sql); #else int size, x = 0, sanity = 0; uint32_t written, want; if (sql_manager.paused) { if (!dup) free((char *)sql); qm_wake(qm); return SWITCH_STATUS_SUCCESS; } if (qm->thread_running != 1) { if (!dup) free((char *)sql); return SWITCH_STATUS_FALSE; } if (pos > qm->numq - 1) { pos = 0; } switch_mutex_lock(qm->mutex); qm->confirm++; switch_queue_push(qm->sql_queue[pos], dup ? strdup(sql) : (char *)sql); written = qm->pre_written[pos]; size = switch_sql_queue_manager_size(qm, pos); want = written + size; switch_mutex_unlock(qm->mutex); qm_wake(qm); while((qm->written[pos] < want) || (qm->written[pos] >= written && want < written && qm->written[pos] > want)) { switch_yield(5000); if (++x == 200) { qm_wake(qm); x = 0; if (++sanity == 20) { break; } } } switch_mutex_lock(qm->mutex); qm->confirm--; switch_mutex_unlock(qm->mutex); #endif return SWITCH_STATUS_SUCCESS; } SWITCH_DECLARE(switch_status_t) switch_sql_queue_manager_init_name(const char *name, switch_sql_queue_manager_t **qmp, uint32_t numq, const char *dsn, uint32_t max_trans, const char *pre_trans_execute, const char *post_trans_execute, const char *inner_pre_trans_execute, const char *inner_post_trans_execute) { switch_memory_pool_t *pool; switch_sql_queue_manager_t *qm; uint32_t i; if (!numq) numq = 1; switch_core_new_memory_pool(&pool); qm = switch_core_alloc(pool, sizeof(*qm)); qm->pool = pool; qm->numq = numq; qm->dsn = switch_core_strdup(qm->pool, dsn); qm->name = switch_core_strdup(qm->pool, name); qm->max_trans = max_trans; switch_mutex_init(&qm->cond_mutex, SWITCH_MUTEX_NESTED, qm->pool); switch_mutex_init(&qm->cond2_mutex, SWITCH_MUTEX_NESTED, qm->pool); switch_mutex_init(&qm->mutex, SWITCH_MUTEX_NESTED, qm->pool); switch_thread_cond_create(&qm->cond, qm->pool); qm->sql_queue = switch_core_alloc(qm->pool, sizeof(switch_queue_t *) * numq); qm->written = switch_core_alloc(qm->pool, sizeof(uint32_t) * numq); qm->pre_written = switch_core_alloc(qm->pool, sizeof(uint32_t) * numq); for (i = 0; i < qm->numq; i++) { switch_queue_create(&qm->sql_queue[i], SWITCH_SQL_QUEUE_LEN, qm->pool); } if (pre_trans_execute) { qm->pre_trans_execute = switch_core_strdup(qm->pool, pre_trans_execute); } if (post_trans_execute) { qm->post_trans_execute = switch_core_strdup(qm->pool, post_trans_execute); } if (inner_pre_trans_execute) { qm->inner_pre_trans_execute = switch_core_strdup(qm->pool, inner_pre_trans_execute); } if (inner_post_trans_execute) { qm->inner_post_trans_execute = switch_core_strdup(qm->pool, inner_post_trans_execute); } *qmp = qm; return SWITCH_STATUS_SUCCESS; } static uint32_t do_trans(switch_sql_queue_manager_t *qm) { char *errmsg = NULL; void *pop; switch_status_t status; uint32_t ttl = 0; uint32_t i; switch_status_t res; if (!zstr(qm->pre_trans_execute)) { switch_cache_db_execute_sql_real(qm->event_db, qm->pre_trans_execute, &errmsg); if (errmsg) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "SQL PRE TRANS EXEC %s [%s]\n", qm->pre_trans_execute, errmsg); switch_safe_free(errmsg); } } switch(qm->event_db->type) { case SCDB_TYPE_CORE_DB: { switch_cache_db_execute_sql_real(qm->event_db, "BEGIN EXCLUSIVE", &errmsg); } break; case SCDB_TYPE_ODBC: { switch_odbc_status_t result; if ((result = switch_odbc_SQLSetAutoCommitAttr(qm->event_db->native_handle.odbc_dbh, 0)) != SWITCH_ODBC_SUCCESS) { char tmp[100]; switch_snprintfv(tmp, sizeof(tmp), "%q-%i", "Unable to Set AutoCommit Off", result); errmsg = strdup(tmp); } } break; case SCDB_TYPE_DATABASE_INTERFACE: { switch_database_interface_t *database_interface = qm->event_db->native_handle.database_interface_dbh->connection_options.database_interface; switch_status_t result; if ((result = database_interface->sql_set_auto_commit_attr(qm->event_db->native_handle.database_interface_dbh, 0)) != SWITCH_STATUS_SUCCESS) { char tmp[100]; switch_snprintfv(tmp, sizeof(tmp), "%q-%i", "Unable to Set AutoCommit Off", result); errmsg = strdup(tmp); } } break; } if (errmsg) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "ERROR [%s], [%s]\n", errmsg, qm->event_db->name); switch_safe_free(errmsg); goto end; } if (!zstr(qm->inner_pre_trans_execute)) { switch_cache_db_execute_sql_real(qm->event_db, qm->inner_pre_trans_execute, &errmsg); if (errmsg) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "SQL PRE TRANS EXEC %s [%s]\n", qm->inner_pre_trans_execute, errmsg); switch_safe_free(errmsg); } } while(qm->max_trans == 0 || ttl <= qm->max_trans) { pop = NULL; for (i = 0; (qm->max_trans == 0 || ttl <= qm->max_trans) && (i < qm->numq); i++) { switch_mutex_lock(qm->mutex); res = switch_queue_trypop(qm->sql_queue[i], &pop); (void)res; switch_mutex_unlock(qm->mutex); if (pop) break; } if (pop) { if ((status = switch_cache_db_execute_sql(qm->event_db, (char *) pop, NULL)) == SWITCH_STATUS_SUCCESS) { switch_mutex_lock(qm->mutex); qm->pre_written[i]++; switch_mutex_unlock(qm->mutex); ttl++; } switch_safe_free(pop); if (status != SWITCH_STATUS_SUCCESS) break; } else { break; } } if (!zstr(qm->inner_post_trans_execute)) { switch_cache_db_execute_sql_real(qm->event_db, qm->inner_post_trans_execute, &errmsg); if (errmsg) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "SQL POST TRANS EXEC %s [%s]\n", qm->inner_post_trans_execute, errmsg); switch_safe_free(errmsg); } } end: switch(qm->event_db->type) { case SCDB_TYPE_CORE_DB: { switch_cache_db_execute_sql_real(qm->event_db, "COMMIT", NULL); } break; case SCDB_TYPE_ODBC: { switch_odbc_SQLEndTran(qm->event_db->native_handle.odbc_dbh, 1); switch_odbc_SQLSetAutoCommitAttr(qm->event_db->native_handle.odbc_dbh, 1); } break; case SCDB_TYPE_DATABASE_INTERFACE: { switch_database_interface_t *database_interface = qm->event_db->native_handle.database_interface_dbh->connection_options.database_interface; switch_status_t result; if ((result = database_interface->commit(qm->event_db->native_handle.database_interface_dbh)) != SWITCH_STATUS_SUCCESS) { char tmp[100]; switch_snprintfv(tmp, sizeof(tmp), "%q-%i", "Unable to commit transaction", result); } } break; } if (!zstr(qm->post_trans_execute)) { switch_cache_db_execute_sql_real(qm->event_db, qm->post_trans_execute, &errmsg); if (errmsg) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "SQL POST TRANS EXEC %s [%s]\n", qm->post_trans_execute, errmsg); switch_safe_free(errmsg); } } switch_mutex_lock(qm->mutex); for (i = 0; i < qm->numq; i++) { qm->written[i] = qm->pre_written[i]; } switch_mutex_unlock(qm->mutex); return ttl; } static void *SWITCH_THREAD_FUNC switch_user_sql_thread(switch_thread_t *thread, void *obj) { uint32_t sanity = 120; switch_sql_queue_manager_t *qm = (switch_sql_queue_manager_t *) obj; uint32_t i; while (sanity && !qm->event_db) { if (switch_cache_db_get_db_handle_dsn(&qm->event_db, qm->dsn) == SWITCH_STATUS_SUCCESS && qm->event_db) break; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "%s Error getting db handle, Retrying\n", qm->name); switch_yield(500000); sanity--; } if (!qm->event_db) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "%s Error getting db handle\n", qm->name); qm->thread_initiated = 1; return NULL; } switch_mutex_lock(qm->cond_mutex); switch (qm->event_db->type) { case SCDB_TYPE_DATABASE_INTERFACE: break; case SCDB_TYPE_ODBC: break; case SCDB_TYPE_CORE_DB: { switch_cache_db_execute_sql(qm->event_db, "PRAGMA synchronous=OFF;", NULL); switch_cache_db_execute_sql(qm->event_db, "PRAGMA count_changes=OFF;", NULL); switch_cache_db_execute_sql(qm->event_db, "PRAGMA temp_store=MEMORY;", NULL); switch_cache_db_execute_sql(qm->event_db, "PRAGMA journal_mode=OFF;", NULL); } break; } qm->thread_initiated = 1; qm->thread_running = 1; while (qm->thread_running == 1) { uint32_t i; uint32_t written = 0, iterations = 0; if (qm->paused) { goto check; } if (sql_manager.paused) { for (i = 0; i < qm->numq; i++) { do_flush(qm, i, NULL); } goto check; } do { if (!qm_ttl(qm)) { goto check; } written = do_trans(qm); iterations += written; } while(written == qm->max_trans); if (switch_test_flag((&runtime), SCF_DEBUG_SQL)) { char line[128] = ""; switch_size_t l; switch_snprintf(line, sizeof(line), "%s RUN QUEUE [", qm->name); for (i = 0; i < qm->numq; i++) { l = strlen(line); switch_snprintf(line + l, sizeof(line) - l, "%d%s", switch_queue_size(qm->sql_queue[i]), i == qm->numq - 1 ? "" : "|"); } l = strlen(line); switch_snprintf(line + l, sizeof(line) - l, "]--[%d]\n", iterations); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "%s", line); } check: if (qm_ttl(qm) == 0) { switch_mutex_lock(qm->cond2_mutex); if (qm->skip_wait > 0) { qm->skip_wait--; switch_mutex_unlock(qm->cond2_mutex); } else { switch_mutex_unlock(qm->cond2_mutex); switch_thread_cond_wait(qm->cond, qm->cond_mutex); } } i = 40; while (--i > 0 && qm_ttl(qm) < 500) { switch_yield(5000); } } switch_mutex_unlock(qm->cond_mutex); for(i = 0; i < qm->numq; i++) { do_flush(qm, i, qm->event_db); } switch_cache_db_release_db_handle(&qm->event_db); qm->thread_running = 0; return NULL; } static char *parse_presence_data_cols(switch_event_t *event) { char *cols[128] = { 0 }; int col_count = 0; char *data_copy; switch_stream_handle_t stream = { 0 }; int i; char *r; char col_name[128] = ""; const char *data = switch_event_get_header(event, "presence-data-cols"); if (zstr(data)) { return NULL; } data_copy = strdup(data); col_count = switch_split(data_copy, ':', cols); SWITCH_STANDARD_STREAM(stream); for (i = 0; i < col_count; i++) { const char *val = NULL; switch_snprintfv(col_name, sizeof(col_name), "PD-%q", cols[i]); val = switch_event_get_header_nil(event, col_name); if (zstr(val)) { stream.write_function(&stream, "%q=NULL,", cols[i]); } else { stream.write_function(&stream, "%q='%q',", cols[i], val); } } r = (char *) stream.data; if (end_of(r) == ',') { end_of(r) = '\0'; } switch_safe_free(data_copy); return r; } #define MAX_SQL 5 #define new_sql() switch_assert(sql_idx+1 < MAX_SQL); if (exists) sql[sql_idx++] #define new_sql_a() switch_assert(sql_idx+1 < MAX_SQL); sql[sql_idx++] static void core_event_handler(switch_event_t *event) { char *sql[MAX_SQL] = { 0 }; int sql_idx = 0; char *extra_cols; int exists = 1; char *uuid = NULL; switch_assert(event); switch (event->event_id) { case SWITCH_EVENT_CHANNEL_UUID: case SWITCH_EVENT_CHANNEL_CREATE: case SWITCH_EVENT_CHANNEL_ANSWER: case SWITCH_EVENT_CHANNEL_PROGRESS_MEDIA: case SWITCH_EVENT_CHANNEL_HOLD: case SWITCH_EVENT_CHANNEL_UNHOLD: case SWITCH_EVENT_CHANNEL_EXECUTE: case SWITCH_EVENT_CHANNEL_ORIGINATE: case SWITCH_EVENT_CALL_UPDATE: case SWITCH_EVENT_CHANNEL_CALLSTATE: case SWITCH_EVENT_CHANNEL_STATE: case SWITCH_EVENT_CHANNEL_BRIDGE: case SWITCH_EVENT_CHANNEL_UNBRIDGE: case SWITCH_EVENT_CALL_SECURE: { if ((uuid = switch_event_get_header(event, "unique-id"))) { exists = switch_ivr_uuid_exists(uuid); } } break; default: break; } switch (event->event_id) { case SWITCH_EVENT_ADD_SCHEDULE: { const char *id = switch_event_get_header(event, "task-id"); const char *manager = switch_event_get_header(event, "task-sql_manager"); if (id) { new_sql() = switch_mprintf("insert into tasks (task_id, task_desc, task_group, task_runtime, task_sql_manager, hostname) " "values(%q,'%q','%q',%q,%q,'%q')", id, switch_event_get_header_nil(event, "task-desc"), switch_event_get_header_nil(event, "task-group"), switch_event_get_header_nil(event, "task-runtime"), manager ? manager : "0", switch_core_get_hostname() ); } } break; case SWITCH_EVENT_DEL_SCHEDULE: case SWITCH_EVENT_EXE_SCHEDULE: new_sql() = switch_mprintf("delete from tasks where task_id=%q and hostname='%q'", switch_event_get_header_nil(event, "task-id"), switch_core_get_hostname()); break; case SWITCH_EVENT_RE_SCHEDULE: { const char *id = switch_event_get_header(event, "task-id"); const char *manager = switch_event_get_header(event, "task-sql_manager"); if (id) { new_sql() = switch_mprintf("update tasks set task_desc='%q',task_group='%q', task_runtime=%q, task_sql_manager=%q where task_id=%q and hostname='%q'", switch_event_get_header_nil(event, "task-desc"), switch_event_get_header_nil(event, "task-group"), switch_event_get_header_nil(event, "task-runtime"), manager ? manager : "0", id, switch_core_get_hostname() ); } } break; case SWITCH_EVENT_CHANNEL_DESTROY: { const char *uuid = switch_event_get_header(event, "unique-id"); if (uuid) { new_sql() = switch_mprintf("delete from channels where uuid='%q'", uuid); new_sql() = switch_mprintf("delete from calls where (caller_uuid='%q' or callee_uuid='%q')", uuid, uuid); } } break; case SWITCH_EVENT_CHANNEL_UUID: { new_sql() = switch_mprintf("update channels set uuid='%q' where uuid='%q'", switch_event_get_header_nil(event, "unique-id"), switch_event_get_header_nil(event, "old-unique-id") ); new_sql() = switch_mprintf("update channels set call_uuid='%q' where call_uuid='%q'", switch_event_get_header_nil(event, "unique-id"), switch_event_get_header_nil(event, "old-unique-id") ); break; } case SWITCH_EVENT_CHANNEL_CREATE: new_sql() = switch_mprintf("insert into channels (uuid,direction,created,created_epoch, name,state,callstate,dialplan,context,hostname,initial_cid_name,initial_cid_num,initial_ip_addr,initial_dest,initial_dialplan,initial_context) " "values('%q','%q','%q','%ld','%q','%q','%q','%q','%q','%q','%q','%q','%q','%q','%q','%q')", switch_event_get_header_nil(event, "unique-id"), switch_event_get_header_nil(event, "call-direction"), switch_event_get_header_nil(event, "event-date-local"), (long) switch_epoch_time_now(NULL), switch_event_get_header_nil(event, "channel-name"), switch_event_get_header_nil(event, "channel-state"), switch_event_get_header_nil(event, "channel-call-state"), switch_event_get_header_nil(event, "caller-dialplan"), switch_event_get_header_nil(event, "caller-context"), switch_core_get_switchname(), switch_event_get_header_nil(event, "caller-caller-id-name"), switch_event_get_header_nil(event, "caller-caller-id-number"), switch_event_get_header_nil(event, "caller-network-addr"), switch_event_get_header_nil(event, "caller-destination-number"), switch_event_get_header_nil(event, "caller-dialplan"), switch_event_get_header_nil(event, "caller-context") ); break; case SWITCH_EVENT_CHANNEL_ANSWER: case SWITCH_EVENT_CHANNEL_PROGRESS_MEDIA: case SWITCH_EVENT_CODEC: new_sql() = switch_mprintf ("update channels set read_codec='%q',read_rate='%q',read_bit_rate='%q',write_codec='%q',write_rate='%q',write_bit_rate='%q' where uuid='%q'", switch_event_get_header_nil(event, "channel-read-codec-name"), switch_event_get_header_nil(event, "channel-read-codec-rate"), switch_event_get_header_nil(event, "channel-read-codec-bit-rate"), switch_event_get_header_nil(event, "channel-write-codec-name"), switch_event_get_header_nil(event, "channel-write-codec-rate"), switch_event_get_header_nil(event, "channel-write-codec-bit-rate"), switch_event_get_header_nil(event, "unique-id")); break; case SWITCH_EVENT_CHANNEL_HOLD: case SWITCH_EVENT_CHANNEL_UNHOLD: case SWITCH_EVENT_CHANNEL_EXECUTE: { new_sql() = switch_mprintf("update channels set application='%q',application_data='%q'," "presence_id='%q',presence_data='%q',accountcode='%q' where uuid='%q'", switch_event_get_header_nil(event, "application"), switch_event_get_header_nil(event, "application-data"), switch_event_get_header_nil(event, "channel-presence-id"), switch_event_get_header_nil(event, "channel-presence-data"), switch_event_get_header_nil(event, "variable_accountcode"), switch_event_get_header_nil(event, "unique-id") ); } break; case SWITCH_EVENT_CHANNEL_ORIGINATE: { if ((extra_cols = parse_presence_data_cols(event))) { new_sql() = switch_mprintf("update channels set " "presence_id='%q',presence_data='%q',accountcode='%q',call_uuid='%q',%s where uuid='%q'", switch_event_get_header_nil(event, "channel-presence-id"), switch_event_get_header_nil(event, "channel-presence-data"), switch_event_get_header_nil(event, "variable_accountcode"), switch_event_get_header_nil(event, "channel-call-uuid"), extra_cols, switch_event_get_header_nil(event, "unique-id")); free(extra_cols); } else { new_sql() = switch_mprintf("update channels set " "presence_id='%q',presence_data='%q',accountcode='%q',call_uuid='%q' where uuid='%q'", switch_event_get_header_nil(event, "channel-presence-id"), switch_event_get_header_nil(event, "channel-presence-data"), switch_event_get_header_nil(event, "variable_accountcode"), switch_event_get_header_nil(event, "channel-call-uuid"), switch_event_get_header_nil(event, "unique-id")); } } break; case SWITCH_EVENT_CALL_UPDATE: { new_sql() = switch_mprintf("update channels set callee_name='%q',callee_num='%q',sent_callee_name='%q',sent_callee_num='%q',callee_direction='%q'," "cid_name='%q',cid_num='%q' where uuid='%q'", switch_event_get_header_nil(event, "caller-callee-id-name"), switch_event_get_header_nil(event, "caller-callee-id-number"), switch_event_get_header_nil(event, "sent-callee-id-name"), switch_event_get_header_nil(event, "sent-callee-id-number"), switch_event_get_header_nil(event, "direction"), switch_event_get_header_nil(event, "caller-caller-id-name"), switch_event_get_header_nil(event, "caller-caller-id-number"), switch_event_get_header_nil(event, "unique-id") ); } break; case SWITCH_EVENT_CHANNEL_CALLSTATE: { char *num = switch_event_get_header_nil(event, "channel-call-state-number"); switch_channel_callstate_t callstate = CCS_DOWN; if (num) { callstate = atoi(num); } if (callstate != CCS_DOWN && callstate != CCS_HANGUP) { if ((extra_cols = parse_presence_data_cols(event))) { new_sql() = switch_mprintf("update channels set callstate='%q',%s where uuid='%q'", switch_event_get_header_nil(event, "channel-call-state"), extra_cols, switch_event_get_header_nil(event, "unique-id")); free(extra_cols); } else { new_sql() = switch_mprintf("update channels set callstate='%q' where uuid='%q'", switch_event_get_header_nil(event, "channel-call-state"), switch_event_get_header_nil(event, "unique-id")); } } } break; case SWITCH_EVENT_CHANNEL_STATE: { char *state = switch_event_get_header_nil(event, "channel-state-number"); switch_channel_state_t state_i = CS_DESTROY; if (!zstr(state)) { state_i = atoi(state); } switch (state_i) { case CS_NEW: case CS_DESTROY: case CS_REPORTING: #ifndef SWITCH_DEPRECATED_CORE_DB case CS_HANGUP: /* marked for deprication */ #endif case CS_INIT: break; #ifdef SWITCH_DEPRECATED_CORE_DB case CS_HANGUP: /* marked for deprication */ new_sql_a() = switch_mprintf("update channels set state='%q' where uuid='%q'", switch_event_get_header_nil(event, "channel-state"), switch_event_get_header_nil(event, "unique-id")); break; #endif case CS_EXECUTE: if ((extra_cols = parse_presence_data_cols(event))) { new_sql() = switch_mprintf("update channels set state='%q',%s where uuid='%q'", switch_event_get_header_nil(event, "channel-state"), extra_cols, switch_event_get_header_nil(event, "unique-id")); free(extra_cols); } else { new_sql() = switch_mprintf("update channels set state='%q' where uuid='%q'", switch_event_get_header_nil(event, "channel-state"), switch_event_get_header_nil(event, "unique-id")); } break; case CS_ROUTING: if ((extra_cols = parse_presence_data_cols(event))) { new_sql() = switch_mprintf("update channels set state='%q',cid_name='%q',cid_num='%q',callee_name='%q',callee_num='%q'," "sent_callee_name='%q',sent_callee_num='%q'," "ip_addr='%q',dest='%q',dialplan='%q',context='%q',presence_id='%q',presence_data='%q',accountcode='%q',%s " "where uuid='%q'", switch_event_get_header_nil(event, "channel-state"), switch_event_get_header_nil(event, "caller-caller-id-name"), switch_event_get_header_nil(event, "caller-caller-id-number"), switch_event_get_header_nil(event, "caller-callee-id-name"), switch_event_get_header_nil(event, "caller-callee-id-number"), switch_event_get_header_nil(event, "sent-callee-id-name"), switch_event_get_header_nil(event, "sent-callee-id-number"), switch_event_get_header_nil(event, "caller-network-addr"), switch_event_get_header_nil(event, "caller-destination-number"), switch_event_get_header_nil(event, "caller-dialplan"), switch_event_get_header_nil(event, "caller-context"), switch_event_get_header_nil(event, "channel-presence-id"), switch_event_get_header_nil(event, "channel-presence-data"), switch_event_get_header_nil(event, "variable_accountcode"), extra_cols, switch_event_get_header_nil(event, "unique-id")); free(extra_cols); } else { new_sql() = switch_mprintf("update channels set state='%q',cid_name='%q',cid_num='%q',callee_name='%q',callee_num='%q'," "sent_callee_name='%q',sent_callee_num='%q'," "ip_addr='%q',dest='%q',dialplan='%q',context='%q',presence_id='%q',presence_data='%q',accountcode='%q' " "where uuid='%q'", switch_event_get_header_nil(event, "channel-state"), switch_event_get_header_nil(event, "caller-caller-id-name"), switch_event_get_header_nil(event, "caller-caller-id-number"), switch_event_get_header_nil(event, "caller-callee-id-name"), switch_event_get_header_nil(event, "caller-callee-id-number"), switch_event_get_header_nil(event, "sent-callee-id-name"), switch_event_get_header_nil(event, "sent-callee-id-number"), switch_event_get_header_nil(event, "caller-network-addr"), switch_event_get_header_nil(event, "caller-destination-number"), switch_event_get_header_nil(event, "caller-dialplan"), switch_event_get_header_nil(event, "caller-context"), switch_event_get_header_nil(event, "channel-presence-id"), switch_event_get_header_nil(event, "channel-presence-data"), switch_event_get_header_nil(event, "variable_accountcode"), switch_event_get_header_nil(event, "unique-id")); } break; default: new_sql() = switch_mprintf("update channels set state='%q' where uuid='%q'", switch_event_get_header_nil(event, "channel-state"), switch_event_get_header_nil(event, "unique-id")); break; } break; } case SWITCH_EVENT_CHANNEL_BRIDGE: { const char *a_uuid, *b_uuid, *uuid; a_uuid = switch_event_get_header(event, "Bridge-A-Unique-ID"); b_uuid = switch_event_get_header(event, "Bridge-B-Unique-ID"); uuid = switch_event_get_header(event, "unique-id"); if (zstr(a_uuid) || zstr(b_uuid)) { a_uuid = switch_event_get_header_nil(event, "caller-unique-id"); b_uuid = switch_event_get_header_nil(event, "other-leg-unique-id"); } if (uuid && (extra_cols = parse_presence_data_cols(event))) { new_sql() = switch_mprintf("update channels set %s where uuid='%q'", extra_cols, uuid); switch_safe_free(extra_cols); } new_sql() = switch_mprintf("update channels set call_uuid='%q' where uuid='%q' or uuid='%q'", switch_event_get_header_nil(event, "channel-call-uuid"), a_uuid, b_uuid); new_sql() = switch_mprintf("insert into calls (call_uuid,call_created,call_created_epoch," "caller_uuid,callee_uuid,hostname) " "values ('%q','%q','%ld','%q','%q','%q')", switch_event_get_header_nil(event, "channel-call-uuid"), switch_event_get_header_nil(event, "event-date-local"), (long) switch_epoch_time_now(NULL), a_uuid, b_uuid, switch_core_get_switchname() ); } break; case SWITCH_EVENT_CHANNEL_UNBRIDGE: { char *cuuid = switch_event_get_header_nil(event, "caller-unique-id"); char *uuid = switch_event_get_header(event, "unique-id"); if (uuid && (extra_cols = parse_presence_data_cols(event))) { new_sql() = switch_mprintf("update channels set %s where uuid='%q'", extra_cols, uuid); switch_safe_free(extra_cols); } new_sql() = switch_mprintf("update channels set call_uuid=uuid where call_uuid='%q'", switch_event_get_header_nil(event, "channel-call-uuid")); new_sql() = switch_mprintf("delete from calls where (caller_uuid='%q' or callee_uuid='%q')", cuuid, cuuid); break; } case SWITCH_EVENT_SHUTDOWN: new_sql() = switch_mprintf("delete from channels where hostname='%q';" "delete from interfaces where hostname='%q';" "delete from calls where hostname='%q'", switch_core_get_switchname(), switch_core_get_hostname(), switch_core_get_switchname() ); break; case SWITCH_EVENT_LOG: return; case SWITCH_EVENT_MODULE_LOAD: { const char *type = switch_event_get_header_nil(event, "type"); const char *name = switch_event_get_header_nil(event, "name"); const char *description = switch_event_get_header_nil(event, "description"); const char *syntax = switch_event_get_header_nil(event, "syntax"); const char *key = switch_event_get_header_nil(event, "key"); const char *filename = switch_event_get_header_nil(event, "filename"); if (!zstr(type) && !zstr(name)) { new_sql() = switch_mprintf ("insert into interfaces (type,name,description,syntax,ikey,filename,hostname) values('%q','%q','%q','%q','%q','%q','%q')", type, name, switch_str_nil(description), switch_str_nil(syntax), switch_str_nil(key), switch_str_nil(filename), switch_core_get_hostname() ); } break; } case SWITCH_EVENT_MODULE_UNLOAD: { const char *type = switch_event_get_header_nil(event, "type"); const char *name = switch_event_get_header_nil(event, "name"); if (!zstr(type) && !zstr(name)) { new_sql() = switch_mprintf("delete from interfaces where type='%q' and name='%q' and hostname='%q'", type, name, switch_core_get_hostname()); } break; } case SWITCH_EVENT_CALL_SECURE: { const char *type = switch_event_get_header_nil(event, "secure_type"); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Secure Type: %s\n", type); if (zstr(type)) { break; } new_sql() = switch_mprintf("update channels set secure='%q' where uuid='%q'", type, switch_event_get_header_nil(event, "caller-unique-id") ); break; } case SWITCH_EVENT_NAT: { const char *op = switch_event_get_header_nil(event, "op"); switch_bool_t sticky = switch_true(switch_event_get_header_nil(event, "sticky")); if (!strcmp("add", op)) { new_sql() = switch_mprintf("insert into nat (port, proto, sticky, hostname) values (%q, %q, %d,'%q')", switch_event_get_header_nil(event, "port"), switch_event_get_header_nil(event, "proto"), sticky, switch_core_get_hostname() ); } else if (!strcmp("del", op)) { new_sql() = switch_mprintf("delete from nat where port=%q and proto=%q and hostname='%q'", switch_event_get_header_nil(event, "port"), switch_event_get_header_nil(event, "proto"), switch_core_get_hostname()); } else if (!strcmp("status", op)) { /* call show nat api */ } else if (!strcmp("status_response", op)) { /* ignore */ } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unknown op for SWITCH_EVENT_NAT: %s\n", op); } break; } default: break; } if (sql_idx) { int i = 0; for (i = 0; i < sql_idx; i++) { if (switch_stristr("update channels", sql[i]) || switch_stristr("delete from channels", sql[i])) { switch_sql_queue_manager_push(sql_manager.qm, sql[i], 1, SWITCH_FALSE); } else { switch_sql_queue_manager_push(sql_manager.qm, sql[i], 0, SWITCH_FALSE); } sql[i] = NULL; } } } static char create_complete_sql[] = "CREATE TABLE complete (\n" " sticky INTEGER,\n" " a1 VARCHAR(128),\n" " a2 VARCHAR(128),\n" " a3 VARCHAR(128),\n" " a4 VARCHAR(128),\n" " a5 VARCHAR(128),\n" " a6 VARCHAR(128),\n" " a7 VARCHAR(128),\n" " a8 VARCHAR(128),\n" " a9 VARCHAR(128),\n" " a10 VARCHAR(128),\n" " hostname VARCHAR(256)\n" ");\n"; static char create_alias_sql[] = "CREATE TABLE aliases (\n" " sticky INTEGER,\n" " alias VARCHAR(128),\n" " command VARCHAR(4096),\n" " hostname VARCHAR(256)\n" ");\n"; static char create_channels_sql[] = "CREATE TABLE channels (\n" " uuid VARCHAR(256),\n" " direction VARCHAR(32),\n" " created VARCHAR(128),\n" " created_epoch INTEGER,\n" " name VARCHAR(1024),\n" " state VARCHAR(64),\n" " cid_name VARCHAR(1024),\n" " cid_num VARCHAR(256),\n" " ip_addr VARCHAR(256),\n" " dest VARCHAR(1024),\n" " application VARCHAR(128),\n" " application_data VARCHAR(4096),\n" " dialplan VARCHAR(128),\n" " context VARCHAR(128),\n" " read_codec VARCHAR(128),\n" " read_rate VARCHAR(32),\n" " read_bit_rate VARCHAR(32),\n" " write_codec VARCHAR(128),\n" " write_rate VARCHAR(32),\n" " write_bit_rate VARCHAR(32),\n" " secure VARCHAR(64),\n" " hostname VARCHAR(256),\n" " presence_id VARCHAR(4096),\n" " presence_data VARCHAR(4096),\n" " accountcode VARCHAR(256),\n" " callstate VARCHAR(64),\n" " callee_name VARCHAR(1024),\n" " callee_num VARCHAR(256),\n" " callee_direction VARCHAR(5),\n" " call_uuid VARCHAR(256),\n" " sent_callee_name VARCHAR(1024),\n" " sent_callee_num VARCHAR(256),\n" " initial_cid_name VARCHAR(1024),\n" " initial_cid_num VARCHAR(256),\n" " initial_ip_addr VARCHAR(256),\n" " initial_dest VARCHAR(1024),\n" " initial_dialplan VARCHAR(128),\n" " initial_context VARCHAR(128)\n" ");\n"; static char create_row_size_limited_channels_sql[] = "CREATE TABLE channels (\n" " uuid VARCHAR(256),\n" " direction VARCHAR(32),\n" " created VARCHAR(128),\n" " created_epoch INTEGER,\n" " name VARCHAR(1024),\n" " state VARCHAR(64),\n" " cid_name VARCHAR(1024),\n" " cid_num VARCHAR(256),\n" " ip_addr VARCHAR(256),\n" " dest VARCHAR(1024),\n" " application VARCHAR(128),\n" " application_data TEXT,\n" " dialplan VARCHAR(128),\n" " context VARCHAR(128),\n" " read_codec VARCHAR(128),\n" " read_rate VARCHAR(32),\n" " read_bit_rate VARCHAR(32),\n" " write_codec VARCHAR(128),\n" " write_rate VARCHAR(32),\n" " write_bit_rate VARCHAR(32),\n" " secure VARCHAR(64),\n" " hostname VARCHAR(256),\n" " presence_id VARCHAR(4096),\n" " presence_data TEXT,\n" " accountcode VARCHAR(256),\n" " callstate VARCHAR(64),\n" " callee_name VARCHAR(1024),\n" " callee_num VARCHAR(256),\n" " callee_direction VARCHAR(5),\n" " call_uuid VARCHAR(256),\n" " sent_callee_name VARCHAR(1024),\n" " sent_callee_num VARCHAR(256),\n" " initial_cid_name VARCHAR(1024),\n" " initial_cid_num VARCHAR(256),\n" " initial_ip_addr VARCHAR(256),\n" " initial_dest VARCHAR(1024),\n" " initial_dialplan VARCHAR(128),\n" " initial_context VARCHAR(128)\n" ");\n"; static char create_calls_sql[] = "CREATE TABLE calls (\n" " call_uuid VARCHAR(255),\n" " call_created VARCHAR(128),\n" " call_created_epoch INTEGER,\n" " caller_uuid VARCHAR(256),\n" " callee_uuid VARCHAR(256),\n" " hostname VARCHAR(256)\n" ");\n"; static char create_interfaces_sql[] = "CREATE TABLE interfaces (\n" " type VARCHAR(128),\n" " name VARCHAR(1024),\n" " description VARCHAR(4096),\n" " ikey VARCHAR(1024),\n" " filename VARCHAR(4096),\n" " syntax VARCHAR(4096),\n" " hostname VARCHAR(256)\n" ");\n"; static char create_tasks_sql[] = "CREATE TABLE tasks (\n" " task_id INTEGER,\n" " task_desc VARCHAR(4096),\n" " task_group VARCHAR(1024),\n" " task_runtime BIGINT,\n" " task_sql_manager INTEGER,\n" " hostname VARCHAR(256)\n" ");\n"; static char create_nat_sql[] = "CREATE TABLE nat (\n" " sticky INTEGER,\n" " port INTEGER,\n" " proto INTEGER,\n" " hostname VARCHAR(256)\n" ");\n"; static char create_registrations_sql[] = "CREATE TABLE registrations (\n" " reg_user VARCHAR(256),\n" " realm VARCHAR(256),\n" " token VARCHAR(256),\n" /* If url is modified please check for code in switch_core_sqldb_start for dependencies for MSSQL" */ " url TEXT,\n" " expires INTEGER,\n" " network_ip VARCHAR(256),\n" " network_port VARCHAR(256),\n" " network_proto VARCHAR(256),\n" " hostname VARCHAR(256),\n" " metadata VARCHAR(256)\n" ");\n"; static char detailed_calls_sql[] = "create view detailed_calls as select " "a.uuid as uuid," "a.direction as direction," "a.created as created," "a.created_epoch as created_epoch," "a.name as name," "a.state as state," "a.cid_name as cid_name," "a.cid_num as cid_num," "a.ip_addr as ip_addr," "a.dest as dest," "a.application as application," "a.application_data as application_data," "a.dialplan as dialplan," "a.context as context," "a.read_codec as read_codec," "a.read_rate as read_rate," "a.read_bit_rate as read_bit_rate," "a.write_codec as write_codec," "a.write_rate as write_rate," "a.write_bit_rate as write_bit_rate," "a.secure as secure," "a.hostname as hostname," "a.presence_id as presence_id," "a.presence_data as presence_data," "a.accountcode as accountcode," "a.callstate as callstate," "a.callee_name as callee_name," "a.callee_num as callee_num," "a.callee_direction as callee_direction," "a.call_uuid as call_uuid," "a.sent_callee_name as sent_callee_name," "a.sent_callee_num as sent_callee_num," "b.uuid as b_uuid," "b.direction as b_direction," "b.created as b_created," "b.created_epoch as b_created_epoch," "b.name as b_name," "b.state as b_state," "b.cid_name as b_cid_name," "b.cid_num as b_cid_num," "b.ip_addr as b_ip_addr," "b.dest as b_dest," "b.application as b_application," "b.application_data as b_application_data," "b.dialplan as b_dialplan," "b.context as b_context," "b.read_codec as b_read_codec," "b.read_rate as b_read_rate," "b.read_bit_rate as b_read_bit_rate," "b.write_codec as b_write_codec," "b.write_rate as b_write_rate," "b.write_bit_rate as b_write_bit_rate," "b.secure as b_secure," "b.hostname as b_hostname," "b.presence_id as b_presence_id," "b.presence_data as b_presence_data," "b.accountcode as b_accountcode," "b.callstate as b_callstate," "b.callee_name as b_callee_name," "b.callee_num as b_callee_num," "b.callee_direction as b_callee_direction," "b.call_uuid as b_call_uuid," "b.sent_callee_name as b_sent_callee_name," "b.sent_callee_num as b_sent_callee_num," "c.call_created_epoch as call_created_epoch " "from channels a " "left join calls c on a.uuid = c.caller_uuid and a.hostname = c.hostname " "left join channels b on b.uuid = c.callee_uuid and b.hostname = c.hostname " "where a.uuid = c.caller_uuid or a.uuid not in (select callee_uuid from calls)"; static char recovery_sql[] = "CREATE TABLE recovery (\n" " runtime_uuid VARCHAR(255),\n" " technology VARCHAR(255),\n" " profile_name VARCHAR(255),\n" " hostname VARCHAR(255),\n" " uuid VARCHAR(255),\n" " metadata text\n" ");\n"; static char basic_calls_sql[] = "create view basic_calls as select " "a.uuid as uuid," "a.direction as direction," "a.created as created," "a.created_epoch as created_epoch," "a.name as name," "a.state as state," "a.cid_name as cid_name," "a.cid_num as cid_num," "a.ip_addr as ip_addr," "a.dest as dest," "a.presence_id as presence_id," "a.presence_data as presence_data," "a.accountcode as accountcode," "a.callstate as callstate," "a.callee_name as callee_name," "a.callee_num as callee_num," "a.callee_direction as callee_direction," "a.call_uuid as call_uuid," "a.hostname as hostname," "a.sent_callee_name as sent_callee_name," "a.sent_callee_num as sent_callee_num," "b.uuid as b_uuid," "b.direction as b_direction," "b.created as b_created," "b.created_epoch as b_created_epoch," "b.name as b_name," "b.state as b_state," "b.cid_name as b_cid_name," "b.cid_num as b_cid_num," "b.ip_addr as b_ip_addr," "b.dest as b_dest," "b.presence_id as b_presence_id," "b.presence_data as b_presence_data," "b.accountcode as b_accountcode," "b.callstate as b_callstate," "b.callee_name as b_callee_name," "b.callee_num as b_callee_num," "b.callee_direction as b_callee_direction," "b.sent_callee_name as b_sent_callee_name," "b.sent_callee_num as b_sent_callee_num," "c.call_created_epoch as call_created_epoch " "from channels a " "left join calls c on a.uuid = c.caller_uuid and a.hostname = c.hostname " "left join channels b on b.uuid = c.callee_uuid and b.hostname = c.hostname " "where a.uuid = c.caller_uuid or a.uuid not in (select callee_uuid from calls)"; SWITCH_DECLARE(void) switch_core_recovery_flush(const char *technology, const char *profile_name) { char *sql = NULL; switch_cache_db_handle_t *dbh; if (switch_core_db_handle(&dbh) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB!\n"); return; } if (zstr(technology)) { if (zstr(profile_name)) { sql = switch_mprintf("delete from recovery"); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "INVALID\n"); } } else { if (zstr(profile_name)) { sql = switch_mprintf("delete from recovery where technology='%q' ", technology); } else { sql = switch_mprintf("delete from recovery where technology='%q' and profile_name='%q'", technology, profile_name); } } if (sql) { switch_cache_db_execute_sql(dbh, sql, NULL); switch_safe_free(sql); } switch_cache_db_release_db_handle(&dbh); } static int recover_callback(void *pArg, int argc, char **argv, char **columnNames) { int *rp = (int *) pArg; switch_xml_t xml; switch_endpoint_interface_t *ep; switch_core_session_t *session; if (argc < 4) { return 0; } if (!(xml = switch_xml_parse_str_dynamic(argv[4], SWITCH_TRUE))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "XML ERROR\n"); return 0; } if (!(ep = switch_loadable_module_get_endpoint_interface(argv[0]))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "EP ERROR\n"); switch_xml_free(xml); return 0; } if (!(session = switch_core_session_request_xml(ep, NULL, xml))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid cdr data, call not recovered\n"); goto end; } if (ep->recover_callback) { switch_caller_extension_t *extension = NULL; switch_channel_t *channel = switch_core_session_get_channel(session); int r = 0; if ((r = ep->recover_callback(session)) > 0) { const char *cbname; switch_channel_set_flag(session->channel, CF_RECOVERING); if (switch_channel_get_partner_uuid(channel)) { switch_channel_set_flag(channel, CF_RECOVERING_BRIDGE); } switch_core_media_recover_session(session); if ((cbname = switch_channel_get_variable(channel, "secondary_recovery_module"))) { switch_core_recover_callback_t recover_callback; if ((recover_callback = switch_core_get_secondary_recover_callback(cbname))) { r = recover_callback(session); } } } if (r > 0) { if (!switch_channel_test_flag(channel, CF_RECOVERING_BRIDGE)) { switch_xml_t callflow, param, x_extension; if ((extension = switch_caller_extension_new(session, "recovery", "recovery")) == 0) { abort(); } if ((callflow = switch_xml_child(xml, "callflow")) && (x_extension = switch_xml_child(callflow, "extension"))) { int recovery_skip_announcement_type_applications = switch_channel_var_true(channel, "recovery_skip_announcement_type_applications"); for (param = switch_xml_child(x_extension, "application"); param; param = param->next) { const char *var = switch_xml_attr_soft(param, "app_name"); const char *val = switch_xml_attr_soft(param, "app_data"); /* skip announcement type apps */ if (!recovery_skip_announcement_type_applications || (strcasecmp(var, "speak") && strcasecmp(var, "playback") && strcasecmp(var, "gentones") && strcasecmp(var, "say"))) { switch_caller_extension_add_application(session, extension, var, val); } } } switch_channel_set_caller_extension(channel, extension); } switch_channel_set_state(channel, CS_INIT); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_NOTICE, "Resurrecting fallen channel %s\n", switch_channel_get_name(channel)); switch_core_session_thread_launch(session); *rp = (*rp) + 1; } } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Endpoint %s has no recovery function\n", argv[0]); } end: UNPROTECT_INTERFACE(ep); switch_xml_free(xml); return 0; } SWITCH_DECLARE(int) switch_core_recovery_recover(const char *technology, const char *profile_name) { char *sql = NULL; char *errmsg = NULL; switch_cache_db_handle_t *dbh; int r = 0; if (!sql_manager.manage) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "DATABASE NOT AVAIALBLE, REVCOVERY NOT POSSIBLE\n"); return 0; } if (switch_core_db_handle(&dbh) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB!\n"); return 0; } if (zstr(technology)) { if (zstr(profile_name)) { sql = switch_mprintf("select technology, profile_name, hostname, uuid, metadata " "from recovery where runtime_uuid!='%q'", switch_core_get_uuid()); } else { sql = switch_mprintf("select technology, profile_name, hostname, uuid, metadata " "from recovery where runtime_uuid!='%q' and profile_name='%q'", switch_core_get_uuid(), profile_name); } } else { if (zstr(profile_name)) { sql = switch_mprintf("select technology, profile_name, hostname, uuid, metadata " "from recovery where technology='%q' and runtime_uuid!='%q'", technology, switch_core_get_uuid()); } else { sql = switch_mprintf("select technology, profile_name, hostname, uuid, metadata " "from recovery where technology='%q' and runtime_uuid!='%q' and profile_name='%q'", technology, switch_core_get_uuid(), profile_name); } } switch_cache_db_execute_sql_callback(dbh, sql, recover_callback, &r, &errmsg); if (errmsg) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SQL ERR: [%s] %s\n", sql, errmsg); switch_safe_free(errmsg); } switch_safe_free(sql); if (zstr(technology)) { if (zstr(profile_name)) { sql = switch_mprintf("delete from recovery where runtime_uuid!='%q'", switch_core_get_uuid()); } else { sql = switch_mprintf("delete from recovery where runtime_uuid!='%q' and profile_name='%q'", switch_core_get_uuid(), profile_name); } } else { if (zstr(profile_name)) { sql = switch_mprintf("delete from recovery where runtime_uuid!='%q' and technology='%q' ", switch_core_get_uuid(), technology); } else { sql = switch_mprintf("delete from recovery where runtime_uuid!='%q' and technology='%q' and profile_name='%q'", switch_core_get_uuid(), technology, profile_name); } } switch_cache_db_execute_sql(dbh, sql, NULL); switch_safe_free(sql); switch_cache_db_release_db_handle(&dbh); return r; } SWITCH_DECLARE(switch_cache_db_handle_type_t) switch_core_dbtype(void) { switch_cache_db_handle_type_t type = SCDB_TYPE_CORE_DB; switch_mutex_lock(sql_manager.ctl_mutex); if (sql_manager.qm && sql_manager.qm->event_db) { type = sql_manager.qm->event_db->type; } switch_mutex_unlock(sql_manager.ctl_mutex); return type; } SWITCH_DECLARE(void) switch_core_sql_exec(const char *sql) { if (!sql_manager.manage) { return; } if (!switch_test_flag((&runtime), SCF_USE_SQL)) { return; } switch_sql_queue_manager_push(sql_manager.qm, sql, 3, SWITCH_TRUE); } SWITCH_DECLARE(void) switch_core_recovery_untrack(switch_core_session_t *session, switch_bool_t force) { char *sql = NULL; switch_channel_t *channel = switch_core_session_get_channel(session); if (!sql_manager.manage) { return; } if (!switch_channel_test_flag(channel, CF_ANSWERED) || switch_channel_get_state(channel) < CS_SOFT_EXECUTE) { return; } if (!switch_channel_test_flag(channel, CF_TRACKABLE)) { return; } if ((switch_channel_test_flag(channel, CF_RECOVERING))) { return; } if (switch_channel_test_flag(channel, CF_TRACKED) || force) { if (force) { sql = switch_mprintf("delete from recovery where uuid='%q'", switch_core_session_get_uuid(session)); } else { sql = switch_mprintf("delete from recovery where runtime_uuid='%q' and uuid='%q'", switch_core_get_uuid(), switch_core_session_get_uuid(session)); } switch_sql_queue_manager_push(sql_manager.qm, sql, 3, SWITCH_FALSE); switch_channel_clear_flag(channel, CF_TRACKED); } } SWITCH_DECLARE(void) switch_core_recovery_track(switch_core_session_t *session) { switch_xml_t cdr = NULL; char *xml_cdr_text = NULL; char *sql = NULL; switch_channel_t *channel = switch_core_session_get_channel(session); const char *profile_name; const char *technology; if (!sql_manager.manage) { return; } if (!switch_channel_test_flag(channel, CF_ANSWERED) || switch_channel_get_state(channel) < CS_SOFT_EXECUTE) { return; } if (switch_channel_test_flag(channel, CF_RECOVERING) || !switch_channel_test_flag(channel, CF_TRACKABLE)) { return; } profile_name = switch_channel_get_variable_dup(channel, "recovery_profile_name", SWITCH_FALSE, -1); technology = session->endpoint_interface->interface_name; if (switch_ivr_generate_xml_cdr(session, &cdr) == SWITCH_STATUS_SUCCESS) { xml_cdr_text = switch_xml_toxml_nolock(cdr, SWITCH_FALSE); switch_xml_free(cdr); } if (xml_cdr_text) { if (switch_channel_test_flag(channel, CF_TRACKED)) { sql = switch_mprintf("update recovery set metadata='%q' where uuid='%q'", xml_cdr_text, switch_core_session_get_uuid(session)); } else { sql = switch_mprintf("insert into recovery (runtime_uuid, technology, profile_name, hostname, uuid, metadata) " "values ('%q','%q','%q','%q','%q','%q')", switch_core_get_uuid(), switch_str_nil(technology), switch_str_nil(profile_name), switch_core_get_switchname(), switch_core_session_get_uuid(session), xml_cdr_text); } switch_sql_queue_manager_push(sql_manager.qm, sql, 2, SWITCH_FALSE); switch_safe_free(xml_cdr_text); switch_channel_set_flag(channel, CF_TRACKED); } } SWITCH_DECLARE(switch_status_t) switch_core_add_registration(const char *user, const char *realm, const char *token, const char *url, uint32_t expires, const char *network_ip, const char *network_port, const char *network_proto, const char *metadata) { char *sql; if (!switch_test_flag((&runtime), SCF_USE_SQL)) { return SWITCH_STATUS_FALSE; } if (runtime.multiple_registrations) { sql = switch_mprintf("delete from registrations where hostname='%q' and (url='%q' or token='%q')", switch_core_get_switchname(), url, switch_str_nil(token)); } else { sql = switch_mprintf("delete from registrations where reg_user='%q' and realm='%q' and hostname='%q'", user, realm, switch_core_get_switchname()); } switch_sql_queue_manager_push(sql_manager.qm, sql, 0, SWITCH_FALSE); if ( !zstr(metadata) ) { sql = switch_mprintf("insert into registrations (reg_user,realm,token,url,expires,network_ip,network_port,network_proto,hostname,metadata) " "values ('%q','%q','%q','%q',%ld,'%q','%q','%q','%q','%q')", switch_str_nil(user), switch_str_nil(realm), switch_str_nil(token), switch_str_nil(url), expires, switch_str_nil(network_ip), switch_str_nil(network_port), switch_str_nil(network_proto), switch_core_get_switchname(), metadata ); } else { sql = switch_mprintf("insert into registrations (reg_user,realm,token,url,expires,network_ip,network_port,network_proto,hostname) " "values ('%q','%q','%q','%q',%ld,'%q','%q','%q','%q')", switch_str_nil(user), switch_str_nil(realm), switch_str_nil(token), switch_str_nil(url), expires, switch_str_nil(network_ip), switch_str_nil(network_port), switch_str_nil(network_proto), switch_core_get_switchname() ); } switch_sql_queue_manager_push(sql_manager.qm, sql, 0, SWITCH_FALSE); return SWITCH_STATUS_SUCCESS; } SWITCH_DECLARE(switch_status_t) switch_core_del_registration(const char *user, const char *realm, const char *token) { char *sql; if (!switch_test_flag((&runtime), SCF_USE_SQL)) { return SWITCH_STATUS_FALSE; } if (!zstr(token) && runtime.multiple_registrations) { sql = switch_mprintf("delete from registrations where reg_user='%q' and realm='%q' and hostname='%q' and token='%q'", user, realm, switch_core_get_switchname(), token); } else { sql = switch_mprintf("delete from registrations where reg_user='%q' and realm='%q' and hostname='%q'", user, realm, switch_core_get_switchname()); } switch_sql_queue_manager_push(sql_manager.qm, sql, 0, SWITCH_FALSE); return SWITCH_STATUS_SUCCESS; } SWITCH_DECLARE(switch_status_t) switch_core_expire_registration(int force) { char *sql; time_t now; if (!switch_test_flag((&runtime), SCF_USE_SQL)) { return SWITCH_STATUS_FALSE; } now = switch_epoch_time_now(NULL); if (force) { sql = switch_mprintf("delete from registrations where hostname='%q'", switch_core_get_switchname()); } else { sql = switch_mprintf("delete from registrations where expires > 0 and expires <= %ld and hostname='%q'", now, switch_core_get_switchname()); } switch_sql_queue_manager_push(sql_manager.qm, sql, 0, SWITCH_FALSE); return SWITCH_STATUS_SUCCESS; } switch_status_t switch_core_sqldb_start(switch_memory_pool_t *pool, switch_bool_t manage) { switch_threadattr_t *thd_attr; sql_manager.memory_pool = pool; sql_manager.manage = manage; switch_mutex_init(&sql_manager.dbh_mutex, SWITCH_MUTEX_NESTED, sql_manager.memory_pool); switch_mutex_init(&sql_manager.ctl_mutex, SWITCH_MUTEX_NESTED, sql_manager.memory_pool); if (!sql_manager.manage) goto skip; top: /* Activate SQL database */ if (switch_core_db_handle(&sql_manager.dbh) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB!\n"); if (switch_test_flag((&runtime), SCF_CORE_NON_SQLITE_DB_REQ)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failure! ODBC IS REQUIRED!\n"); return SWITCH_STATUS_FALSE; } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "CORE DATABASE INITIALIZATION FAILURE! CHECK `core-db-dsn`!\n"); switch_clear_flag((&runtime), SCF_USE_SQL); return SWITCH_STATUS_FALSE; } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Opening DB\n"); switch (sql_manager.dbh->type) { case SCDB_TYPE_DATABASE_INTERFACE: case SCDB_TYPE_ODBC: if (switch_test_flag((&runtime), SCF_CLEAR_SQL)) { char sql[512] = ""; char *tables[] = { "channels", "calls", "tasks", NULL }; int i; const char *hostname = switch_core_get_switchname(); for (i = 0; tables[i]; i++) { switch_snprintfv(sql, sizeof(sql), "delete from %q where hostname='%q'", tables[i], hostname); switch_cache_db_execute_sql(sql_manager.dbh, sql, NULL); } } break; case SCDB_TYPE_CORE_DB: { switch_cache_db_execute_sql(sql_manager.dbh, "drop table channels", NULL); switch_cache_db_execute_sql(sql_manager.dbh, "drop table calls", NULL); switch_cache_db_execute_sql(sql_manager.dbh, "drop view detailed_calls", NULL); switch_cache_db_execute_sql(sql_manager.dbh, "drop view basic_calls", NULL); switch_cache_db_execute_sql(sql_manager.dbh, "drop table interfaces", NULL); switch_cache_db_execute_sql(sql_manager.dbh, "drop table tasks", NULL); switch_cache_db_execute_sql(sql_manager.dbh, "PRAGMA synchronous=OFF;", NULL); switch_cache_db_execute_sql(sql_manager.dbh, "PRAGMA count_changes=OFF;", NULL); switch_cache_db_execute_sql(sql_manager.dbh, "PRAGMA default_cache_size=8000", NULL); switch_cache_db_execute_sql(sql_manager.dbh, "PRAGMA temp_store=MEMORY;", NULL); switch_cache_db_execute_sql(sql_manager.dbh, "PRAGMA journal_mode=OFF;", NULL); } break; } switch_cache_db_test_reactive(sql_manager.dbh, "select hostname from aliases", "DROP TABLE aliases", create_alias_sql); switch_cache_db_test_reactive(sql_manager.dbh, "select hostname from complete", "DROP TABLE complete", create_complete_sql); switch_cache_db_test_reactive(sql_manager.dbh, "select hostname from nat", "DROP TABLE nat", create_nat_sql); switch_cache_db_test_reactive(sql_manager.dbh, "delete from registrations where reg_user=''", "DROP TABLE registrations", create_registrations_sql); switch_cache_db_test_reactive(sql_manager.dbh, "select metadata from registrations", NULL, "ALTER TABLE registrations ADD COLUMN metadata VARCHAR(256)"); switch_cache_db_test_reactive(sql_manager.dbh, "select hostname from recovery", "DROP TABLE recovery", recovery_sql); switch_cache_db_create_schema(sql_manager.dbh, "create index recovery1 on recovery(technology)", NULL); switch_cache_db_create_schema(sql_manager.dbh, "create index recovery2 on recovery(profile_name)", NULL); switch_cache_db_create_schema(sql_manager.dbh, "create index recovery3 on recovery(uuid)", NULL); switch_cache_db_create_schema(sql_manager.dbh, "create index recovery4 on recovery(runtime_uuid)", NULL); switch (sql_manager.dbh->type) { case SCDB_TYPE_DATABASE_INTERFACE: case SCDB_TYPE_ODBC: { char *err; int result = 0; switch_cache_db_test_reactive_ex(sql_manager.dbh, "select call_uuid, read_bit_rate, sent_callee_name, initial_cid_name, initial_cid_num, initial_ip_addr, initial_dest, initial_dialplan, initial_context, accountcode from channels", "DROP TABLE channels", create_channels_sql, create_row_size_limited_channels_sql); switch_cache_db_test_reactive(sql_manager.dbh, "select call_uuid from calls", "DROP TABLE calls", create_calls_sql); switch_cache_db_test_reactive(sql_manager.dbh, "select * from basic_calls where sent_callee_name=''", "DROP VIEW basic_calls", basic_calls_sql); switch_cache_db_test_reactive(sql_manager.dbh, "select * from detailed_calls where sent_callee_name=''", "DROP VIEW detailed_calls", detailed_calls_sql); if (runtime.odbc_dbtype == DBTYPE_DEFAULT) { switch_cache_db_test_reactive(sql_manager.dbh, "delete from registrations where reg_user=''", "DROP TABLE registrations", create_registrations_sql); } else { char *tmp = switch_string_replace(create_registrations_sql, "url TEXT", "url VARCHAR(max)"); switch_cache_db_test_reactive(sql_manager.dbh, "delete from registrations where reg_user=''", "DROP TABLE registrations", tmp); free(tmp); } switch_cache_db_test_reactive(sql_manager.dbh, "select ikey from interfaces", "DROP TABLE interfaces", create_interfaces_sql); switch_cache_db_test_reactive(sql_manager.dbh, "select task_id, task_desc, task_group, task_runtime, task_sql_manager, hostname from tasks", "DROP TABLE tasks", create_tasks_sql); switch(sql_manager.dbh->type) { case SCDB_TYPE_CORE_DB: { switch_cache_db_execute_sql_real(sql_manager.dbh, "BEGIN EXCLUSIVE", &err); } break; case SCDB_TYPE_ODBC: { switch_odbc_status_t result; if ((result = switch_odbc_SQLSetAutoCommitAttr(sql_manager.dbh->native_handle.odbc_dbh, 0)) != SWITCH_ODBC_SUCCESS) { char tmp[100]; switch_snprintfv(tmp, sizeof(tmp), "%q-%i", "Unable to Set AutoCommit Off", result); err = strdup(tmp); } } break; case SCDB_TYPE_DATABASE_INTERFACE: { switch_database_interface_t *database_interface = sql_manager.dbh->native_handle.database_interface_dbh->connection_options.database_interface; switch_status_t result; if ((result = database_interface->sql_set_auto_commit_attr(sql_manager.dbh->native_handle.database_interface_dbh, 0)) != SWITCH_STATUS_SUCCESS) { char tmp[100]; switch_snprintfv(tmp, sizeof(tmp), "%q-%i", "Unable to Set AutoCommit Off", result); err = strdup(tmp); } } break; } switch_cache_db_execute_sql(sql_manager.dbh, "delete from channels where hostname=''", &err); if (!err) { switch_cache_db_execute_sql(sql_manager.dbh, "delete from channels where hostname=''", &err); switch(sql_manager.dbh->type) { case SCDB_TYPE_CORE_DB: { switch_cache_db_execute_sql_real(sql_manager.dbh, "COMMIT", &err); } break; case SCDB_TYPE_ODBC: { if (switch_odbc_SQLEndTran(sql_manager.dbh->native_handle.odbc_dbh, 1) != SWITCH_ODBC_SUCCESS || switch_odbc_SQLSetAutoCommitAttr(sql_manager.dbh->native_handle.odbc_dbh, 1) != SWITCH_ODBC_SUCCESS) { char tmp[100]; switch_snprintfv(tmp, sizeof(tmp), "%q-%i", "Unable to commit transaction.", result); err = strdup(tmp); } } break; case SCDB_TYPE_DATABASE_INTERFACE: { switch_database_interface_t *database_interface = sql_manager.dbh->native_handle.database_interface_dbh->connection_options.database_interface; switch_status_t result; if ((result = database_interface->commit(sql_manager.dbh->native_handle.database_interface_dbh)) != SWITCH_STATUS_SUCCESS) { char tmp[100]; switch_snprintfv(tmp, sizeof(tmp), "%q-%i", "Unable to commit transaction", result); err = strdup(tmp); } } break; } } if (err) { //runtime.odbc_dsn = NULL; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Database Error [%s]\n", err); //switch_cache_db_release_db_handle(&sql_manager.dbh); if (switch_stristr("read-only", err)) { switch_safe_free(err); } else { switch_safe_free(err); goto top; } } } break; case SCDB_TYPE_CORE_DB: { switch_cache_db_execute_sql(sql_manager.dbh, create_channels_sql, NULL); switch_cache_db_execute_sql(sql_manager.dbh, create_calls_sql, NULL); switch_cache_db_execute_sql(sql_manager.dbh, create_interfaces_sql, NULL); switch_cache_db_execute_sql(sql_manager.dbh, create_tasks_sql, NULL); switch_cache_db_execute_sql(sql_manager.dbh, detailed_calls_sql, NULL); switch_cache_db_execute_sql(sql_manager.dbh, basic_calls_sql, NULL); if (sql_manager.dbh->native_handle.core_db_dbh->in_memory == SWITCH_TRUE) { switch_set_flag(sql_manager.dbh, CDF_NONEXPIRING); } } break; } if (switch_test_flag((&runtime), SCF_CLEAR_SQL)) { char sql[512] = ""; char *tables[] = { "complete", "aliases", "nat", NULL }; int i; const char *hostname = switch_core_get_hostname(); for (i = 0; tables[i]; i++) { switch_snprintfv(sql, sizeof(sql), "delete from %q where sticky=0 and hostname='%q'", tables[i], hostname); switch_cache_db_execute_sql(sql_manager.dbh, sql, NULL); } switch_snprintfv(sql, sizeof(sql), "delete from interfaces where hostname='%q'", hostname); switch_cache_db_execute_sql(sql_manager.dbh, sql, NULL); } switch_cache_db_create_schema(sql_manager.dbh, "create index alias1 on aliases (alias)", NULL); switch_cache_db_create_schema(sql_manager.dbh, "create index tasks1 on tasks (hostname,task_id)", NULL); switch_cache_db_create_schema(sql_manager.dbh, "create index complete1 on complete (a1,hostname)", NULL); switch_cache_db_create_schema(sql_manager.dbh, "create index complete2 on complete (a2,hostname)", NULL); switch_cache_db_create_schema(sql_manager.dbh, "create index complete3 on complete (a3,hostname)", NULL); switch_cache_db_create_schema(sql_manager.dbh, "create index complete4 on complete (a4,hostname)", NULL); switch_cache_db_create_schema(sql_manager.dbh, "create index complete5 on complete (a5,hostname)", NULL); switch_cache_db_create_schema(sql_manager.dbh, "create index complete6 on complete (a6,hostname)", NULL); switch_cache_db_create_schema(sql_manager.dbh, "create index complete7 on complete (a7,hostname)", NULL); switch_cache_db_create_schema(sql_manager.dbh, "create index complete8 on complete (a8,hostname)", NULL); switch_cache_db_create_schema(sql_manager.dbh, "create index complete9 on complete (a9,hostname)", NULL); switch_cache_db_create_schema(sql_manager.dbh, "create index complete10 on complete (a10,hostname)", NULL); switch_cache_db_create_schema(sql_manager.dbh, "create index complete11 on complete (a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,hostname)", NULL); switch_cache_db_create_schema(sql_manager.dbh, "create index nat_map_port_proto on nat (port,proto,hostname)", NULL); switch_cache_db_create_schema(sql_manager.dbh, "create index chidx1 on channels (hostname)", NULL); switch_cache_db_create_schema(sql_manager.dbh, "create index uuindex on channels (uuid, hostname)", NULL); switch_cache_db_create_schema(sql_manager.dbh, "create index uuindex2 on channels (call_uuid)", NULL); switch_cache_db_create_schema(sql_manager.dbh, "create index callsidx1 on calls (hostname)", NULL); switch_cache_db_create_schema(sql_manager.dbh, "create index eruuindex on calls (caller_uuid, hostname)", NULL); switch_cache_db_create_schema(sql_manager.dbh, "create index eeuuindex on calls (callee_uuid)", NULL); switch_cache_db_create_schema(sql_manager.dbh, "create index eeuuindex2 on calls (call_uuid)", NULL); switch_cache_db_create_schema(sql_manager.dbh, "create index regindex1 on registrations (reg_user,realm,hostname)", NULL); skip: if (sql_manager.manage) { /* Initiate switch_sql_queue_manager */ switch_threadattr_create(&thd_attr, sql_manager.memory_pool); switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE); switch_threadattr_priority_set(thd_attr, SWITCH_PRI_REALTIME); switch_core_sqldb_start_thread(); switch_thread_create(&sql_manager.db_thread, thd_attr, switch_core_sql_db_thread, NULL, sql_manager.memory_pool); /* switch_sql_queue_manager initiated, now we can bind to core_event_handler */ #ifdef SWITCH_SQL_BIND_EVERY_EVENT switch_event_bind("core_db", SWITCH_EVENT_ALL, SWITCH_EVENT_SUBCLASS_ANY, core_event_handler, NULL); #else switch_event_bind("core_db", SWITCH_EVENT_ADD_SCHEDULE, SWITCH_EVENT_SUBCLASS_ANY, core_event_handler, NULL); switch_event_bind("core_db", SWITCH_EVENT_DEL_SCHEDULE, SWITCH_EVENT_SUBCLASS_ANY, core_event_handler, NULL); switch_event_bind("core_db", SWITCH_EVENT_EXE_SCHEDULE, SWITCH_EVENT_SUBCLASS_ANY, core_event_handler, NULL); switch_event_bind("core_db", SWITCH_EVENT_RE_SCHEDULE, SWITCH_EVENT_SUBCLASS_ANY, core_event_handler, NULL); switch_event_bind("core_db", SWITCH_EVENT_CHANNEL_DESTROY, SWITCH_EVENT_SUBCLASS_ANY, core_event_handler, NULL); switch_event_bind("core_db", SWITCH_EVENT_CHANNEL_UUID, SWITCH_EVENT_SUBCLASS_ANY, core_event_handler, NULL); switch_event_bind("core_db", SWITCH_EVENT_CHANNEL_CREATE, SWITCH_EVENT_SUBCLASS_ANY, core_event_handler, NULL); switch_event_bind("core_db", SWITCH_EVENT_CHANNEL_ANSWER, SWITCH_EVENT_SUBCLASS_ANY, core_event_handler, NULL); switch_event_bind("core_db", SWITCH_EVENT_CHANNEL_PROGRESS_MEDIA, SWITCH_EVENT_SUBCLASS_ANY, core_event_handler, NULL); switch_event_bind("core_db", SWITCH_EVENT_CHANNEL_HOLD, SWITCH_EVENT_SUBCLASS_ANY, core_event_handler, NULL); switch_event_bind("core_db", SWITCH_EVENT_CHANNEL_UNHOLD, SWITCH_EVENT_SUBCLASS_ANY, core_event_handler, NULL); switch_event_bind("core_db", SWITCH_EVENT_CHANNEL_EXECUTE, SWITCH_EVENT_SUBCLASS_ANY, core_event_handler, NULL); switch_event_bind("core_db", SWITCH_EVENT_CHANNEL_ORIGINATE, SWITCH_EVENT_SUBCLASS_ANY, core_event_handler, NULL); switch_event_bind("core_db", SWITCH_EVENT_CALL_UPDATE, SWITCH_EVENT_SUBCLASS_ANY, core_event_handler, NULL); switch_event_bind("core_db", SWITCH_EVENT_CHANNEL_CALLSTATE, SWITCH_EVENT_SUBCLASS_ANY, core_event_handler, NULL); switch_event_bind("core_db", SWITCH_EVENT_CHANNEL_STATE, SWITCH_EVENT_SUBCLASS_ANY, core_event_handler, NULL); switch_event_bind("core_db", SWITCH_EVENT_CHANNEL_BRIDGE, SWITCH_EVENT_SUBCLASS_ANY, core_event_handler, NULL); switch_event_bind("core_db", SWITCH_EVENT_CHANNEL_UNBRIDGE, SWITCH_EVENT_SUBCLASS_ANY, core_event_handler, NULL); switch_event_bind("core_db", SWITCH_EVENT_SHUTDOWN, SWITCH_EVENT_SUBCLASS_ANY, core_event_handler, NULL); switch_event_bind("core_db", SWITCH_EVENT_LOG, SWITCH_EVENT_SUBCLASS_ANY, core_event_handler, NULL); switch_event_bind("core_db", SWITCH_EVENT_MODULE_LOAD, SWITCH_EVENT_SUBCLASS_ANY, core_event_handler, NULL); switch_event_bind("core_db", SWITCH_EVENT_MODULE_UNLOAD, SWITCH_EVENT_SUBCLASS_ANY, core_event_handler, NULL); switch_event_bind("core_db", SWITCH_EVENT_CALL_SECURE, SWITCH_EVENT_SUBCLASS_ANY, core_event_handler, NULL); switch_event_bind("core_db", SWITCH_EVENT_NAT, SWITCH_EVENT_SUBCLASS_ANY, core_event_handler, NULL); switch_event_bind("core_db", SWITCH_EVENT_CODEC, SWITCH_EVENT_SUBCLASS_ANY, core_event_handler, NULL); #endif } switch_cache_db_release_db_handle(&sql_manager.dbh); return SWITCH_STATUS_SUCCESS; } SWITCH_DECLARE(void) switch_core_sqldb_pause(void) { if (sql_manager.paused) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "SQL is already paused.\n"); } sql_manager.paused = 1; } SWITCH_DECLARE(void) switch_core_sqldb_resume(void) { if (!sql_manager.paused) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "SQL is already running.\n"); } sql_manager.paused = 0; } static void switch_core_sqldb_stop_thread(void) { switch_mutex_lock(sql_manager.ctl_mutex); if (sql_manager.manage) { if (sql_manager.qm) { switch_sql_queue_manager_destroy(&sql_manager.qm); } } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SQL is not enabled\n"); } switch_mutex_unlock(sql_manager.ctl_mutex); } static void switch_core_sqldb_start_thread(void) { switch_mutex_lock(sql_manager.ctl_mutex); if (sql_manager.manage) { if (!sql_manager.qm) { char *dbname = runtime.odbc_dsn; if (zstr(dbname)) { dbname = runtime.dbname; if (zstr(dbname)) { dbname = "core"; } } switch_sql_queue_manager_init_name("CORE", &sql_manager.qm, 4, dbname, SWITCH_MAX_TRANS, runtime.core_db_pre_trans_execute, runtime.core_db_post_trans_execute, runtime.core_db_inner_pre_trans_execute, runtime.core_db_inner_post_trans_execute); } switch_sql_queue_manager_start(sql_manager.qm); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SQL is not enabled\n"); } switch_mutex_unlock(sql_manager.ctl_mutex); } void switch_core_sqldb_stop(void) { switch_status_t st; switch_event_unbind_callback(core_event_handler); if (sql_manager.db_thread && sql_manager.db_thread_running) { sql_manager.db_thread_running = -1; switch_thread_join(&st, sql_manager.db_thread); } switch_core_sqldb_stop_thread(); switch_cache_db_flush_handles(); sql_close(0); } SWITCH_DECLARE(void) switch_cache_db_status(switch_stream_handle_t *stream) { /* return some status info suitable for the cli */ switch_cache_db_handle_t *dbh = NULL; switch_bool_t locked = SWITCH_FALSE; time_t now = switch_epoch_time_now(NULL); char cleankey_str[CACHE_DB_LEN]; char *pos1 = NULL; char *pos2 = NULL; int count = 0, used = 0; switch_mutex_lock(sql_manager.dbh_mutex); for (dbh = sql_manager.handle_pool; dbh; dbh = dbh->next) { char *needles[3]; time_t diff = 0; int i = 0; needles[0] = "pass=\""; needles[1] = "password="; needles[2] = "password='"; diff = now - dbh->last_used; if (switch_mutex_trylock(dbh->mutex) == SWITCH_STATUS_SUCCESS) { switch_mutex_unlock(dbh->mutex); locked = SWITCH_FALSE; } else { locked = SWITCH_TRUE; } /* sanitize password */ memset(cleankey_str, 0, sizeof(cleankey_str)); for (i = 0; i < 3; i++) { if((pos1 = strstr(dbh->name, needles[i]))) { pos1 += strlen(needles[i]); if (!(pos2 = strstr(pos1, "\""))) { if (!(pos2 = strstr(pos1, "'"))) { if (!(pos2 = strstr(pos1, " "))) { pos2 = pos1 + strlen(pos1); } } } strncpy(cleankey_str, dbh->name, pos1 - dbh->name); strcpy(&cleankey_str[pos1 - dbh->name], pos2); break; } } if (i == 3) { snprintf(cleankey_str, sizeof(cleankey_str), "%s", dbh->name); } count++; if (dbh->use_count) { used++; } stream->write_function(stream, "%s\n\tType: %s\n\tLast used: %d\n\tTotal used: %ld\n\tFlags: %s, %s(%d)%s\n" "\tCreator: %s\n\tLast User: %s\n", cleankey_str, switch_cache_db_type_name(dbh->type), diff, dbh->total_used_count, locked ? "Locked" : "Unlocked", dbh->use_count ? "Attached" : "Detached", dbh->use_count, switch_test_flag(dbh, CDF_NONEXPIRING) ? ", Non-expiring" : "", dbh->creator, dbh->last_user); } stream->write_function(stream, "%d total. %d in use.\n", count, used); switch_mutex_unlock(sql_manager.dbh_mutex); } SWITCH_DECLARE(char*)switch_sql_concat(void) { if(runtime.odbc_dbtype == DBTYPE_MSSQL) return "+"; return "||"; } /* For Emacs: * Local Variables: * mode:c * indent-tabs-mode:t * tab-width:4 * c-basic-offset:4 * End: * For VIM: * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: */