Allow res_parking to be unloadable

This change protects accesses of res_parking such that it can unload
safely once transient uses of its registered functions are complete.
The parking API has been restructured such that its consumers do not
have access to the vtable exposed by the parking provider, but instead
route through stubs to prevent consumers from holding on to function
pointers.

This adds calls to all the parking unload functions and moves
application loading and unloading into functions in
parking_applications.c similar to the rest of the parts of res_parking.

Review: https://reviewboard.asterisk.org/r/2763/
(closes issue ASTERISK-22142)


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@396890 65c4cc65-6c06-0410-ace0-fbb531ad65f3
changes/78/78/1
Kinsey Moore 12 years ago
parent d7f1f31270
commit 56aea1c030

@ -9245,10 +9245,6 @@ static void *analog_ss_thread(void *data)
int idx;
struct ast_format tmpfmt;
RAII_VAR(struct ast_features_pickup_config *, pickup_cfg, NULL, ao2_cleanup);
RAII_VAR(struct ast_parking_bridge_feature_fn_table *, parking_provider,
ast_parking_get_bridge_features(),
ao2_cleanup);
int is_exten_parking;
const char *pickupexten;
ast_mutex_lock(&ss_thread_lock);
@ -9562,6 +9558,8 @@ static void *analog_ss_thread(void *data)
if (p->subs[SUB_THREEWAY].owner)
timeout = 999999;
while (len < AST_MAX_EXTENSION-1) {
int is_exten_parking = 0;
/* Read digit unless it's supposed to be immediate, in which case the
only answer is 's' */
if (p->immediate)
@ -9584,7 +9582,9 @@ static void *analog_ss_thread(void *data)
} else {
tone_zone_play_tone(p->subs[idx].dfd, DAHDI_TONE_DIALTONE);
}
is_exten_parking = (parking_provider ? parking_provider->parking_is_exten_park(ast_channel_context(chan), exten) : 0);
if (ast_parking_provider_registered()) {
is_exten_parking = ast_parking_is_exten_park(ast_channel_context(chan), exten);
}
if (ast_exists_extension(chan, ast_channel_context(chan), exten, 1, p->cid_num) && !is_exten_parking) {
if (!res || !ast_matchmore_extension(chan, ast_channel_context(chan), exten, 1, p->cid_num)) {
if (getforward) {
@ -9729,7 +9729,7 @@ static void *analog_ss_thread(void *data)
ast_channel_lock(chan);
bridge_channel = ast_channel_get_bridge_channel(chan);
ast_channel_unlock(chan);
if (bridge_channel && !parking_provider->parking_blind_transfer_park(bridge_channel, ast_channel_context(chan), exten)) {
if (bridge_channel && !ast_parking_blind_transfer_park(bridge_channel, ast_channel_context(chan), exten)) {
ast_verb(3, "Parking call to '%s'\n", ast_channel_name(chan));
}
break;

@ -6408,14 +6408,11 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession
{
char extout[AST_MAX_EXTENSION];
char message[32];
RAII_VAR(struct ast_parking_bridge_feature_fn_table *, parking_provider,
ast_parking_get_bridge_features(),
ao2_cleanup);
RAII_VAR(struct ast_bridge_channel *, bridge_channel, NULL, ao2_cleanup);
SKINNY_DEBUG(DEBUG_PACKET, 3, "Received STIMULUS_CALLPARK from %s, inst %d, callref %d\n",
d->name, instance, callreference);
if (!parking_provider) {
if (!ast_parking_provider_registered()) {
transmit_displaynotify(d, "Call Park not available", 10);
break;
}
@ -6431,7 +6428,7 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession
break;
}
if (!parking_provider->parking_park_call(bridge_channel, extout, sizeof(extout))) {
if (!ast_parking_park_call(bridge_channel, extout, sizeof(extout))) {
snprintf(message, sizeof(message), "Call Parked at: %s", extout);
transmit_displaynotify(d, message, 10);
break;
@ -7158,14 +7155,11 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse
{
char extout[AST_MAX_EXTENSION];
char message[32];
RAII_VAR(struct ast_parking_bridge_feature_fn_table *, parking_provider,
ast_parking_get_bridge_features(),
ao2_cleanup);
RAII_VAR(struct ast_bridge_channel *, bridge_channel, NULL, ao2_cleanup);
SKINNY_DEBUG(DEBUG_PACKET, 3, "Received SOFTKEY_PARK from %s, inst %d, callref %d\n",
d->name, instance, callreference);
if (!parking_provider) {
if (!ast_parking_provider_registered()) {
transmit_displaynotify(d, "Call Park not available", 10);
break;
}
@ -7183,7 +7177,7 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse
break;
}
if (!parking_provider->parking_park_call(bridge_channel, extout, sizeof(extout))) {
if (!ast_parking_park_call(bridge_channel, extout, sizeof(extout))) {
snprintf(message, sizeof(message), "Call Parked at: %s", extout);
transmit_displaynotify(d, message, 10);
break;

@ -1715,11 +1715,7 @@ static void *__analog_ss_thread(void *data)
int idx;
struct ast_callid *callid;
RAII_VAR(struct ast_features_pickup_config *, pickup_cfg, NULL, ao2_cleanup);
RAII_VAR(struct ast_parking_bridge_feature_fn_table *, parking_provider,
ast_parking_get_bridge_features(),
ao2_cleanup);
const char *pickupexten;
int is_exten_parking;
analog_increase_ss_count();
@ -2077,6 +2073,8 @@ static void *__analog_ss_thread(void *data)
timeout = 999999;
}
while (len < AST_MAX_EXTENSION-1) {
int is_exten_parking = 0;
/* Read digit unless it's supposed to be immediate, in which case the
only answer is 's' */
if (p->immediate) {
@ -2100,7 +2098,9 @@ static void *__analog_ss_thread(void *data)
} else {
analog_play_tone(p, idx, ANALOG_TONE_DIALTONE);
}
is_exten_parking = (parking_provider ? parking_provider->parking_is_exten_park(ast_channel_context(chan), exten) : 0);
if (ast_parking_provider_registered()) {
is_exten_parking = ast_parking_is_exten_park(ast_channel_context(chan), exten);
}
if (ast_exists_extension(chan, ast_channel_context(chan), exten, 1, p->cid_num) && !is_exten_parking) {
if (!res || !ast_matchmore_extension(chan, ast_channel_context(chan), exten, 1, p->cid_num)) {
if (getforward) {
@ -2253,7 +2253,7 @@ static void *__analog_ss_thread(void *data)
ast_channel_lock(chan);
bridge_channel = ast_channel_get_bridge_channel(chan);
ast_channel_unlock(chan);
if (bridge_channel && !parking_provider->parking_blind_transfer_park(bridge_channel, ast_channel_context(chan), exten)) {
if (bridge_channel && !ast_parking_blind_transfer_park(bridge_channel, ast_channel_context(chan), exten)) {
ast_verb(3, "Parking call to '%s'\n", ast_channel_name(chan));
}
ao2_ref(bridge_channel, -1);

@ -114,6 +114,8 @@ struct stasis_message_type *ast_parked_call_type(void);
#define PARKING_MODULE_VERSION 1
struct ast_module_info;
/*!
* \brief A function table providing parking functionality to the \ref AstBridging
* Bridging API and other consumers
@ -188,15 +190,67 @@ struct ast_parking_bridge_feature_fn_table {
* \retval non-zero on error
*/
int (* parking_park_bridge_channel)(struct ast_bridge_channel *parkee, const char *parkee_uuid, const char *parker_uuid, const char *app_data);
/*! \brief The module info for the module registering this parking provider */
const struct ast_module_info *module_info;
};
/*!
* \brief Obtain the current parking provider
* \brief Determine if the context/exten is a "parking" extension
*
* \retval 0 if the extension is not a parking extension
* \retval 1 if the extension is a parking extension
*/
int ast_parking_is_exten_park(const char *context, const char *exten);
/*!
* \brief Park the bridge and/or callers that this channel is in
*
* \param parker The bridge_channel parking the bridge
* \param exten Optional. The extension the channel or bridge was parked at if the
* call succeeds.
* \param length Optional. If \c exten is specified, the size of the buffer.
*
* \note This is safe to be called outside of the \ref AstBridging Bridging API.
*
* \retval 0 on success
* \retval non-zero on error
*/
int ast_parking_park_call(struct ast_bridge_channel *parker, char *exten, size_t length);
/*!
* \brief Perform a blind transfer to a parking extension.
*
* \param parker The \ref bridge_channel object that is initiating the parking
* \param context The context to blind transfer to
* \param exten The extension to blind transfer to
*
* \note If the bridge \ref parker is in has more than one other occupant, the entire
* bridge will be parked using a Local channel
*
* \note This is safe to be called outside of the \ref AstBridging Bridging API.
*
* \retval 0 on success
* \retval non-zero on error
*/
int ast_parking_blind_transfer_park(struct ast_bridge_channel *parker, const char *context, const char *exten);
/*!
* \brief Perform a direct park on a channel in a bridge.
*
* \retval NULL if no provider exists
* \retval an ao2 ref counted object of the existing provider's function table
* \param parkee The channel in the bridge to be parked.
* \param parkee_uuid The UUID of the channel being packed.
* \param parker_uuid The UUID of the channel performing the park.
* \param app_data Data to pass to the Park application
*
* \note This must be called within the context of the \ref AstBridging Bridging API.
* External entities should not call this method directly, but should instead use
* the direct call parking method or the blind transfer method.
*
* \retval 0 on success
* \retval non-zero on error
*/
struct ast_parking_bridge_feature_fn_table *ast_parking_get_bridge_features(void);
int ast_parking_park_bridge_channel(struct ast_bridge_channel *parkee, const char *parkee_uuid, const char *parker_uuid, const char *app_data);
/*!
* \brief Register a parking provider
@ -217,3 +271,11 @@ int ast_parking_register_bridge_features(struct ast_parking_bridge_feature_fn_ta
* \retval -1 on error
*/
int ast_parking_unregister_bridge_features(const char *module_name);
/*!
* \brief Check whether a parking provider is registered
*
* \retval 0 if there is no parking provider regsistered
* \retval 1 if there is a parking provider regsistered
*/
int ast_parking_provider_registered(void);

@ -3844,11 +3844,8 @@ static struct ast_channel *get_transferee(struct ao2_container *channels, struct
static enum ast_transfer_result try_parking(struct ast_channel *transferer, const char *context, const char *exten)
{
RAII_VAR(struct ast_bridge_channel *, transferer_bridge_channel, NULL, ao2_cleanup);
RAII_VAR(struct ast_parking_bridge_feature_fn_table *, parking_provider,
ast_parking_get_bridge_features(),
ao2_cleanup);
if (!parking_provider) {
if (!ast_parking_provider_registered()) {
return AST_BRIDGE_TRANSFER_FAIL;
}
@ -3860,7 +3857,7 @@ static enum ast_transfer_result try_parking(struct ast_channel *transferer, cons
return AST_BRIDGE_TRANSFER_FAIL;
}
if (parking_provider->parking_blind_transfer_park(transferer_bridge_channel,
if (ast_parking_blind_transfer_park(transferer_bridge_channel,
context, exten)) {
return AST_BRIDGE_TRANSFER_FAIL;
}

@ -754,17 +754,13 @@ struct bridge_park {
*/
static void bridge_channel_park(struct ast_bridge_channel *bridge_channel, struct bridge_park *payload)
{
RAII_VAR(struct ast_parking_bridge_feature_fn_table *, parking_provider,
ast_parking_get_bridge_features(),
ao2_cleanup);
if (!parking_provider) {
if (!ast_parking_provider_registered()) {
ast_log(AST_LOG_WARNING, "Unable to park %s: No parking provider loaded!\n",
ast_channel_name(bridge_channel->chan));
return;
}
if (parking_provider->parking_park_bridge_channel(bridge_channel, payload->parkee_uuid,
if (ast_parking_park_bridge_channel(bridge_channel, payload->parkee_uuid,
&payload->parkee_uuid[payload->parker_uuid_offset],
payload->app_data_offset ? &payload->parkee_uuid[payload->app_data_offset] : NULL)) {
ast_log(AST_LOG_WARNING, "Error occurred while parking %s\n",

@ -34,6 +34,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/parking.h"
#include "asterisk/channel.h"
#include "asterisk/_private.h"
#include "asterisk/module.h"
/*! \brief Message type for parked calls */
STASIS_MESSAGE_TYPE_DEFN(ast_parked_call_type);
@ -124,19 +125,77 @@ struct ast_parked_call_payload *ast_parked_call_payload_create(enum ast_parked_c
return payload;
}
struct ast_parking_bridge_feature_fn_table *ast_parking_get_bridge_features(void)
int ast_parking_park_bridge_channel(struct ast_bridge_channel *parkee, const char *parkee_uuid, const char *parker_uuid, const char *app_data)
{
return (struct ast_parking_bridge_feature_fn_table*)ao2_global_obj_ref(parking_provider);
RAII_VAR(struct ast_parking_bridge_feature_fn_table *, table,
ao2_global_obj_ref(parking_provider), ao2_cleanup);
if (!table || !table->parking_park_bridge_channel) {
return -1;
}
if (table->module_info) {
SCOPED_MODULE_USE(table->module_info->self);
return table->parking_park_bridge_channel(parkee, parkee_uuid, parker_uuid, app_data);
}
return table->parking_park_bridge_channel(parkee, parkee_uuid, parker_uuid, app_data);
}
/*! \brief A wrapper around the fn_table to ao2-ify it */
struct parking_provider_wrapper {
struct ast_parking_bridge_feature_fn_table fn_table;
};
int ast_parking_blind_transfer_park(struct ast_bridge_channel *parker, const char *context, const char *exten)
{
RAII_VAR(struct ast_parking_bridge_feature_fn_table *, table,
ao2_global_obj_ref(parking_provider), ao2_cleanup);
if (!table || !table->parking_blind_transfer_park) {
return -1;
}
if (table->module_info) {
SCOPED_MODULE_USE(table->module_info->self);
return table->parking_blind_transfer_park(parker, context, exten);
}
return table->parking_blind_transfer_park(parker, context, exten);
}
int ast_parking_park_call(struct ast_bridge_channel *parker, char *exten, size_t length)
{
RAII_VAR(struct ast_parking_bridge_feature_fn_table *, table,
ao2_global_obj_ref(parking_provider), ao2_cleanup);
if (!table || !table->parking_park_call) {
return -1;
}
if (table->module_info) {
SCOPED_MODULE_USE(table->module_info->self);
return table->parking_park_call(parker, exten, length);
}
return table->parking_park_call(parker, exten, length);
}
int ast_parking_is_exten_park(const char *context, const char *exten)
{
RAII_VAR(struct ast_parking_bridge_feature_fn_table *, table,
ao2_global_obj_ref(parking_provider), ao2_cleanup);
if (!table || !table->parking_is_exten_park) {
return -1;
}
if (table->module_info) {
SCOPED_MODULE_USE(table->module_info->self);
return table->parking_is_exten_park(context, exten);
}
return table->parking_is_exten_park(context, exten);
}
int ast_parking_register_bridge_features(struct ast_parking_bridge_feature_fn_table *fn_table)
{
RAII_VAR(struct parking_provider_wrapper *, wrapper,
RAII_VAR(struct ast_parking_bridge_feature_fn_table *, wrapper,
ao2_global_obj_ref(parking_provider), ao2_cleanup);
if (fn_table->module_version != PARKING_MODULE_VERSION) {
@ -147,7 +206,7 @@ int ast_parking_register_bridge_features(struct ast_parking_bridge_feature_fn_ta
if (wrapper) {
ast_log(AST_LOG_WARNING, "Parking provider already registered by %s!\n",
wrapper->fn_table.module_name);
wrapper->module_name);
return -1;
}
@ -155,7 +214,7 @@ int ast_parking_register_bridge_features(struct ast_parking_bridge_feature_fn_ta
if (!wrapper) {
return -1;
}
wrapper->fn_table = *fn_table;
*wrapper = *fn_table;
ao2_global_obj_replace(parking_provider, wrapper);
return 0;
@ -163,15 +222,14 @@ int ast_parking_register_bridge_features(struct ast_parking_bridge_feature_fn_ta
int ast_parking_unregister_bridge_features(const char *module_name)
{
RAII_VAR(struct parking_provider_wrapper *, wrapper,
RAII_VAR(struct ast_parking_bridge_feature_fn_table *, wrapper,
ao2_global_obj_ref(parking_provider), ao2_cleanup);
if (!wrapper) {
ast_log(AST_LOG_WARNING, "No parking provider to unregister\n");
return -1;
}
if (strcmp(wrapper->fn_table.module_name, module_name)) {
if (strcmp(wrapper->module_name, module_name)) {
ast_log(AST_LOG_WARNING, "%s has not registered the parking provider\n", module_name);
return -1;
}
@ -179,3 +237,11 @@ int ast_parking_unregister_bridge_features(const char *module_name)
ao2_global_obj_replace_unref(parking_provider, NULL);
return 0;
}
int ast_parking_provider_registered(void)
{
RAII_VAR(struct ast_parking_bridge_feature_fn_table *, table,
ao2_global_obj_ref(parking_provider), ao2_cleanup);
return !!table;
}

@ -194,6 +194,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</application>
***/
#define PARK_AND_ANNOUNCE_APPLICATION "ParkAndAnnounce"
/* Park a call */
enum park_args {
@ -488,7 +490,7 @@ struct ast_bridge *park_application_setup(struct ast_channel *parkee, struct ast
}
int park_app_exec(struct ast_channel *chan, const char *data)
static int park_app_exec(struct ast_channel *chan, const char *data)
{
RAII_VAR(struct ast_bridge *, parking_bridge, NULL, ao2_cleanup);
@ -547,7 +549,7 @@ int park_app_exec(struct ast_channel *chan, const char *data)
/* Retrieve a parked call */
int parked_call_app_exec(struct ast_channel *chan, const char *data)
static int parked_call_app_exec(struct ast_channel *chan, const char *data)
{
RAII_VAR(struct parking_lot *, lot, NULL, ao2_cleanup);
RAII_VAR(struct parked_user *, pu, NULL, ao2_cleanup); /* Parked user being retrieved */
@ -765,7 +767,7 @@ static void park_announce_update_cb(void *data, struct stasis_subscription *sub,
*dial_string = '\0'; /* If we observe this dial string on a second pass, we don't want to do anything with it. */
}
int park_and_announce_app_exec(struct ast_channel *chan, const char *data)
static int park_and_announce_app_exec(struct ast_channel *chan, const char *data)
{
struct ast_bridge_features chan_features;
char *parse;
@ -857,3 +859,29 @@ int park_and_announce_app_exec(struct ast_channel *chan, const char *data)
return res;
}
int load_parking_applications(void)
{
const struct ast_module_info *ast_module_info = parking_get_module_info();
if (ast_register_application_xml(PARK_APPLICATION, park_app_exec)) {
return -1;
}
if (ast_register_application_xml(PARKED_CALL_APPLICATION, parked_call_app_exec)) {
return -1;
}
if (ast_register_application_xml(PARK_AND_ANNOUNCE_APPLICATION, park_and_announce_app_exec)) {
return -1;
}
return 0;
}
void unload_parking_applications(void)
{
ast_unregister_application(PARK_APPLICATION);
ast_unregister_application(PARKED_CALL_APPLICATION);
ast_unregister_application(PARK_AND_ANNOUNCE_APPLICATION);
}

@ -40,6 +40,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/say.h"
#include "asterisk/datastore.h"
#include "asterisk/stasis.h"
#include "asterisk/module.h"
#include "asterisk/core_local.h"
struct parked_subscription_datastore {
@ -444,6 +445,8 @@ static int parking_park_call(struct ast_bridge_channel *parker, char *exten, siz
static int feature_park_call(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
{
SCOPED_MODULE_USE(parking_get_module_info()->self);
return parking_park_call(bridge_channel, NULL, 0);
}
@ -623,10 +626,15 @@ void unload_parking_bridge_features(void)
int load_parking_bridge_features(void)
{
parking_provider.module_info = parking_get_module_info();
if (ast_parking_register_bridge_features(&parking_provider)) {
return -1;
}
ast_bridge_features_register(AST_BRIDGE_BUILTIN_PARKCALL, feature_park_call, NULL);
if (ast_bridge_features_register(AST_BRIDGE_BUILTIN_PARKCALL, feature_park_call, NULL)) {
return -1;
}
return 0;
}

@ -32,6 +32,7 @@
#define DEFAULT_PARKING_EXTEN "700"
#define BASE_REGISTRAR "res_parking"
#define PARK_DIAL_CONTEXT "park-dial"
#define PARKED_CALL_APPLICATION "ParkedCall"
enum park_call_resolution {
PARK_UNSET = 0, /*! Nothing set a resolution. This should never be observed in practice. */
@ -463,43 +464,18 @@ int parking_dynamic_lots_enabled(void);
/*!
* \since 12.0.0
* \brief Execution function for the parking application
* \brief Register parking applications
*
* \param chan ast_channel entering the application
* \param data arguments to the application
*
* \retval 0 the application executed in such a way that the channel should proceed in the dial plan
* \retval -1 the channel should no longer proceed through the dial plan
*
* \note this function should only be used to register the parking application and not generally to park calls.
*/
int park_app_exec(struct ast_channel *chan, const char *data);
/*!
* \since 12.0.0
* \brief Execution function for the parked call application
*
* \param chan ast_channel entering the application
* \param data arguments to the application
*
* \retval 0 the application executed in such a way that the channel should proceed in the dial plan
* \retval -1 the channel should no longer proceed through the dial plan
* \retval 0 if successful
* \retval -1 on failure
*/
int parked_call_app_exec(struct ast_channel *chan, const char *data);
int load_parking_applications(void);
/*!
* \since 12.0.0
* \brief Execution function for the park and retrieve application
*
* \param chan ast_channel entering the application
* \param data arguments to the application
*
* \retval 0 the application executed in such a way that the channel should proceed in the dial plan
* \retval -1 the channel should no longer proceed through the dial plan
*
* \note this function should only be used to register the park and announce application and not generally to park and announce.
* \brief Unregister parking applications
*/
int park_and_announce_app_exec(struct ast_channel *chan, const char *data);
void unload_parking_applications(void);
/*!
* \since 12.0.0
@ -571,3 +547,12 @@ int load_parking_tests(void);
* \return Nothing
*/
void unload_parking_tests(void);
struct ast_module_info;
/*!
* \since 12.0.0
* \brief Get res_parking's module info
*
* \retval res_parking's ast_module
*/
const struct ast_module_info *parking_get_module_info(void);

@ -33,6 +33,9 @@
<configFile name="res_parking.conf">
<configObject name="globals">
<synopsis>Options that apply to every parking lot</synopsis>
<configOption name="parkeddynamic">
<synopsis>Enables dynamically created parkinglots.</synopsis>
</configOption>
</configObject>
<configObject name="parking_lot">
<synopsis>Defined parking lots for res_parking to use to park calls on</synopsis>
@ -192,9 +195,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/manager.h"
#include "asterisk/pbx.h"
#define PARKED_CALL_APPLICATION "ParkedCall"
#define PARK_AND_ANNOUNCE_APPLICATION "ParkAndAnnounce"
static int parking_lot_sort_fn(const void *obj_left, const void *obj_right, int flags)
{
const struct parking_lot *left = obj_left;
@ -1152,6 +1152,27 @@ static void link_configured_disable_marked_lots(void)
disable_marked_lots();
}
const struct ast_module_info *parking_get_module_info(void)
{
return ast_module_info;
}
static int unload_module(void)
{
unload_parking_bridge_features();
remove_all_configured_parking_lot_extensions();
unload_parking_applications();
unload_parking_manager();
unload_parking_ui();
unload_parking_devstate();
unload_parking_tests();
ao2_cleanup(parking_lot_container);
parking_lot_container = NULL;
aco_info_destroy(&cfg_info);
return 0;
}
static int load_module(void)
{
parking_lot_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX,
@ -1194,15 +1215,7 @@ static int load_module(void)
goto error;
}
if (ast_register_application_xml(PARK_APPLICATION, park_app_exec)) {
goto error;
}
if (ast_register_application_xml(PARKED_CALL_APPLICATION, parked_call_app_exec)) {
goto error;
}
if (ast_register_application_xml(PARK_AND_ANNOUNCE_APPLICATION, park_and_announce_app_exec)) {
if (load_parking_applications()) {
goto error;
}
@ -1229,9 +1242,7 @@ static int load_module(void)
return AST_MODULE_LOAD_SUCCESS;
error:
/* XXX errored loads don't currently do a good job of cleaning up after themselves */
ao2_cleanup(parking_lot_container);
aco_info_destroy(&cfg_info);
unload_module();
return AST_MODULE_LOAD_DECLINE;
}
@ -1244,25 +1255,6 @@ static int reload_module(void)
return 0;
}
static int unload_module(void)
{
/*ast_parking_unregister_bridge_features(parking_provider.module_name);*/
/* XXX Parking is currently not unloadable due to the fact that it loads features which could cause
* significant problems if they disappeared while a channel still had access to them.
*/
return -1;
/* TODO Things we will need to do here:
*
* destroy existing parking lots
* uninstall parking related bridge features
* remove extensions owned by the parking registrar
* unload currently loaded unit tests, CLI/AMI commands, etc.
*/
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Call Parking Resource",
.load = load_module,
.unload = unload_module,

Loading…
Cancel
Save