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);
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)
} 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) {
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;
{
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;
}
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;
{
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;
}
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;
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();
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) {
} 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) {
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);
#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
* \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
* \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);
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;
}
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;
}
*/
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",
#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);
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) {
if (wrapper) {
ast_log(AST_LOG_WARNING, "Parking provider already registered by %s!\n",
- wrapper->fn_table.module_name);
+ wrapper->module_name);
return -1;
}
if (!wrapper) {
return -1;
}
- wrapper->fn_table = *fn_table;
+ *wrapper = *fn_table;
ao2_global_obj_replace(parking_provider, wrapper);
return 0;
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;
}
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;
+}
</application>
***/
+#define PARK_AND_ANNOUNCE_APPLICATION "ParkAndAnnounce"
+
/* Park a call */
enum park_args {
}
-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);
/* 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 */
*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;
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);
+}
#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 {
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);
}
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;
}
#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. */
/*!
* \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
* \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);
<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>
#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;
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,
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;
}
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;
}
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,