diff options
-rw-r--r-- | CHANGES | 18 | ||||
-rw-r--r-- | configs/samples/res_odbc.conf.sample | 5 | ||||
-rw-r--r-- | include/asterisk/codec.h | 2 | ||||
-rw-r--r-- | main/codec.c | 10 | ||||
-rw-r--r-- | main/codec_builtin.c | 6 | ||||
-rw-r--r-- | res/ari/resource_channels.c | 88 | ||||
-rw-r--r-- | res/ari/resource_channels.h | 6 | ||||
-rw-r--r-- | res/res_ari_channels.c | 21 | ||||
-rw-r--r-- | res/res_ari_recordings.c | 1 | ||||
-rw-r--r-- | res/res_odbc.c | 232 | ||||
-rw-r--r-- | res/res_srtp.c | 8 | ||||
-rw-r--r-- | rest-api/api-docs/channels.json | 24 |
12 files changed, 357 insertions, 64 deletions
@@ -381,6 +381,12 @@ res_hep valid value using the specified 'uuid_type', the module may fallback to a more readily available source for the correlation UUID. +res_odbc +------------------ + * A new option has been added, 'max_connections', which sets the maximum number + of concurrent connections to the database. This option defaults to 1 which + returns the behavior to that of Asterisk 13.7 and prior. + app_confbridge ------------------ * Added a bridge profile option called regcontext that allows you to @@ -389,6 +395,18 @@ app_confbridge server installations via alternate means (DUNDI for example). By default this feature is not used. +Codecs +------------------ + * Added the associated format name to 'core show codecs'. + +res_ari_channels +------------------ + * Added 'formats' to channel create/originate to allow setting the allowed + formats for a channel when no originator channel is available. Especially + useful for Local channel creation where no other format information is + available. 'core show codecs' can now be used to look up suitable format + names. + ------------------------------------------------------------------------------ --- Functionality changes from Asterisk 13.8.0 to Asterisk 13.9.0 ------------ ------------------------------------------------------------------------------ diff --git a/configs/samples/res_odbc.conf.sample b/configs/samples/res_odbc.conf.sample index 66659ae42..a21e96d07 100644 --- a/configs/samples/res_odbc.conf.sample +++ b/configs/samples/res_odbc.conf.sample @@ -51,6 +51,11 @@ pre-connect => yes ; that we should attempt? ;limit => 5 ; +; The maximum number of connections to have open at any given time. +; This defaults to 1 and it is highly recommended to only set this higher +; if using a version of UnixODBC greater than 2.3.1. +;max_connections => 20 +; ; When the channel is destroyed, should any uncommitted open transactions ; automatically be committed? ;forcecommit => no diff --git a/include/asterisk/codec.h b/include/asterisk/codec.h index 3873324b1..4ea94f9bb 100644 --- a/include/asterisk/codec.h +++ b/include/asterisk/codec.h @@ -77,6 +77,8 @@ struct ast_codec { unsigned int smooth; /*! \brief The module that registered this codec */ struct ast_module *mod; + /*! \brief A format name for a default sane format using this codec */ + const char *format_name; }; /*! diff --git a/main/codec.c b/main/codec.c index 9c4169a1c..a76264503 100644 --- a/main/codec.c +++ b/main/codec.c @@ -135,8 +135,8 @@ static char *show_codecs(struct ast_cli_entry *e, int cmd, struct ast_cli_args * "\tIt does not indicate anything about your configuration.\n"); } - ast_cli(a->fd, "%8s %5s %8s %s\n","ID","TYPE","NAME","DESCRIPTION"); - ast_cli(a->fd, "-----------------------------------------------------------------------------------\n"); + ast_cli(a->fd, "%8s %-5s %-12s %-16s %s\n","ID","TYPE","NAME","FORMAT","DESCRIPTION"); + ast_cli(a->fd, "------------------------------------------------------------------------------------------------\n"); ao2_rdlock(codecs); i = ao2_iterator_init(codecs, AO2_ITERATOR_DONTLOCK); @@ -164,10 +164,11 @@ static char *show_codecs(struct ast_cli_entry *e, int cmd, struct ast_cli_args * } } - ast_cli(a->fd, "%8u %5s %8s (%s)\n", + ast_cli(a->fd, "%8u %-5s %-12s %-16s (%s)\n", codec->id, ast_codec_media_type2str(codec->type), codec->name, + S_OR(codec->format_name, "no cached format"), codec->description); } @@ -216,7 +217,8 @@ static char *show_codec(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a return CLI_SUCCESS; } - ast_cli(a->fd, "%11u %s\n", (unsigned int) codec->id, codec->description); + ast_cli(a->fd, "%11u %s (%s)\n", (unsigned int) codec->id, codec->description, + S_OR(codec->format_name, "no format")); ao2_ref(codec, -1); diff --git a/main/codec_builtin.c b/main/codec_builtin.c index f86a8dfc8..fdaa01825 100644 --- a/main/codec_builtin.c +++ b/main/codec_builtin.c @@ -774,6 +774,7 @@ static struct ast_codec t140 = { int __res_ ## __LINE__ = 0; \ struct ast_format *__fmt_ ## __LINE__; \ struct ast_codec *__codec_ ## __LINE__; \ + codec.format_name = (codec).name; \ res |= __ast_codec_register(&(codec), NULL); \ __codec_ ## __LINE__ = ast_codec_get((codec).name, (codec).type, (codec).sample_rate); \ __fmt_ ## __LINE__ = __codec_ ## __LINE__ ? ast_format_create(__codec_ ## __LINE__) : NULL; \ @@ -783,14 +784,15 @@ static struct ast_codec t140 = { __res_ ## __LINE__; \ }) -#define CODEC_REGISTER_AND_CACHE_NAMED(format_name, codec) \ +#define CODEC_REGISTER_AND_CACHE_NAMED(fmt_name, codec) \ ({ \ int __res_ ## __LINE__ = 0; \ struct ast_format *__fmt_ ## __LINE__; \ struct ast_codec *__codec_ ## __LINE__; \ + codec.format_name = fmt_name; \ res |= __ast_codec_register(&(codec), NULL); \ __codec_ ## __LINE__ = ast_codec_get((codec).name, (codec).type, (codec).sample_rate); \ - __fmt_ ## __LINE__ = ast_format_create_named((format_name), __codec_ ## __LINE__); \ + __fmt_ ## __LINE__ = ast_format_create_named((fmt_name), __codec_ ## __LINE__); \ res |= ast_format_cache_set(__fmt_ ## __LINE__); \ ao2_ref(__fmt_ ## __LINE__, -1); \ ao2_ref(__codec_ ## __LINE__, -1); \ diff --git a/res/ari/resource_channels.c b/res/ari/resource_channels.c index 0f18b2dc1..88a329598 100644 --- a/res/ari/resource_channels.c +++ b/res/ari/resource_channels.c @@ -916,6 +916,7 @@ static void ari_channels_handle_originate_with_id(const char *args_endpoint, const char *args_channel_id, const char *args_other_channel_id, const char *args_originator, + const char *args_formats, struct ast_ari_response *response) { char *dialtech; @@ -934,6 +935,7 @@ static void ari_channels_handle_originate_with_id(const char *args_endpoint, }; struct ari_origination *origination; pthread_t thread; + struct ast_format_cap *format_cap = NULL; if ((assignedids.uniqueid && AST_MAX_PUBLIC_UNIQUEID < strlen(assignedids.uniqueid)) || (assignedids.uniqueid2 && AST_MAX_PUBLIC_UNIQUEID < strlen(assignedids.uniqueid2))) { @@ -948,6 +950,12 @@ static void ari_channels_handle_originate_with_id(const char *args_endpoint, return; } + if (!ast_strlen_zero(args_originator) && !ast_strlen_zero(args_formats)) { + ast_ari_response_error(response, 400, "Bad Request", + "Originator and formats can't both be specified"); + return; + } + dialtech = ast_strdupa(args_endpoint); if ((stuff = strchr(dialtech, '/'))) { *stuff++ = '\0'; @@ -1070,7 +1078,41 @@ static void ari_channels_handle_originate_with_id(const char *args_endpoint, } } - if (ast_dial_prerun(dial, other, NULL)) { + if (!ast_strlen_zero(args_formats)) { + char *format_name; + char *formats_copy = ast_strdupa(args_formats); + + if (!(format_cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) { + ast_ari_response_alloc_failed(response); + ast_dial_destroy(dial); + ast_free(origination); + ast_channel_cleanup(other); + return; + } + + while ((format_name = ast_strip(strsep(&formats_copy, ",")))) { + struct ast_format *fmt = ast_format_cache_get(format_name); + + if (!fmt || ast_format_cap_append(format_cap, fmt, 0)) { + if (!fmt) { + ast_ari_response_error( + response, 400, "Bad Request", + "Provided format (%s) was not found", format_name); + } else { + ast_ari_response_alloc_failed(response); + } + ast_dial_destroy(dial); + ast_free(origination); + ast_channel_cleanup(other); + ao2_ref(format_cap, -1); + ao2_cleanup(fmt); + return; + } + ao2_ref(fmt, -1); + } + } + + if (ast_dial_prerun(dial, other, format_cap)) { ast_ari_response_alloc_failed(response); ast_dial_destroy(dial); ast_free(origination); @@ -1079,6 +1121,7 @@ static void ari_channels_handle_originate_with_id(const char *args_endpoint, } ast_channel_cleanup(other); + ao2_cleanup(format_cap); chan = ast_dial_get_channel(dial, 0); if (!chan) { @@ -1219,6 +1262,7 @@ void ast_ari_channels_originate_with_id(struct ast_variable *headers, args->channel_id, args->other_channel_id, args->originator, + args->formats, response); ast_variables_destroy(variables); } @@ -1255,6 +1299,7 @@ void ast_ari_channels_originate(struct ast_variable *headers, args->channel_id, args->other_channel_id, args->originator, + args->formats, response); ast_variables_destroy(variables); } @@ -1589,6 +1634,12 @@ void ast_ari_channels_create(struct ast_variable *headers, return; } + if (!ast_strlen_zero(args->originator) && !ast_strlen_zero(args->formats)) { + ast_ari_response_error(response, 400, "Bad Request", + "Originator and formats can't both be specified"); + return; + } + chan_data->stasis_stuff = ast_str_create(32); if (!chan_data->stasis_stuff) { ast_ari_response_alloc_failed(response); @@ -1610,8 +1661,41 @@ void ast_ari_channels_create(struct ast_variable *headers, originator = ast_channel_get_by_name(args->originator); if (originator) { request_cap = ao2_bump(ast_channel_nativeformats(originator)); + } else if (!ast_strlen_zero(args->formats)) { + char *format_name; + char *formats_copy = ast_strdupa(args->formats); + + if (!(request_cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) { + ast_ari_response_alloc_failed(response); + chan_data_destroy(chan_data); + return; + } + + while ((format_name = ast_strip(strsep(&formats_copy, ",")))) { + struct ast_format *fmt = ast_format_cache_get(format_name); + + if (!fmt || ast_format_cap_append(request_cap, fmt, 0)) { + if (!fmt) { + ast_ari_response_error( + response, 400, "Bad Request", + "Provided format (%s) was not found", format_name); + } else { + ast_ari_response_alloc_failed(response); + } + ao2_ref(request_cap, -1); + ao2_cleanup(fmt); + chan_data_destroy(chan_data); + return; + } + ao2_ref(fmt, -1); + } } else { - request_cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); + if (!(request_cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) { + ast_ari_response_alloc_failed(response); + chan_data_destroy(chan_data); + return; + } + ast_format_cap_append_by_type(request_cap, AST_MEDIA_TYPE_AUDIO); } diff --git a/res/ari/resource_channels.h b/res/ari/resource_channels.h index c690d70c8..951433f13 100644 --- a/res/ari/resource_channels.h +++ b/res/ari/resource_channels.h @@ -78,6 +78,8 @@ struct ast_ari_channels_originate_args { const char *other_channel_id; /*! The unique id of the channel which is originating this one. */ const char *originator; + /*! The format name capability list to use if originator is not specified. Ex. "ulaw,slin16". Format names an be found with "core show codecs". */ + const char *formats; }; /*! * \brief Body parsing function for /channels. @@ -114,6 +116,8 @@ struct ast_ari_channels_create_args { const char *other_channel_id; /*! Unique ID of the calling channel */ const char *originator; + /*! The format name capability list to use if originator is not specified. Ex. "ulaw,slin16". Format names an be found with "core show codecs". */ + const char *formats; }; /*! * \brief Body parsing function for /channels/create. @@ -175,6 +179,8 @@ struct ast_ari_channels_originate_with_id_args { const char *other_channel_id; /*! The unique id of the channel which is originating this one. */ const char *originator; + /*! The format name capability list to use if originator is not specified. Ex. "ulaw,slin16". Format names an be found with "core show codecs". */ + const char *formats; }; /*! * \brief Body parsing function for /channels/{channelId}. diff --git a/res/res_ari_channels.c b/res/res_ari_channels.c index 951a5475b..412c06d98 100644 --- a/res/res_ari_channels.c +++ b/res/res_ari_channels.c @@ -157,6 +157,10 @@ int ast_ari_channels_originate_parse_body( if (field) { args->originator = ast_json_string_get(field); } + field = ast_json_object_get(body, "formats"); + if (field) { + args->formats = ast_json_string_get(field); + } return 0; } @@ -217,6 +221,9 @@ static void ast_ari_channels_originate_cb( if (strcmp(i->name, "originator") == 0) { args.originator = (i->value); } else + if (strcmp(i->name, "formats") == 0) { + args.formats = (i->value); + } else {} } /* Look for a JSON request entity */ @@ -298,6 +305,10 @@ int ast_ari_channels_create_parse_body( if (field) { args->originator = ast_json_string_get(field); } + field = ast_json_object_get(body, "formats"); + if (field) { + args->formats = ast_json_string_get(field); + } return 0; } @@ -340,6 +351,9 @@ static void ast_ari_channels_create_cb( if (strcmp(i->name, "originator") == 0) { args.originator = (i->value); } else + if (strcmp(i->name, "formats") == 0) { + args.formats = (i->value); + } else {} } /* Look for a JSON request entity */ @@ -502,6 +516,10 @@ int ast_ari_channels_originate_with_id_parse_body( if (field) { args->originator = ast_json_string_get(field); } + field = ast_json_object_get(body, "formats"); + if (field) { + args->formats = ast_json_string_get(field); + } return 0; } @@ -559,6 +577,9 @@ static void ast_ari_channels_originate_with_id_cb( if (strcmp(i->name, "originator") == 0) { args.originator = (i->value); } else + if (strcmp(i->name, "formats") == 0) { + args.formats = (i->value); + } else {} } for (i = path_vars; i; i = i->next) { diff --git a/res/res_ari_recordings.c b/res/res_ari_recordings.c index abc264d9e..a21943520 100644 --- a/res/res_ari_recordings.c +++ b/res/res_ari_recordings.c @@ -257,6 +257,7 @@ static void ast_ari_recordings_get_stored_file_cb( break; case 500: /* Internal Server Error */ case 501: /* Not Implemented */ + case 403: /* The recording file could not be opened */ case 404: /* Recording not found */ is_valid = 1; break; diff --git a/res/res_odbc.c b/res/res_odbc.c index 81e1b3c13..a89c95492 100644 --- a/res/res_odbc.c +++ b/res/res_odbc.c @@ -78,10 +78,19 @@ struct odbc_class unsigned int forcecommit:1; /*!< Should uncommitted transactions be auto-committed on handle release? */ unsigned int isolation; /*!< Flags for how the DB should deal with data in other, uncommitted transactions */ unsigned int conntimeout; /*!< Maximum time the connection process should take */ + unsigned int maxconnections; /*!< Maximum number of allowed connections */ /*! When a connection fails, cache that failure for how long? */ struct timeval negative_connection_cache; /*! When a connection fails, when did that last occur? */ struct timeval last_negative_connect; + /*! A pool of available connections */ + AST_LIST_HEAD_NOLOCK(, odbc_obj) connections; + /*! Lock to protect the connections */ + ast_mutex_t lock; + /*! Condition to notify any pending connection requesters */ + ast_cond_t cond; + /*! The total number of current connections */ + size_t connection_cnt; }; static struct ao2_container *class_container; @@ -90,7 +99,7 @@ static AST_RWLIST_HEAD_STATIC(odbc_tables, odbc_cache_tables); static odbc_status odbc_obj_connect(struct odbc_obj *obj); static odbc_status odbc_obj_disconnect(struct odbc_obj *obj); -static int odbc_register_class(struct odbc_class *class, int connect); +static void odbc_register_class(struct odbc_class *class, int connect); AST_THREADSTORAGE(errors_buf); @@ -157,6 +166,8 @@ int ast_odbc_text2isolation(const char *txt) static void odbc_class_destructor(void *data) { struct odbc_class *class = data; + struct odbc_obj *obj; + /* Due to refcounts, we can safely assume that any objects with a reference * to us will prevent our destruction, so we don't need to worry about them. */ @@ -169,7 +180,14 @@ static void odbc_class_destructor(void *data) if (class->sanitysql) { ast_free(class->sanitysql); } + + while ((obj = AST_LIST_REMOVE_HEAD(&class->connections, list))) { + ao2_ref(obj, -1); + } + SQLFreeHandle(SQL_HANDLE_ENV, class->env); + ast_mutex_destroy(&class->lock); + ast_cond_destroy(&class->cond); } static int null_hash_fn(const void *obj, const int flags) @@ -180,21 +198,23 @@ static int null_hash_fn(const void *obj, const int flags) static void odbc_obj_destructor(void *data) { struct odbc_obj *obj = data; - struct odbc_class *class = obj->parent; - obj->parent = NULL; + odbc_obj_disconnect(obj); - ao2_ref(class, -1); } -static void destroy_table_cache(struct odbc_cache_tables *table) { +static void destroy_table_cache(struct odbc_cache_tables *table) +{ struct odbc_cache_columns *col; + ast_debug(1, "Destroying table cache for %s\n", table->table); + AST_RWLIST_WRLOCK(&table->columns); while ((col = AST_RWLIST_REMOVE_HEAD(&table->columns, list))) { ast_free(col); } AST_RWLIST_UNLOCK(&table->columns); AST_RWLIST_HEAD_DESTROY(&table->columns); + ast_free(table); } @@ -370,18 +390,19 @@ SQLHSTMT ast_odbc_prepare_and_execute(struct odbc_obj *obj, SQLHSTMT (*prepare_c * We must therefore redo everything when we establish a new * connection. */ stmt = prepare_cb(obj, data); + if (!stmt) { + return NULL; + } - if (stmt) { - res = SQLExecute(stmt); - if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) { - if (res == SQL_ERROR) { - ast_odbc_print_errors(SQL_HANDLE_STMT, stmt, "SQL Execute"); - } - - ast_log(LOG_WARNING, "SQL Execute error %d!\n", res); - SQLFreeHandle(SQL_HANDLE_STMT, stmt); - stmt = NULL; + res = SQLExecute(stmt); + if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) { + if (res == SQL_ERROR) { + ast_odbc_print_errors(SQL_HANDLE_STMT, stmt, "SQL Execute"); } + + ast_log(LOG_WARNING, "SQL Execute error %d!\n", res); + SQLFreeHandle(SQL_HANDLE_STMT, stmt); + stmt = NULL; } return stmt; @@ -468,7 +489,7 @@ static int load_odbc_config(void) struct ast_variable *v; char *cat; const char *dsn, *username, *password, *sanitysql; - int enabled, bse, conntimeout, forcecommit, isolation; + int enabled, bse, conntimeout, forcecommit, isolation, maxconnections; struct timeval ncache = { 0, 0 }; int preconnect = 0, res = 0; struct ast_flags config_flags = { 0 }; @@ -495,6 +516,7 @@ static int load_odbc_config(void) conntimeout = 10; forcecommit = 0; isolation = SQL_TXN_READ_COMMITTED; + maxconnections = 1; for (v = ast_variable_browse(config, cat); v; v = v->next) { if (!strcasecmp(v->name, "pooling") || !strncasecmp(v->name, "share", 5) || @@ -538,6 +560,11 @@ static int load_odbc_config(void) ast_log(LOG_ERROR, "Unrecognized value for 'isolation': '%s' in section '%s'\n", v->value, cat); isolation = SQL_TXN_READ_COMMITTED; } + } else if (!strcasecmp(v->name, "max_connections")) { + if (sscanf(v->value, "%30d", &maxconnections) != 1 || maxconnections < 1) { + ast_log(LOG_WARNING, "max_connections must be a positive integer\n"); + maxconnections = 1; + } } } @@ -563,6 +590,7 @@ static int load_odbc_config(void) new->isolation = isolation; new->conntimeout = conntimeout; new->negative_connection_cache = ncache; + new->maxconnections = maxconnections; if (cat) ast_copy_string(new->name, cat, sizeof(new->name)); @@ -581,6 +609,9 @@ static int load_odbc_config(void) break; } + ast_mutex_init(&new->lock); + ast_cond_init(&new->cond, NULL); + odbc_register_class(new, preconnect); ast_log(LOG_NOTICE, "Registered ODBC class '%s' dsn->[%s]\n", cat, dsn); ao2_ref(new, -1); @@ -641,6 +672,7 @@ static char *handle_cli_odbc_show(struct ast_cli_entry *e, int cmd, struct ast_c ast_strftime(timestr, sizeof(timestr), "%Y-%m-%d %T", &tm); ast_cli(a->fd, " Name: %s\n DSN: %s\n", class->name, class->dsn); ast_cli(a->fd, " Last connection attempt: %s\n", timestr); + ast_cli(a->fd, " Number of active connections: %zd (out of %d)\n", class->connection_cnt, class->maxconnections); ast_cli(a->fd, "\n"); } ao2_ref(class, -1); @@ -654,38 +686,47 @@ static struct ast_cli_entry cli_odbc[] = { AST_CLI_DEFINE(handle_cli_odbc_show, "List ODBC DSN(s)") }; -static int odbc_register_class(struct odbc_class *class, int preconnect) +static void odbc_register_class(struct odbc_class *class, int preconnect) { struct odbc_obj *obj; - if (class) { - ao2_link(class_container, class); - /* I still have a reference in the caller, so a deref is NOT missing here. */ - - if (preconnect) { - /* Request and release builds a connection */ - obj = ast_odbc_request_obj(class->name, 0); - if (obj) { - ast_odbc_release_obj(obj); - } - } - return 0; - } else { - ast_log(LOG_WARNING, "Attempted to register a NULL class?\n"); - return -1; + ao2_link(class_container, class); + /* I still have a reference in the caller, so a deref is NOT missing here. */ + + if (!preconnect) { + return; } + + /* Request and release builds a connection */ + obj = ast_odbc_request_obj(class->name, 0); + if (obj) { + ast_odbc_release_obj(obj); + } + + return; } void ast_odbc_release_obj(struct odbc_obj *obj) { - ast_debug(2, "Releasing ODBC handle %p\n", obj); + struct odbc_class *class = obj->parent; -#ifdef DEBUG_THREADS - obj->file[0] = '\0'; - obj->function[0] = '\0'; - obj->lineno = 0; -#endif - ao2_ref(obj, -1); + ast_debug(2, "Releasing ODBC handle %p into pool\n", obj); + + /* The odbc_obj only holds a reference to the class when it is + * actively being used. This guarantees no circular reference + * between odbc_class and odbc_obj. Since it is being released + * we also release our class reference. If a reload occurred before + * the class will go away automatically once all odbc_obj are + * released back. + */ + obj->parent = NULL; + + ast_mutex_lock(&class->lock); + AST_LIST_INSERT_HEAD(&class->connections, obj, list); + ast_cond_signal(&class->cond); + ast_mutex_unlock(&class->lock); + + ao2_ref(class, -1); } int ast_odbc_backslash_is_escape(struct odbc_obj *obj) @@ -703,6 +744,50 @@ static int aoro2_class_cb(void *obj, void *arg, int flags) return 0; } +/* + * \brief Determine if the connection has died. + * + * \param connection The connection to check + * \param class The ODBC class + * \retval 1 Yep, it's dead + * \retval 0 It's alive and well + */ +static int connection_dead(struct odbc_obj *connection, struct odbc_class *class) +{ + char *test_sql = "select 1"; + SQLINTEGER dead; + SQLRETURN res; + SQLHSTMT stmt; + + res = SQLGetConnectAttr(connection->con, SQL_ATTR_CONNECTION_DEAD, &dead, 0, 0); + if (SQL_SUCCEEDED(res)) { + return dead == SQL_CD_TRUE ? 1 : 0; + } + + /* If the Driver doesn't support SQL_ATTR_CONNECTION_DEAD do a + * probing query instead + */ + res = SQLAllocHandle(SQL_HANDLE_STMT, connection->con, &stmt); + if (!SQL_SUCCEEDED(res)) { + return 1; + } + + if (!ast_strlen_zero(class->sanitysql)) { + test_sql = class->sanitysql; + } + + res = SQLPrepare(stmt, (unsigned char *)test_sql, SQL_NTS); + if (!SQL_SUCCEEDED(res)) { + SQLFreeHandle(SQL_HANDLE_STMT, stmt); + return 1; + } + + res = SQLExecute(stmt); + SQLFreeHandle(SQL_HANDLE_STMT, stmt); + + return SQL_SUCCEEDED(res) ? 0 : 1; +} + struct odbc_obj *_ast_odbc_request_obj2(const char *name, struct ast_flags flags, const char *file, const char *function, int lineno) { struct odbc_obj *obj = NULL; @@ -713,17 +798,60 @@ struct odbc_obj *_ast_odbc_request_obj2(const char *name, struct ast_flags flags return NULL; } - /* XXX ODBC connection objects do not have shared ownership, so there is no reason - * to use refcounted objects here. - */ - obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor); - /* Inherit reference from the ao2_callback from before */ - obj->parent = class; - if (odbc_obj_connect(obj) == ODBC_FAIL) { - ao2_ref(obj, -1); - return NULL; + ast_mutex_lock(&class->lock); + + while (!obj) { + obj = AST_LIST_REMOVE_HEAD(&class->connections, list); + + if (!obj) { + if (class->connection_cnt < class->maxconnections) { + /* If no connection is immediately available establish a new + * one if allowed. If we try and fail we give up completely as + * we could go into an infinite loop otherwise. + */ + obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor); + if (!obj) { + break; + } + + obj->parent = ao2_bump(class); + if (odbc_obj_connect(obj) == ODBC_FAIL) { + ao2_ref(obj->parent, -1); + ao2_ref(obj, -1); + obj = NULL; + break; + } + + class->connection_cnt++; + ast_debug(2, "Created ODBC handle %p on class '%s', new count is %zd\n", obj, + name, class->connection_cnt); + } else { + /* Otherwise if we're not allowed to create a new one we + * wait for another thread to give up the connection they + * own. + */ + ast_cond_wait(&class->cond, &class->lock); + } + } else if (connection_dead(obj, class)) { + /* If the connection is dead try to grab another functional one from the + * pool instead of trying to resurrect this one. + */ + ao2_ref(obj, -1); + obj = NULL; + class->connection_cnt--; + ast_debug(2, "ODBC handle %p dead - removing from class '%s', new count is %zd\n", + obj, name, class->connection_cnt); + } else { + /* We successfully grabbed a connection from the pool and all is well! + */ + obj->parent = ao2_bump(class); + ast_debug(2, "Reusing ODBC handle %p from class '%s'\n", obj, name); + } } + ast_mutex_unlock(&class->lock); + ao2_ref(class, -1); + return obj; } @@ -755,14 +883,6 @@ static odbc_status odbc_obj_disconnect(struct odbc_obj *obj) obj->con = NULL; res = SQLDisconnect(con); - if (obj->parent) { - if (res == SQL_SUCCESS || res == SQL_SUCCESS_WITH_INFO) { - ast_debug(3, "Disconnected %d from %s [%s](%p)\n", res, obj->parent->name, obj->parent->dsn, obj); - } else { - ast_debug(3, "res_odbc: %s [%s](%p) already disconnected\n", obj->parent->name, obj->parent->dsn, obj); - } - } - if ((res = SQLFreeHandle(SQL_HANDLE_DBC, con)) == SQL_SUCCESS) { ast_debug(3, "Database handle %p (connection %p) deallocated\n", obj, con); } else { diff --git a/res/res_srtp.c b/res/res_srtp.c index 1e68f9675..0b1fb73e7 100644 --- a/res/res_srtp.c +++ b/res/res_srtp.c @@ -40,7 +40,11 @@ ASTERISK_REGISTER_FILE() #include <srtp/srtp.h> +#ifdef HAVE_OPENSSL +#include <openssl/rand.h> +#else #include <srtp/crypto_kernel.h> +#endif #include "asterisk/lock.h" #include "asterisk/sched.h" @@ -305,7 +309,11 @@ static int ast_srtp_policy_set_master_key(struct ast_srtp_policy *policy, const static int ast_srtp_get_random(unsigned char *key, size_t len) { +#ifdef HAVE_OPENSSL + return RAND_bytes(key, len) > 0 ? 0: -1; +#else return crypto_get_random(key, len) != err_status_ok ? -1: 0; +#endif } static void ast_srtp_set_cb(struct ast_srtp *srtp, const struct ast_srtp_cb *cb, void *data) diff --git a/rest-api/api-docs/channels.json b/rest-api/api-docs/channels.json index aafd231a1..9e8c7c3d4 100644 --- a/rest-api/api-docs/channels.json +++ b/rest-api/api-docs/channels.json @@ -128,6 +128,14 @@ "required": false, "allowMultiple": false, "dataType": "string" + }, + { + "name": "formats", + "description": "The format name capability list to use if originator is not specified. Ex. \"ulaw,slin16\". Format names can be found with \"core show codecs\".", + "paramType": "query", + "required": false, + "allowMultiple": false, + "dataType": "string" } ], "errorResponses": [ @@ -196,6 +204,14 @@ "required": false, "allowMultiple": false, "dataType": "string" + }, + { + "name": "formats", + "description": "The format name capability list to use if originator is not specified. Ex. \"ulaw,slin16\". Format names can be found with \"core show codecs\".", + "paramType": "query", + "required": false, + "allowMultiple": false, + "dataType": "string" } ] } @@ -338,6 +354,14 @@ "required": false, "allowMultiple": false, "dataType": "string" + }, + { + "name": "formats", + "description": "The format name capability list to use if originator is not specified. Ex. \"ulaw,slin16\". Format names can be found with \"core show codecs\".", + "paramType": "query", + "required": false, + "allowMultiple": false, + "dataType": "string" } ], "errorResponses": [ |