diff options
-rw-r--r-- | CHANGES | 4 | ||||
-rw-r--r-- | apps/app_dictate.c | 2 | ||||
-rw-r--r-- | include/asterisk/ari.h | 5 | ||||
-rw-r--r-- | include/asterisk/bridge.h | 2 | ||||
-rw-r--r-- | include/asterisk/bridge_channel_internal.h | 40 | ||||
-rw-r--r-- | include/asterisk/module.h | 19 | ||||
-rw-r--r-- | main/bridge.c | 51 | ||||
-rw-r--r-- | main/bridge_channel.c | 25 | ||||
-rw-r--r-- | main/format_cap.c | 19 | ||||
-rw-r--r-- | main/loader.c | 28 | ||||
-rw-r--r-- | main/sorcery.c | 7 | ||||
-rw-r--r-- | res/ari/resource_asterisk.c | 188 | ||||
-rw-r--r-- | res/ari/resource_asterisk.h | 52 | ||||
-rw-r--r-- | res/parking/parking_applications.c | 12 | ||||
-rw-r--r-- | res/res_ari.c | 7 | ||||
-rw-r--r-- | res/res_ari_asterisk.c | 256 | ||||
-rw-r--r-- | res/res_pjsip_session.c | 104 | ||||
-rw-r--r-- | rest-api/api-docs/asterisk.json | 106 |
18 files changed, 892 insertions, 35 deletions
@@ -192,7 +192,9 @@ AMI ARI ------------------ * A new feature has been added that enables the retrieval of modules and - module information through an HTTP request. + module information through an HTTP request. Information on a single module + can be also be retrieved. Individual modules can be loaded to Asterisk, as + well as unloaded and reloaded. res_pjsip ------------------ diff --git a/apps/app_dictate.c b/apps/app_dictate.c index 0e4ef8d46..98e1fb154 100644 --- a/apps/app_dictate.c +++ b/apps/app_dictate.c @@ -5,7 +5,7 @@ * * Anthony Minessale II <anthmct@yahoo.com> * - * Donated by Sangoma Technologies <http://www.samgoma.com> + * Donated by Sangoma Technologies <http://www.sangoma.com> * * See http://www.asterisk.org for more information about * the Asterisk project. Please do not directly contact diff --git a/include/asterisk/ari.h b/include/asterisk/ari.h index 00769eea5..c3df46a2b 100644 --- a/include/asterisk/ari.h +++ b/include/asterisk/ari.h @@ -225,6 +225,11 @@ void ast_ari_response_ok(struct ast_ari_response *response, void ast_ari_response_no_content(struct ast_ari_response *response); /*! + * \brief Fill in a <tt>Accepted</tt> (202) \a ast_ari_response. + */ +void ast_ari_response_accepted(struct ast_ari_response *response); + +/*! * \brief Fill in a <tt>Created</tt> (201) \a ast_ari_response. * \param response Response to fill in. * \param url URL to the created resource. diff --git a/include/asterisk/bridge.h b/include/asterisk/bridge.h index 8243a1ddb..8858cf02c 100644 --- a/include/asterisk/bridge.h +++ b/include/asterisk/bridge.h @@ -509,6 +509,8 @@ enum ast_bridge_impart_flags { * \param features Bridge features structure. * \param flags defined by enum ast_bridge_impart_flags. * + * \note The given bridge must be unlocked when calling this function. + * * \note The features parameter must be NULL or obtained by * ast_bridge_features_new(). You must not dereference features * after calling even if the call fails. diff --git a/include/asterisk/bridge_channel_internal.h b/include/asterisk/bridge_channel_internal.h index 71892f51a..e3fb73d7e 100644 --- a/include/asterisk/bridge_channel_internal.h +++ b/include/asterisk/bridge_channel_internal.h @@ -130,10 +130,47 @@ int bridge_channel_internal_push(struct ast_bridge_channel *bridge_channel); void bridge_channel_internal_pull(struct ast_bridge_channel *bridge_channel); /*! + * \brief Internal bridge channel wait condition and associated result. + */ +struct bridge_channel_internal_cond { + /*! Lock for the data structure */ + ast_mutex_t lock; + /*! Wait condition */ + ast_cond_t cond; + /*! Wait until done */ + int done; + /*! The bridge channel */ + struct ast_bridge_channel *bridge_channel; +}; + +/*! + * \internal + * \brief Wait for the expected signal. + * \since 13.5.0 + * + * \param cond the wait object + * + * \return Nothing + */ +void bridge_channel_internal_wait(struct bridge_channel_internal_cond *cond); + +/*! + * \internal + * \brief Signal the condition wait. + * \since 13.5.0 + * + * \param cond the wait object + * + * \return Nothing + */ +void bridge_channel_internal_signal(struct bridge_channel_internal_cond *cond); + +/*! * \internal * \brief Join the bridge_channel to the bridge (blocking) * * \param bridge_channel The Channel in the bridge + * \param cond data used for signaling * * \note The bridge_channel->swap holds a channel reference for the swap * channel going into the bridging system. The ref ensures that the swap @@ -148,7 +185,8 @@ void bridge_channel_internal_pull(struct ast_bridge_channel *bridge_channel); * \retval 0 bridge channel successfully joined the bridge * \retval -1 bridge channel failed to join the bridge */ -int bridge_channel_internal_join(struct ast_bridge_channel *bridge_channel); +int bridge_channel_internal_join(struct ast_bridge_channel *bridge_channel, + struct bridge_channel_internal_cond *cond); /*! * \internal diff --git a/include/asterisk/module.h b/include/asterisk/module.h index 59e6c7ed5..9fbeb5ebc 100644 --- a/include/asterisk/module.h +++ b/include/asterisk/module.h @@ -179,6 +179,25 @@ int ast_update_module_list_data(int (*modentry)(const char *module, const char * const char *like, void *data); /*! + * \brief Ask for a list of modules, descriptions, use counts and status. + * \param modentry A callback to an updater function + * \param like + * \param data Data passed into the callback for manipulation + * \param condition The condition to meet + * + * For each of the modules loaded, modentry will be executed with the resource, + * description, and usecount values of each particular module. + * + * \return the number of conditions met + * \since 13.5.0 + */ +int ast_update_module_list_condition(int (*modentry)(const char *module, const char *description, + int usecnt, const char *status, const char *like, + enum ast_module_support_level support_level, + void *data, const char *condition), + const char *like, void *data, const char *condition); + +/*! * \brief Check if module with the name given is loaded * \param name Module name, like "chan_sip.so" * \retval 1 if true diff --git a/main/bridge.c b/main/bridge.c index b2f7b0ffa..ebbfc3976 100644 --- a/main/bridge.c +++ b/main/bridge.c @@ -1549,7 +1549,7 @@ int ast_bridge_join(struct ast_bridge *bridge, } if (!res) { - res = bridge_channel_internal_join(bridge_channel); + res = bridge_channel_internal_join(bridge_channel, NULL); } /* Cleanup all the data in the bridge channel after it leaves the bridge. */ @@ -1579,13 +1579,14 @@ join_exit:; /*! \brief Thread responsible for imparted bridged channels to be departed */ static void *bridge_channel_depart_thread(void *data) { - struct ast_bridge_channel *bridge_channel = data; + struct bridge_channel_internal_cond *cond = data; + struct ast_bridge_channel *bridge_channel = cond->bridge_channel; if (bridge_channel->callid) { ast_callid_threadassoc_add(bridge_channel->callid); } - bridge_channel_internal_join(bridge_channel); + bridge_channel_internal_join(bridge_channel, cond); /* * cleanup @@ -1606,14 +1607,15 @@ static void *bridge_channel_depart_thread(void *data) /*! \brief Thread responsible for independent imparted bridged channels */ static void *bridge_channel_ind_thread(void *data) { - struct ast_bridge_channel *bridge_channel = data; + struct bridge_channel_internal_cond *cond = data; + struct ast_bridge_channel *bridge_channel = cond->bridge_channel; struct ast_channel *chan; if (bridge_channel->callid) { ast_callid_threadassoc_add(bridge_channel->callid); } - bridge_channel_internal_join(bridge_channel); + bridge_channel_internal_join(bridge_channel, cond); chan = bridge_channel->chan; /* cleanup */ @@ -1697,13 +1699,27 @@ int ast_bridge_impart(struct ast_bridge *bridge, /* Actually create the thread that will handle the channel */ if (!res) { + struct bridge_channel_internal_cond cond = { + .done = 0, + .bridge_channel = bridge_channel + }; + ast_mutex_init(&cond.lock); + ast_cond_init(&cond.cond, NULL); + if ((flags & AST_BRIDGE_IMPART_CHAN_MASK) == AST_BRIDGE_IMPART_CHAN_INDEPENDENT) { res = ast_pthread_create_detached(&bridge_channel->thread, NULL, - bridge_channel_ind_thread, bridge_channel); + bridge_channel_ind_thread, &cond); } else { res = ast_pthread_create(&bridge_channel->thread, NULL, - bridge_channel_depart_thread, bridge_channel); + bridge_channel_depart_thread, &cond); } + + if (!res) { + bridge_channel_internal_wait(&cond); + } + + ast_cond_destroy(&cond.cond); + ast_mutex_destroy(&cond.lock); } if (res) { @@ -3953,6 +3969,15 @@ static enum ast_transfer_result attended_transfer_bridge(struct ast_channel *cha struct ast_channel *chan2, struct ast_bridge *bridge1, struct ast_bridge *bridge2, struct ast_attended_transfer_message *transfer_msg) { +#define BRIDGE_LOCK_ONE_OR_BOTH(b1, b2) \ + do { \ + if (b2) { \ + ast_bridge_lock_both(b1, b2); \ + } else { \ + ast_bridge_lock(b1); \ + } \ + } while (0) + static const char *dest = "_attended@transfer/m"; struct ast_channel *local_chan; int cause; @@ -3983,8 +4008,18 @@ static enum ast_transfer_result attended_transfer_bridge(struct ast_channel *cha return AST_BRIDGE_TRANSFER_FAIL; } + /* + * Since bridges need to be unlocked before entering ast_bridge_impart and + * core_local may call into it then the bridges need to be unlocked here. + */ + ast_bridge_unlock(bridge1); + if (bridge2) { + ast_bridge_unlock(bridge2); + } + if (ast_call(local_chan, dest, 0)) { ast_hangup(local_chan); + BRIDGE_LOCK_ONE_OR_BOTH(bridge1, bridge2); return AST_BRIDGE_TRANSFER_FAIL; } @@ -3994,8 +4029,10 @@ static enum ast_transfer_result attended_transfer_bridge(struct ast_channel *cha AST_BRIDGE_IMPART_CHAN_INDEPENDENT)) { ast_hangup(local_chan); ao2_cleanup(local_chan); + BRIDGE_LOCK_ONE_OR_BOTH(bridge1, bridge2); return AST_BRIDGE_TRANSFER_FAIL; } + BRIDGE_LOCK_ONE_OR_BOTH(bridge1, bridge2); if (bridge2) { RAII_VAR(struct ast_channel *, local_chan2, NULL, ao2_cleanup); diff --git a/main/bridge_channel.c b/main/bridge_channel.c index 159722697..28dfd9ccb 100644 --- a/main/bridge_channel.c +++ b/main/bridge_channel.c @@ -2560,7 +2560,27 @@ static void bridge_channel_event_join_leave(struct ast_bridge_channel *bridge_ch ao2_iterator_destroy(&iter); } -int bridge_channel_internal_join(struct ast_bridge_channel *bridge_channel) +void bridge_channel_internal_wait(struct bridge_channel_internal_cond *cond) +{ + ast_mutex_lock(&cond->lock); + while (!cond->done) { + ast_cond_wait(&cond->cond, &cond->lock); + } + ast_mutex_unlock(&cond->lock); +} + +void bridge_channel_internal_signal(struct bridge_channel_internal_cond *cond) +{ + if (cond) { + ast_mutex_lock(&cond->lock); + cond->done = 1; + ast_cond_signal(&cond->cond); + ast_mutex_unlock(&cond->lock); + } +} + +int bridge_channel_internal_join(struct ast_bridge_channel *bridge_channel, + struct bridge_channel_internal_cond *cond) { int res = 0; struct ast_bridge_features *channel_features; @@ -2590,6 +2610,7 @@ int bridge_channel_internal_join(struct ast_bridge_channel *bridge_channel) bridge_channel->bridge->uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan)); + bridge_channel_internal_signal(cond); return -1; } ast_channel_internal_bridge_set(bridge_channel->chan, bridge_channel->bridge); @@ -2624,6 +2645,8 @@ int bridge_channel_internal_join(struct ast_bridge_channel *bridge_channel) } bridge_reconfigured(bridge_channel->bridge, !bridge_channel->inhibit_colp); + bridge_channel_internal_signal(cond); + if (bridge_channel->state == BRIDGE_CHANNEL_STATE_WAIT) { /* * Indicate a source change since this channel is entering the diff --git a/main/format_cap.c b/main/format_cap.c index 52262579c..224fe331f 100644 --- a/main/format_cap.c +++ b/main/format_cap.c @@ -293,7 +293,24 @@ int ast_format_cap_update_by_allow_disallow(struct ast_format_cap *cap, const ch } parse = ast_strdupa(list); - while ((this = strsep(&parse, ","))) { + + /* If the list is being fed to us as a result of ast_format_cap_get_names, + * strip off the paranthesis and immediately apply the inverse of the + * allowing option + */ + if (parse[0] == '(' && parse[strlen(parse) - 1] == ')') { + parse++; + parse[strlen(parse) - 1] = '\0'; + + if (allowing) { + ast_format_cap_remove_by_type(cap, AST_MEDIA_TYPE_UNKNOWN); + } else { + ast_format_cap_append_by_type(cap, AST_MEDIA_TYPE_UNKNOWN); + } + } + + + while ((this = strsep(&parse, ",|"))) { int framems = 0; struct ast_format *format = NULL; diff --git a/main/loader.c b/main/loader.c index 99a47b3b4..b2bdd4a3d 100644 --- a/main/loader.c +++ b/main/loader.c @@ -1445,6 +1445,34 @@ int ast_update_module_list_data(int (*modentry)(const char *module, const char * return total_mod_loaded; } +int ast_update_module_list_condition(int (*modentry)(const char *module, const char *description, + int usecnt, const char *status, + const char *like, + enum ast_module_support_level support_level, + void *data, const char *condition), + const char *like, void *data, const char *condition) +{ + struct ast_module *cur; + int conditions_met = 0; + AST_LIST_HEAD_NOLOCK(, ast_module) alpha_module_list = AST_LIST_HEAD_NOLOCK_INIT_VALUE; + + AST_DLLIST_LOCK(&module_list); + + AST_DLLIST_TRAVERSE(&module_list, cur, entry) { + AST_LIST_INSERT_SORTALPHA(&alpha_module_list, cur, list_entry, resource); + } + + while ((cur = AST_LIST_REMOVE_HEAD(&alpha_module_list, list_entry))) { + conditions_met += modentry(cur->resource, cur->info->description, cur->usecount, + cur->flags.running? "Running" : "Not Running", like, cur->info->support_level, data, + condition); + } + + AST_DLLIST_UNLOCK(&module_list); + + return conditions_met; +} + /*! \brief Check if module exists */ int ast_module_check(const char *name) { diff --git a/main/sorcery.c b/main/sorcery.c index 790e782cc..6d24b1486 100644 --- a/main/sorcery.c +++ b/main/sorcery.c @@ -1600,10 +1600,13 @@ struct ast_json *ast_sorcery_objectset_json_create(const struct ast_sorcery *sor char *buf = NULL; struct ast_json *value = NULL; - if ((res = object_field->handler(object, object_field->args, &buf)) + if (object_field->handler(object, object_field->args, &buf) || !(value = ast_json_string_create(buf)) || ast_json_object_set(json, object_field->name, value)) { - res = -1; + ast_free(buf); + ast_debug(5, "Skipping field '%s' for object type '%s'\n", + object_field->name, object_type->name); + continue; } ast_free(buf); diff --git a/res/ari/resource_asterisk.c b/res/ari/resource_asterisk.c index cc80d0ca3..569013f85 100644 --- a/res/ari/resource_asterisk.c +++ b/res/ari/resource_asterisk.c @@ -185,6 +185,194 @@ void ast_ari_asterisk_list_modules(struct ast_variable *headers, ast_ari_response_ok(response, json); } +/*! + * \brief Identify module by name and process resource information + * \param module Resource name + * \param description Resource description + * \param usecnt Resource use count + * \param status Resource running status + * \param like + * \param support_level Resource support level + * \param data JSON body for resource + * \param condition Name to match resource to + * + * \retval 0 if no resource exists + * \retval 1 if resource exists + */ +static int identify_module(const char *module, const char *description, int usecnt, + const char *status, const char *like, + enum ast_module_support_level support_level, void *data, + const char *condition) +{ + int json_obj_set = 0; + + if (strcmp(condition, module) != 0) { + return 0; + } + + json_obj_set += ast_json_object_set(data, "name", ast_json_string_create(module)); + json_obj_set += ast_json_object_set(data, "description", ast_json_string_create(description)); + json_obj_set += ast_json_object_set(data, "use_count", ast_json_integer_create(usecnt)); + json_obj_set += ast_json_object_set(data, "status", ast_json_string_create(status)); + json_obj_set += ast_json_object_set(data, "support_level", ast_json_string_create( + ast_module_support_level_to_string(support_level))); + + if (json_obj_set != 0) { + return 0; + } + + return 1; +} + +void ast_ari_asterisk_get_module(struct ast_variable *headers, + struct ast_ari_asterisk_get_module_args *args, + struct ast_ari_response *response) +{ + struct ast_json *json; + int module_retrieved = 0; + + ast_assert(response != NULL); + + if (!ast_module_check(args->module_name)) { + ast_ari_response_error( + response, 404, "Not Found", + "Module could not be found in running modules"); + return; + } + + json = ast_json_object_create(); + if (!json) { + ast_ari_response_alloc_failed(response); + return; + } + + module_retrieved = ast_update_module_list_condition(&identify_module, NULL, json, + args->module_name); + if (!module_retrieved) { + ast_ari_response_error( + response, 409, "Conflict", + "Module information could not be retrieved"); + return; + } + + ast_ari_response_ok(response, json); +} + +void ast_ari_asterisk_load_module(struct ast_variable *headers, + struct ast_ari_asterisk_load_module_args *args, + struct ast_ari_response *response) +{ + enum ast_module_load_result load_result; + + ast_assert(response != NULL); + + if (ast_module_check(args->module_name)) { + ast_ari_response_error( + response, 409, "Conflict", + "Module is already loaded"); + return; + } + + load_result = ast_load_resource(args->module_name); + + if (load_result == AST_MODULE_LOAD_DECLINE) { + ast_ari_response_error( + response, 409, "Conflict", + "Module load declined"); + return; + } else if (load_result == AST_MODULE_LOAD_SKIP) { + ast_ari_response_error( + response, 409, "Conflict", + "Module was skipped"); + return; + } else if (load_result == AST_MODULE_LOAD_FAILURE) { + ast_ari_response_error( + response, 409, "Conflict", + "Module could not be loaded properly"); + return; + } + + ast_ari_response_no_content(response); +} + +void ast_ari_asterisk_unload_module(struct ast_variable *headers, + struct ast_ari_asterisk_unload_module_args *args, + struct ast_ari_response *response) +{ + int unload_result; + enum ast_module_unload_mode unload_mode = AST_FORCE_SOFT; + + ast_assert(response != NULL); + + if (!ast_module_check(args->module_name)) { + ast_ari_response_error( + response, 404, "Not Found", + "Module not found in running modules"); + return; + } + + unload_result = ast_unload_resource(args->module_name, unload_mode); + + if (unload_result != 0) { + ast_ari_response_error( + response, 409, "Conflict", + "Module could not be unloaded"); + return; + } + + ast_ari_response_no_content(response); +} + +void ast_ari_asterisk_reload_module(struct ast_variable *headers, + struct ast_ari_asterisk_reload_module_args *args, + struct ast_ari_response *response) +{ + enum ast_module_reload_result reload_result; + + ast_assert(response != NULL); + + if (!ast_module_check(args->module_name)) { + ast_ari_response_error( + response, 404, "Not Found", + "Module not found in running modules"); + return; + } + + reload_result = ast_module_reload(args->module_name); + + if (reload_result == AST_MODULE_RELOAD_NOT_FOUND) { + ast_ari_response_error( + response, 404, "Not Found", + "Module could not be found"); + return; + } else if (reload_result == AST_MODULE_RELOAD_ERROR) { + ast_ari_response_error( + response, 409, "Conflict", + "An unknown error occurred while reloading the module"); + return; + } else if (reload_result == AST_MODULE_RELOAD_IN_PROGRESS) { + ast_ari_response_error( + response, 409, "Conflict", + "Another reload is currently in progress"); + return; + } else if (reload_result == AST_MODULE_RELOAD_UNINITIALIZED) { + ast_ari_response_error( + response, 409, "Conflict", + "Module has not been initialized"); + return; + } else if (reload_result == AST_MODULE_RELOAD_NOT_IMPLEMENTED) { + ast_ari_response_error( + response, 409, "Conflict", + "Module does not support reloading"); + return; + } else if (reload_result == AST_MODULE_RELOAD_QUEUED) { + ast_ari_response_accepted(response); + return; + } + + ast_ari_response_no_content(response); +} + void ast_ari_asterisk_get_global_var(struct ast_variable *headers, struct ast_ari_asterisk_get_global_var_args *args, struct ast_ari_response *response) diff --git a/res/ari/resource_asterisk.h b/res/ari/resource_asterisk.h index b09d2715a..574d947e4 100644 --- a/res/ari/resource_asterisk.h +++ b/res/ari/resource_asterisk.h @@ -78,6 +78,58 @@ struct ast_ari_asterisk_list_modules_args { * \param[out] response HTTP response */ void ast_ari_asterisk_list_modules(struct ast_variable *headers, struct ast_ari_asterisk_list_modules_args *args, struct ast_ari_response *response); +/*! Argument struct for ast_ari_asterisk_get_module() */ +struct ast_ari_asterisk_get_module_args { + /*! Module's name */ + const char *module_name; +}; +/*! + * \brief Get Asterisk module information. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_asterisk_get_module(struct ast_variable *headers, struct ast_ari_asterisk_get_module_args *args, struct ast_ari_response *response); +/*! Argument struct for ast_ari_asterisk_load_module() */ +struct ast_ari_asterisk_load_module_args { + /*! Module's name */ + const char *module_name; +}; +/*! + * \brief Load an Asterisk module. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_asterisk_load_module(struct ast_variable *headers, struct ast_ari_asterisk_load_module_args *args, struct ast_ari_response *response); +/*! Argument struct for ast_ari_asterisk_unload_module() */ +struct ast_ari_asterisk_unload_module_args { + /*! Module's name */ + const char *module_name; +}; +/*! + * \brief Unload an Asterisk module. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_asterisk_unload_module(struct ast_variable *headers, struct ast_ari_asterisk_unload_module_args *args, struct ast_ari_response *response); +/*! Argument struct for ast_ari_asterisk_reload_module() */ +struct ast_ari_asterisk_reload_module_args { + /*! Module's name */ + const char *module_name; +}; +/*! + * \brief Reload an Asterisk module. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_asterisk_reload_module(struct ast_variable *headers, struct ast_ari_asterisk_reload_module_args *args, struct ast_ari_response *response); /*! Argument struct for ast_ari_asterisk_get_global_var() */ struct ast_ari_asterisk_get_global_var_args { /*! The variable to get */ diff --git a/res/parking/parking_applications.c b/res/parking/parking_applications.c index 507747430..34b081bb2 100644 --- a/res/parking/parking_applications.c +++ b/res/parking/parking_applications.c @@ -349,15 +349,21 @@ static int setup_park_common_datastore(struct ast_channel *parkee, const char *p attended_transfer = pbx_builtin_getvar_helper(parkee, "ATTENDEDTRANSFER"); blind_transfer = pbx_builtin_getvar_helper(parkee, "BLINDTRANSFER"); - if (attended_transfer || blind_transfer) { - parker_dial_string = ast_strdupa(S_OR(attended_transfer, blind_transfer)); + if (!ast_strlen_zero(attended_transfer)) { + parker_dial_string = ast_strdupa(attended_transfer); + } else if (!ast_strlen_zero(blind_transfer)) { + parker_dial_string = ast_strdupa(blind_transfer); + /* Ensure that attended_transfer is NULL and not an empty string. */ + attended_transfer = NULL; } ast_channel_unlock(parkee); if (!ast_strlen_zero(parker_dial_string)) { ast_channel_name_to_dial_string(parker_dial_string); - ast_verb(4, "Setting Parker dial string to %s from %s value", parker_dial_string, attended_transfer ? "ATTENDEDTRANSFER" : "BLINDTRANSFER"); + ast_verb(4, "Setting Parker dial string to %s from %s value\n", + parker_dial_string, + attended_transfer ? "ATTENDEDTRANSFER" : "BLINDTRANSFER"); park_datastore->parker_dial_string = ast_strdup(parker_dial_string); } diff --git a/res/res_ari.c b/res/res_ari.c index bdc4379b2..0575fdecd 100644 --- a/res/res_ari.c +++ b/res/res_ari.c @@ -284,6 +284,13 @@ void ast_ari_response_no_content(struct ast_ari_response *response) response->response_text = "No Content"; } +void ast_ari_response_accepted(struct ast_ari_response *response) +{ + response->message = ast_json_null(); + response->response_code = 202; + response->response_text = "Accepted"; +} + void ast_ari_response_alloc_failed(struct ast_ari_response *response) { response->message = ast_json_ref(oom_json); diff --git a/res/res_ari_asterisk.c b/res/res_ari_asterisk.c index fb4207aab..7d938a0a4 100644 --- a/res/res_ari_asterisk.c +++ b/res/res_ari_asterisk.c @@ -260,6 +260,245 @@ static void ast_ari_asterisk_list_modules_cb( fin: __attribute__((unused)) return; } +/*! + * \brief Parameter parsing callback for /asterisk/modules/{moduleName}. + * \param get_params GET parameters in the HTTP request. + * \param path_vars Path variables extracted from the request. + * \param headers HTTP headers. + * \param[out] response Response to the HTTP request. + */ +static void ast_ari_asterisk_get_module_cb( + struct ast_tcptls_session_instance *ser, + struct ast_variable *get_params, struct ast_variable *path_vars, + struct ast_variable *headers, struct ast_ari_response *response) +{ + struct ast_ari_asterisk_get_module_args args = {}; + struct ast_variable *i; + RAII_VAR(struct ast_json *, body, NULL, ast_json_unref); +#if defined(AST_DEVMODE) + int is_valid; + int code; +#endif /* AST_DEVMODE */ + + for (i = path_vars; i; i = i->next) { + if (strcmp(i->name, "moduleName") == 0) { + args.module_name = (i->value); + } else + {} + } + ast_ari_asterisk_get_module(headers, &args, response); +#if defined(AST_DEVMODE) + code = response->response_code; + + switch (code) { + case 0: /* Implementation is still a stub, or the code wasn't set */ + is_valid = response->message == NULL; + break; + case 500: /* Internal Server Error */ + case 501: /* Not Implemented */ + case 404: /* Module could not be found in running modules. */ + case 409: /* Module information could not be retrieved. */ + is_valid = 1; + break; + default: + if (200 <= code && code <= 299) { + is_valid = ast_ari_validate_module( + response->message); + } else { + ast_log(LOG_ERROR, "Invalid error response %d for /asterisk/modules/{moduleName}\n", code); + is_valid = 0; + } + } + + if (!is_valid) { + ast_log(LOG_ERROR, "Response validation failed for /asterisk/modules/{moduleName}\n"); + ast_ari_response_error(response, 500, + "Internal Server Error", "Response validation failed"); + } +#endif /* AST_DEVMODE */ + +fin: __attribute__((unused)) + return; +} +/*! + * \brief Parameter parsing callback for /asterisk/modules/{moduleName}. + * \param get_params GET parameters in the HTTP request. + * \param path_vars Path variables extracted from the request. + * \param headers HTTP headers. + * \param[out] response Response to the HTTP request. + */ +static void ast_ari_asterisk_load_module_cb( + struct ast_tcptls_session_instance *ser, + struct ast_variable *get_params, struct ast_variable *path_vars, + struct ast_variable *headers, struct ast_ari_response *response) +{ + struct ast_ari_asterisk_load_module_args args = {}; + struct ast_variable *i; + RAII_VAR(struct ast_json *, body, NULL, ast_json_unref); +#if defined(AST_DEVMODE) + int is_valid; + int code; +#endif /* AST_DEVMODE */ + + for (i = path_vars; i; i = i->next) { + if (strcmp(i->name, "moduleName") == 0) { + args.module_name = (i->value); + } else + {} + } + ast_ari_asterisk_load_module(headers, &args, response); +#if defined(AST_DEVMODE) + code = response->response_code; + + switch (code) { + case 0: /* Implementation is still a stub, or the code wasn't set */ + is_valid = response->message == NULL; + break; + case 500: /* Internal Server Error */ + case 501: /* Not Implemented */ + case 409: /* Module could not be loaded. */ + is_valid = 1; + break; + default: + if (200 <= code && code <= 299) { + is_valid = ast_ari_validate_void( + response->message); + } else { + ast_log(LOG_ERROR, "Invalid error response %d for /asterisk/modules/{moduleName}\n", code); + is_valid = 0; + } + } + + if (!is_valid) { + ast_log(LOG_ERROR, "Response validation failed for /asterisk/modules/{moduleName}\n"); + ast_ari_response_error(response, 500, + "Internal Server Error", "Response validation failed"); + } +#endif /* AST_DEVMODE */ + +fin: __attribute__((unused)) + return; +} +/*! + * \brief Parameter parsing callback for /asterisk/modules/{moduleName}. + * \param get_params GET parameters in the HTTP request. + * \param path_vars Path variables extracted from the request. + * \param headers HTTP headers. + * \param[out] response Response to the HTTP request. + */ +static void ast_ari_asterisk_unload_module_cb( + struct ast_tcptls_session_instance *ser, + struct ast_variable *get_params, struct ast_variable *path_vars, + struct ast_variable *headers, struct ast_ari_response *response) +{ + struct ast_ari_asterisk_unload_module_args args = {}; + struct ast_variable *i; + RAII_VAR(struct ast_json *, body, NULL, ast_json_unref); +#if defined(AST_DEVMODE) + int is_valid; + int code; +#endif /* AST_DEVMODE */ + + for (i = path_vars; i; i = i->next) { + if (strcmp(i->name, "moduleName") == 0) { + args.module_name = (i->value); + } else + {} + } + ast_ari_asterisk_unload_module(headers, &args, response); +#if defined(AST_DEVMODE) + code = response->response_code; + + switch (code) { + case 0: /* Implementation is still a stub, or the code wasn't set */ + is_valid = response->message == NULL; + break; + case 500: /* Internal Server Error */ + case 501: /* Not Implemented */ + case 404: /* Module not found in running modules. */ + case 409: /* Module could not be unloaded. */ + is_valid = 1; + break; + default: + if (200 <= code && code <= 299) { + is_valid = ast_ari_validate_void( + response->message); + } else { + ast_log(LOG_ERROR, "Invalid error response %d for /asterisk/modules/{moduleName}\n", code); + is_valid = 0; + } + } + + if (!is_valid) { + ast_log(LOG_ERROR, "Response validation failed for /asterisk/modules/{moduleName}\n"); + ast_ari_response_error(response, 500, + "Internal Server Error", "Response validation failed"); + } +#endif /* AST_DEVMODE */ + +fin: __attribute__((unused)) + return; +} +/*! + * \brief Parameter parsing callback for /asterisk/modules/{moduleName}. + * \param get_params GET parameters in the HTTP request. + * \param path_vars Path variables extracted from the request. + * \param headers HTTP headers. + * \param[out] response Response to the HTTP request. + */ +static void ast_ari_asterisk_reload_module_cb( + struct ast_tcptls_session_instance *ser, + struct ast_variable *get_params, struct ast_variable *path_vars, + struct ast_variable *headers, struct ast_ari_response *response) +{ + struct ast_ari_asterisk_reload_module_args args = {}; + struct ast_variable *i; + RAII_VAR(struct ast_json *, body, NULL, ast_json_unref); +#if defined(AST_DEVMODE) + int is_valid; + int code; +#endif /* AST_DEVMODE */ + + for (i = path_vars; i; i = i->next) { + if (strcmp(i->name, "moduleName") == 0) { + args.module_name = (i->value); + } else + {} + } + ast_ari_asterisk_reload_module(headers, &args, response); +#if defined(AST_DEVMODE) + code = response->response_code; + + switch (code) { + case 0: /* Implementation is still a stub, or the code wasn't set */ + is_valid = response->message == NULL; + break; + case 500: /* Internal Server Error */ + case 501: /* Not Implemented */ + case 404: /* Module not found in running modules. */ + case 409: /* Module could not be reloaded. */ + is_valid = 1; + break; + default: + if (200 <= code && code <= 299) { + is_valid = ast_ari_validate_void( + response->message); + } else { + ast_log(LOG_ERROR, "Invalid error response %d for /asterisk/modules/{moduleName}\n", code); + is_valid = 0; + } + } + + if (!is_valid) { + ast_log(LOG_ERROR, "Response validation failed for /asterisk/modules/{moduleName}\n"); + ast_ari_response_error(response, 500, + "Internal Server Error", "Response validation failed"); + } +#endif /* AST_DEVMODE */ + +fin: __attribute__((unused)) + return; +} int ast_ari_asterisk_get_global_var_parse_body( struct ast_json *body, struct ast_ari_asterisk_get_global_var_args *args) @@ -460,13 +699,26 @@ static struct stasis_rest_handlers asterisk_info = { .children = { } }; /*! \brief REST handler for /api-docs/asterisk.{format} */ +static struct stasis_rest_handlers asterisk_modules_moduleName = { + .path_segment = "moduleName", + .is_wildcard = 1, + .callbacks = { + [AST_HTTP_GET] = ast_ari_asterisk_get_module_cb, + [AST_HTTP_POST] = ast_ari_asterisk_load_module_cb, + [AST_HTTP_DELETE] = ast_ari_asterisk_unload_module_cb, + [AST_HTTP_PUT] = ast_ari_asterisk_reload_module_cb, + }, + .num_children = 0, + .children = { } +}; +/*! \brief REST handler for /api-docs/asterisk.{format} */ static struct stasis_rest_handlers asterisk_modules = { .path_segment = "modules", .callbacks = { [AST_HTTP_GET] = ast_ari_asterisk_list_modules_cb, }, - .num_children = 0, - .children = { } + .num_children = 1, + .children = { &asterisk_modules_moduleName, } }; /*! \brief REST handler for /api-docs/asterisk.{format} */ static struct stasis_rest_handlers asterisk_variable = { diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c index 84c343d3c..ce5237717 100644 --- a/res/res_pjsip_session.c +++ b/res/res_pjsip_session.c @@ -1161,7 +1161,7 @@ static void session_destructor(void *obj) struct ast_sip_session_delayed_request *delay; ast_debug(3, "Destroying SIP session with endpoint %s\n", - ast_sorcery_object_get_id(session->endpoint)); + session->endpoint ? ast_sorcery_object_get_id(session->endpoint) : "<none>"); while ((supplement = AST_LIST_REMOVE_HEAD(&session->supplements, next))) { if (supplement->session_destroy) { @@ -1212,8 +1212,9 @@ static int add_supplements(struct ast_sip_session *session) static int add_session_media(void *obj, void *arg, int flags) { struct sdp_handler_list *handler_list = obj; - struct ast_sip_session * session = arg; + struct ast_sip_session *session = arg; RAII_VAR(struct ast_sip_session_media *, session_media, NULL, ao2_cleanup); + session_media = ao2_alloc(sizeof(*session_media) + strlen(handler_list->stream_type), session_media_dtor); if (!session_media) { return CMP_STOP; @@ -1253,9 +1254,11 @@ struct ast_sip_channel_pvt *ast_sip_channel_pvt_alloc(void *pvt, struct ast_sip_ struct ast_sip_session *ast_sip_session_alloc(struct ast_sip_endpoint *endpoint, struct ast_sip_contact *contact, pjsip_inv_session *inv_session) { - RAII_VAR(struct ast_sip_session *, session, ao2_alloc(sizeof(*session), session_destructor), ao2_cleanup); + RAII_VAR(struct ast_sip_session *, session, NULL, ao2_cleanup); struct ast_sip_session_supplement *iter; int dsp_features = 0; + + session = ao2_alloc(sizeof(*session), session_destructor); if (!session) { return NULL; } @@ -1296,6 +1299,7 @@ struct ast_sip_session *ast_sip_session_alloc(struct ast_sip_endpoint *endpoint, if (dsp_features) { if (!(session->dsp = ast_dsp_new())) { + /* Release the ref held by session->inv_session */ ao2_ref(session, -1); return NULL; } @@ -1304,6 +1308,7 @@ struct ast_sip_session *ast_sip_session_alloc(struct ast_sip_endpoint *endpoint, } if (add_supplements(session)) { + /* Release the ref held by session->inv_session */ ao2_ref(session, -1); return NULL; } @@ -1688,6 +1693,23 @@ int ast_sip_session_defer_termination(struct ast_sip_session *session) return res; } +/*! + * \internal + * \brief Stop the defer termination timer if it is still running. + * \since 13.5.0 + * + * \param session Which session to stop the timer. + * + * \return Nothing + */ +static void sip_session_defer_termination_stop_timer(struct ast_sip_session *session) +{ + if (pj_timer_heap_cancel(pjsip_endpt_get_timer_heap(ast_sip_get_pjsip_endpoint()), + &session->scheduled_termination)) { + ao2_ref(session, -1); + } +} + void ast_sip_session_defer_termination_cancel(struct ast_sip_session *session) { if (!session->defer_terminate) { @@ -1702,10 +1724,7 @@ void ast_sip_session_defer_termination_cancel(struct ast_sip_session *session) } /* Stop the termination timer if it is still running. */ - if (pj_timer_heap_cancel(pjsip_endpt_get_timer_heap(ast_sip_get_pjsip_endpoint()), - &session->scheduled_termination)) { - ao2_ref(session, -1); - } + sip_session_defer_termination_stop_timer(session); } struct ast_sip_session *ast_sip_dialog_get_session(pjsip_dialog *dlg) @@ -2247,25 +2266,39 @@ static void handle_outgoing(struct ast_sip_session *session, pjsip_tx_data *tdat } } -static int session_end(struct ast_sip_session *session) +static void session_end(struct ast_sip_session *session) { struct ast_sip_session_supplement *iter; /* Stop the scheduled termination */ - if (pj_timer_heap_cancel(pjsip_endpt_get_timer_heap(ast_sip_get_pjsip_endpoint()), &session->scheduled_termination)) { - ao2_ref(session, -1); - } + sip_session_defer_termination_stop_timer(session); - /* Session is dead. Let's get rid of the reference to the session */ + /* Session is dead. Notify the supplements. */ AST_LIST_TRAVERSE(&session->supplements, iter, next) { if (iter->session_end) { iter->session_end(session); } } +} + +/*! + * \internal + * \brief Complete ending session activities. + * \since 13.5.0 + * + * \param vsession Which session to complete stopping. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int session_end_completion(void *vsession) +{ + struct ast_sip_session *session = vsession; - session->inv_session->mod_data[session_module.id] = NULL; ast_sip_dialog_set_serializer(session->inv_session->dlg, NULL); ast_sip_dialog_set_endpoint(session->inv_session->dlg, NULL); + + /* Now we can release the ref that was held by session->inv_session */ ao2_cleanup(session); return 0; } @@ -2372,9 +2405,7 @@ static void session_inv_on_tsx_state_changed(pjsip_inv_session *inv, pjsip_trans print_debug_details(inv, tsx, e); if (!session) { - /* Transaction likely timed out after the call was hung up. Just - * ignore such transaction changes - */ + /* The session has ended. Ignore the transaction change. */ return; } switch (e->body.tsx_state.type) { @@ -2455,7 +2486,48 @@ static void session_inv_on_tsx_state_changed(pjsip_inv_session *inv, pjsip_trans } break; case PJSIP_EVENT_TRANSPORT_ERROR: + /* + * Clear the module data now to block session_inv_on_state_changed() + * from calling session_end() if it hasn't already done so. + */ + inv->mod_data[session_module.id] = NULL; + + if (inv->state != PJSIP_INV_STATE_DISCONNECTED) { + session_end(session); + } + + /* + * Pass the session ref held by session->inv_session to + * session_end_completion(). + */ + session_end_completion(session); + return; case PJSIP_EVENT_TIMER: + /* + * The timer event is run by the pjsip monitor thread and not + * by the session serializer. + */ + if (inv->state == PJSIP_INV_STATE_DISCONNECTED) { + /* + * We are locking because ast_sip_dialog_get_session() needs + * the dialog locked to get the session by other threads. + */ + pjsip_dlg_inc_lock(inv->dlg); + session = inv->mod_data[session_module.id]; + inv->mod_data[session_module.id] = NULL; + pjsip_dlg_dec_lock(inv->dlg); + + /* + * Pass the session ref held by session->inv_session to + * session_end_completion(). + */ + if (ast_sip_push_task(session->serializer, session_end_completion, session)) { + /* Do it anyway even though this is not the right thread. */ + session_end_completion(session); + } + return; + } + break; case PJSIP_EVENT_USER: case PJSIP_EVENT_UNKNOWN: case PJSIP_EVENT_TSX_STATE: diff --git a/rest-api/api-docs/asterisk.json b/rest-api/api-docs/asterisk.json index ade5938c9..c6968a5ba 100644 --- a/rest-api/api-docs/asterisk.json +++ b/rest-api/api-docs/asterisk.json @@ -51,6 +51,112 @@ ] }, { + "path": "/asterisk/modules/{moduleName}", + "description": "Asterisk module", + "operations": [ + { + "httpMethod": "GET", + "summary": "Get Asterisk module information.", + "nickname": "getModule", + "responseClass": "Module", + "parameters": [ + { + "name": "moduleName", + "description": "Module's name", + "paramType": "path", + "required": true, + "allowMultiple": false, + "dataType": "string" + } + ], + "errorResponses": [ + { + "code": 404, + "reason": "Module could not be found in running modules." + }, + { + "code": 409, + "reason": "Module information could not be retrieved." + } + ] + }, + { + "httpMethod": "POST", + "summary": "Load an Asterisk module.", + "nickname": "loadModule", + "responseClass": "void", + "parameters": [ + { + "name": "moduleName", + "description": "Module's name", + "paramType": "path", + "required": true, + "allowMultiple": false, + "dataType": "string" + } + ], + "errorResponses": [ + { + "code": 409, + "reason": "Module could not be loaded." + } + ] + }, + { + "httpMethod": "DELETE", + "summary": "Unload an Asterisk module.", + "nickname": "unloadModule", + "responseClass": "void", + "parameters": [ + { + "name": "moduleName", + "description": "Module's name", + "paramType": "path", + "required": true, + "allowMultiple": false, + "dataType": "string" + } + ], + "errorResponses": [ + { + "code": 404, + "reason": "Module not found in running modules." + }, + { + "code": 409, + "reason": "Module could not be unloaded." + } + ] + }, + { + "httpMethod": "PUT", + "summary": "Reload an Asterisk module.", + "nickname": "reloadModule", + "responseClass": "void", + "parameters": [ + { + "name": "moduleName", + "description": "Module's name", + "paramType": "path", + "required": true, + "allowMultiple": false, + "dataType": "string" + } + ], + "errorResponses": [ + { + "code": 404, + "reason": "Module not found in running modules." + }, + { + "code": 409, + "reason": "Module could not be reloaded." + } + ] + } + ] + }, + { "path": "/asterisk/variable", "description": "Global variables", "operations": [ |