summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Michelson <mmichelson@digium.com>2016-05-09 14:48:51 -0500
committerMark Michelson <mmichelson@digium.com>2016-05-27 09:08:49 -0500
commit88d997913faabe81f8b9e7bdaa56742be0d669b9 (patch)
treedde0bb91e5840514d684887bb8e5e10864e3dcc3
parentf6c33771f660c3ad15bc554b355cb21e83c85e36 (diff)
ARI: Re-implement the ARI dial command, allowing for early bridging.
ARI dial had been implemented using the Dial API. This made great sense when dialing was 100% separate from bridging. However, if a channel were to be added to a bridge during the dial attempt, there would be a conflict between the dialing thread and the bridging thread. Each would be attempting to read frames from the dialed channel and act on them. The initial attempt to make the two play nice was to have the Dial API suspend the channel in the bridge and stay in charge of the channel until the dial was complete. The problem with this was that it was riddled with potential race conditions. It also was not well-suited for the case where the channel changed which bridge it was in during the dial. This new approach removes the use of the Dial API altogether. Instead, the channel we are dialing is placed into an invisible ARI dialing bridge. The bridge channel thread handles incoming frames from the channel. If the channel is added to a real bridge, it is departed from the invisible bridge and then added to the real bridge. Similarly, if the channel is removed from the real bridge, it is automatically added back to the invisible bridge if the dial attempt is still active. This approach keeps the threading simple by always having the channel being handled by bridge channel threads. ASTERISK-25925 Change-Id: I7750359ddf45fcd45eaec749c5b3822de4a8ddbb
-rw-r--r--include/asterisk/stasis_app.h29
-rw-r--r--res/ari/resource_channels.c139
-rw-r--r--res/res_stasis.c22
-rw-r--r--res/stasis/control.c376
4 files changed, 470 insertions, 96 deletions
diff --git a/include/asterisk/stasis_app.h b/include/asterisk/stasis_app.h
index 0863f9f98..a73461547 100644
--- a/include/asterisk/stasis_app.h
+++ b/include/asterisk/stasis_app.h
@@ -673,6 +673,18 @@ int stasis_app_control_queue_control(struct stasis_app_control *control,
struct ast_bridge *stasis_app_bridge_create(const char *type, const char *name, const char *id);
/*!
+ * \brief Create an invisible bridge of the specified type.
+ *
+ * \param type The type of bridge to be created
+ * \param name Optional name to give to the bridge
+ * \param id Optional Unique ID to give to the bridge
+ *
+ * \return New bridge.
+ * \return \c NULL on error.
+ */
+struct ast_bridge *stasis_app_bridge_create_invisible(const char *type, const char *name, const char *id);
+
+/*!
* \brief Returns the bridge with the given id.
* \param bridge_id Uniqueid of the bridge.
*
@@ -855,20 +867,23 @@ int stasis_app_channel_unreal_set_internal(struct ast_channel *chan);
*/
int stasis_app_channel_set_internal(struct ast_channel *chan);
-struct ast_dial;
-
/*!
* \brief Dial a channel
* \param control Control for \c res_stasis.
- * \param dial The ast_dial for the outbound channel
+ * \param dialstring The dialstring to pass to the channel driver
+ * \param timeout Optional timeout in milliseconds
*/
-int stasis_app_control_dial(struct stasis_app_control *control, struct ast_dial *dial);
+int stasis_app_control_dial(struct stasis_app_control *control,
+ const char *dialstring, unsigned int timeout);
/*!
- * \brief Get dial structure on a control
+ * \brief Let Stasis app internals shut down
+ *
+ * This is called when res_stasis is unloaded. It ensures that
+ * the Stasis app internals can free any resources they may have
+ * allocated during the time that res_stasis was loaded.
*/
-struct ast_dial *stasis_app_get_dial(struct stasis_app_control *control);
-
+void stasis_app_control_shutdown(void);
/*! @} */
#endif /* _ASTERISK_STASIS_APP_H */
diff --git a/res/ari/resource_channels.c b/res/ari/resource_channels.c
index c838bc39c..d0abcfd25 100644
--- a/res/ari/resource_channels.c
+++ b/res/ari/resource_channels.c
@@ -48,6 +48,7 @@ ASTERISK_REGISTER_FILE()
#include "asterisk/format_cache.h"
#include "asterisk/core_local.h"
#include "asterisk/dial.h"
+#include "asterisk/max_forwards.h"
#include "resource_channels.h"
#include <limits.h>
@@ -1500,6 +1501,67 @@ static void *ari_channel_thread(void *data)
return NULL;
}
+struct ast_datastore_info dialstring_info = {
+ .type = "ARI Dialstring",
+ .destroy = ast_free_ptr,
+};
+
+/*!
+ * \brief Save dialstring onto a channel datastore
+ *
+ * This will later be retrieved when it comes time to actually dial the channel
+ *
+ * \param chan The channel on which to save the dialstring
+ * \param dialstring The dialstring to save
+ * \retval 0 SUCCESS!
+ * \reval -1 Failure :(
+ */
+static int save_dialstring(struct ast_channel *chan, const char *dialstring)
+{
+ struct ast_datastore *datastore;
+
+ datastore = ast_datastore_alloc(&dialstring_info, NULL);
+ if (!datastore) {
+ return -1;
+ }
+
+ datastore->data = ast_strdup(dialstring);
+ if (!datastore->data) {
+ ast_datastore_free(datastore);
+ return -1;
+ }
+
+ ast_channel_lock(chan);
+ if (ast_channel_datastore_add(chan, datastore)) {
+ ast_channel_unlock(chan);
+ ast_datastore_free(datastore);
+ return -1;
+ }
+ ast_channel_unlock(chan);
+
+ return 0;
+}
+
+/*!
+ * \brief Retrieve the dialstring from the channel datastore
+ *
+ * \pre chan is locked
+ * \param chan Channel that was previously created in ARI
+ * \retval NULL Failed to find datastore
+ * \retval non-NULL The dialstring
+ */
+static char *restore_dialstring(struct ast_channel *chan)
+{
+ struct ast_datastore *datastore;
+
+ datastore = ast_channel_datastore_find(chan, &dialstring_info, NULL);
+ if (!datastore) {
+ return NULL;
+ }
+
+ return datastore->data;
+}
+
void ast_ari_channels_create(struct ast_variable *headers,
struct ast_ari_channels_create_args *args,
struct ast_ari_response *response)
@@ -1559,6 +1621,12 @@ void ast_ari_channels_create(struct ast_variable *headers,
return;
}
+ if (save_dialstring(chan_data->chan, stuff)) {
+ ast_ari_response_alloc_failed(response);
+ chan_data_destroy(chan_data);
+ return;
+ }
+
snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(chan_data->chan));
if (ast_pthread_create_detached(&thread, NULL, ari_channel_thread, chan_data)) {
@@ -1577,8 +1645,8 @@ void ast_ari_channels_dial(struct ast_variable *headers,
{
RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
RAII_VAR(struct ast_channel *, caller, NULL, ast_channel_cleanup);
- struct ast_channel *callee;
- struct ast_dial *dial;
+ RAII_VAR(struct ast_channel *, callee, NULL, ast_channel_cleanup);
+ char *dialstring;
control = find_control(response, args->channel_id);
if (control == NULL) {
@@ -1595,43 +1663,64 @@ void ast_ari_channels_dial(struct ast_variable *headers,
return;
}
- if (ast_channel_state(callee) != AST_STATE_DOWN) {
- ast_channel_unref(callee);
+ if (ast_channel_state(callee) != AST_STATE_DOWN
+ && ast_channel_state(callee) != AST_STATE_RESERVED) {
ast_ari_response_error(response, 409, "Conflict",
"Channel is not in the 'Down' state");
return;
}
- dial = ast_dial_create();
- if (!dial) {
- ast_channel_unref(callee);
- ast_ari_response_alloc_failed(response);
- return;
+ /* XXX This is straight up copied from main/dial.c. It's probably good
+ * to separate this to some common method.
+ */
+ if (caller) {
+ ast_channel_lock_both(caller, callee);
+ } else {
+ ast_channel_lock(callee);
}
- if (ast_dial_append_channel(dial, callee) < 0) {
- ast_channel_unref(callee);
- ast_dial_destroy(dial);
- ast_ari_response_alloc_failed(response);
+ dialstring = restore_dialstring(callee);
+ if (!dialstring) {
+ ast_channel_unlock(callee);
+ if (caller) {
+ ast_channel_unlock(caller);
+ }
+ ast_ari_response_error(response, 409, "Conflict",
+ "Dialing a channel not created by ARI");
return;
}
-
- /* From this point, we don't have to unref the callee channel on
- * failure paths because the dial owns the reference to the called
- * channel and will unref the channel for us
+ /* Make a copy of the dialstring just in case some jerk tries to hang up the
+ * channel before we can actually dial
*/
+ dialstring = ast_strdupa(dialstring);
- if (ast_dial_prerun(dial, caller, NULL)) {
- ast_dial_destroy(dial);
- ast_ari_response_alloc_failed(response);
- return;
+ ast_channel_stage_snapshot(callee);
+ if (caller) {
+ ast_channel_inherit_variables(caller, callee);
+ ast_channel_datastore_inherit(caller, callee);
+ ast_max_forwards_decrement(callee);
+
+ /* Copy over callerid information */
+ ast_party_redirecting_copy(ast_channel_redirecting(callee), ast_channel_redirecting(caller));
+
+ ast_channel_dialed(callee)->transit_network_select = ast_channel_dialed(caller)->transit_network_select;
+
+ ast_connected_line_copy_from_caller(ast_channel_connected(callee), ast_channel_caller(caller));
+
+ ast_channel_language_set(callee, ast_channel_language(caller));
+ ast_channel_req_accountcodes(callee, caller, AST_CHANNEL_REQUESTOR_BRIDGE_PEER);
+ if (ast_strlen_zero(ast_channel_musicclass(callee)))
+ ast_channel_musicclass_set(callee, ast_channel_musicclass(caller));
+
+ ast_channel_adsicpe_set(callee, ast_channel_adsicpe(caller));
+ ast_channel_transfercapability_set(callee, ast_channel_transfercapability(caller));
+ ast_channel_unlock(caller);
}
- ast_dial_set_user_data(dial, control);
- ast_dial_set_global_timeout(dial, args->timeout * 1000);
+ ast_channel_stage_snapshot_done(callee);
+ ast_channel_unlock(callee);
- if (stasis_app_control_dial(control, dial)) {
- ast_dial_destroy(dial);
+ if (stasis_app_control_dial(control, dialstring, args->timeout)) {
ast_ari_response_alloc_failed(response);
return;
}
diff --git a/res/res_stasis.c b/res/res_stasis.c
index 346be563c..e7e6bcaa3 100644
--- a/res/res_stasis.c
+++ b/res/res_stasis.c
@@ -749,7 +749,7 @@ static void control_unlink(struct stasis_app_control *control)
ao2_cleanup(control);
}
-struct ast_bridge *stasis_app_bridge_create(const char *type, const char *name, const char *id)
+static struct ast_bridge *bridge_create_common(const char *type, const char *name, const char *id, int invisible)
{
struct ast_bridge *bridge;
char *requested_type, *requested_types = ast_strdupa(S_OR(type, "mixing"));
@@ -758,6 +758,10 @@ struct ast_bridge *stasis_app_bridge_create(const char *type, const char *name,
| AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM | AST_BRIDGE_FLAG_SWAP_INHIBIT_TO
| AST_BRIDGE_FLAG_TRANSFER_BRIDGE_ONLY;
+ if (invisible) {
+ flags |= AST_BRIDGE_FLAG_INVISIBLE;
+ }
+
while ((requested_type = strsep(&requested_types, ","))) {
requested_type = ast_strip(requested_type);
@@ -789,6 +793,16 @@ struct ast_bridge *stasis_app_bridge_create(const char *type, const char *name,
return bridge;
}
+struct ast_bridge *stasis_app_bridge_create(const char *type, const char *name, const char *id)
+{
+ return bridge_create_common(type, name, id, 0);
+}
+
+struct ast_bridge *stasis_app_bridge_create_invisible(const char *type, const char *name, const char *id)
+{
+ return bridge_create_common(type, name, id, 1);
+}
+
void stasis_app_bridge_destroy(const char *bridge_id)
{
struct ast_bridge *bridge = stasis_app_bridge_find_by_id(bridge_id);
@@ -1287,7 +1301,6 @@ int stasis_app_exec(struct ast_channel *chan, const char *app_name, int argc,
int r;
int command_count;
RAII_VAR(struct ast_bridge *, last_bridge, NULL, ao2_cleanup);
- struct ast_dial *dial;
/* Check to see if a bridge absorbed our hangup frame */
if (ast_check_hangup_locked(chan)) {
@@ -1297,7 +1310,6 @@ int stasis_app_exec(struct ast_channel *chan, const char *app_name, int argc,
last_bridge = bridge;
bridge = ao2_bump(stasis_app_get_bridge(control));
- dial = stasis_app_get_dial(control);
if (bridge != last_bridge) {
app_unsubscribe_bridge(app, last_bridge);
@@ -1306,7 +1318,7 @@ int stasis_app_exec(struct ast_channel *chan, const char *app_name, int argc,
}
}
- if (bridge || dial) {
+ if (bridge) {
/* Bridge/dial is handling channel frames */
control_wait(control);
control_dispatch_all(control, chan);
@@ -1951,6 +1963,8 @@ static int unload_module(void)
ao2_cleanup(app_bridges_playback);
app_bridges_playback = NULL;
+ stasis_app_control_shutdown();
+
STASIS_MESSAGE_TYPE_CLEANUP(end_message_type);
STASIS_MESSAGE_TYPE_CLEANUP(start_message_type);
diff --git a/res/stasis/control.c b/res/stasis/control.c
index aa6866aee..b255477bf 100644
--- a/res/stasis/control.c
+++ b/res/stasis/control.c
@@ -28,6 +28,7 @@
ASTERISK_REGISTER_FILE()
#include "asterisk/stasis_channels.h"
+#include "asterisk/stasis_app.h"
#include "command.h"
#include "control.h"
@@ -43,6 +44,11 @@ ASTERISK_REGISTER_FILE()
AST_LIST_HEAD(app_control_rules, stasis_app_control_rule);
+/*!
+ * \brief Indicates if the Stasis app internals are being shut down
+ */
+static int shutting_down;
+
struct stasis_app_control {
ast_cond_t wait_cond;
/*! Queue of commands to dispatch on the channel */
@@ -78,10 +84,6 @@ struct stasis_app_control {
*/
struct stasis_app *app;
/*!
- * If channel is being dialed, the dial structure.
- */
- struct ast_dial *dial;
- /*!
* When set, /c app_stasis should exit and continue in the dialplan.
*/
int is_done:1;
@@ -825,6 +827,128 @@ struct ast_bridge *stasis_app_get_bridge(struct stasis_app_control *control)
}
}
+/*!
+ * \brief Singleton dial bridge
+ *
+ * The dial bridge is a holding bridge used to hold all
+ * outbound dialed channels that are not in any "real" ARI-created
+ * bridge. The dial bridge is invisible, meaning that it does not
+ * show up in channel snapshots, AMI or ARI output, and no events
+ * get raised for it.
+ *
+ * This is used to keep dialed channels confined to the bridging system
+ * and unify the threading model used for dialing outbound channels.
+ */
+static struct ast_bridge *dial_bridge;
+AST_MUTEX_DEFINE_STATIC(dial_bridge_lock);
+
+/*!
+ * \brief Retrieve a reference to the dial bridge.
+ *
+ * If the dial bridge has not been created yet, it will
+ * be created, otherwise, a reference to the existing bridge
+ * will be returned.
+ *
+ * The caller will need to unreference the dial bridge once
+ * they are finished with it.
+ *
+ * \retval NULL Unable to find/create the dial bridge
+ * \retval non-NULL A reference to teh dial bridge
+ */
+static struct ast_bridge *get_dial_bridge(void)
+{
+ struct ast_bridge *ret_bridge = NULL;
+
+ ast_mutex_lock(&dial_bridge_lock);
+
+ if (shutting_down) {
+ goto end;
+ }
+
+ if (dial_bridge) {
+ ret_bridge = ao2_bump(dial_bridge);
+ goto end;
+ }
+
+ dial_bridge = stasis_app_bridge_create_invisible("holding", "dial_bridge", NULL);
+ if (!dial_bridge) {
+ goto end;
+ }
+ ret_bridge = ao2_bump(dial_bridge);
+
+end:
+ ast_mutex_unlock(&dial_bridge_lock);
+ return ret_bridge;
+}
+
+/*!
+ * \brief after bridge callback for the dial bridge
+ *
+ * The only purpose of this callback is to ensure that the control structure's
+ * bridge pointer is NULLed
+ */
+static void dial_bridge_after_cb(struct ast_channel *chan, void *data)
+{
+ struct stasis_app_control *control = data;
+
+ control->bridge = NULL;
+}
+
+static void dial_bridge_after_cb_failed(enum ast_bridge_after_cb_reason reason, void *data)
+{
+ struct stasis_app_control *control = data;
+
+ dial_bridge_after_cb(control->channel, data);
+}
+
+/*!
+ * \brief Add a channel to the singleton dial bridge.
+ *
+ * \param control The Stasis control structure
+ * \param chan The channel to add to the bridge
+ * \retval -1 Failed
+ * \retval 0 Success
+ */
+static int add_to_dial_bridge(struct stasis_app_control *control, struct ast_channel *chan)
+{
+ struct ast_bridge *bridge;
+
+ bridge = get_dial_bridge();
+ if (!bridge) {
+ return -1;
+ }
+
+ control->bridge = bridge;
+ ast_bridge_set_after_callback(chan, dial_bridge_after_cb, dial_bridge_after_cb_failed, control);
+ if (ast_bridge_impart(bridge, chan, NULL, NULL, AST_BRIDGE_IMPART_CHAN_DEPARTABLE)) {
+ control->bridge = NULL;
+ ao2_ref(bridge, -1);
+ return -1;
+ }
+
+ ao2_ref(bridge, -1);
+
+ return 0;
+}
+
+/*!
+ * \brief Depart a channel from a bridge, and potentially add it back to the dial bridge
+ *
+ * \param control Take a guess
+ * \param chan Take another guess
+ */
+static int depart_channel(struct stasis_app_control *control, struct ast_channel *chan)
+{
+ ast_bridge_depart(chan);
+
+ if (!ast_check_hangup(chan) && ast_channel_state(chan) != AST_STATE_UP) {
+ /* Channel is still being dialed, so put it back in the dialing bridge */
+ add_to_dial_bridge(control, chan);
+ }
+
+ return 0;
+}
+
static int bridge_channel_depart(struct stasis_app_control *control,
struct ast_channel *chan, void *data)
{
@@ -843,7 +967,7 @@ static int bridge_channel_depart(struct stasis_app_control *control,
ast_debug(3, "%s: Channel departing bridge\n",
ast_channel_uniqueid(chan));
- ast_bridge_depart(chan);
+ depart_channel(control, chan);
return 0;
}
@@ -903,6 +1027,107 @@ static void bridge_after_cb_failed(enum ast_bridge_after_cb_reason reason,
ast_bridge_after_cb_reason_string(reason));
}
+/*!
+ * \brief Dial timeout datastore
+ *
+ * A datastore is used because a channel may change
+ * bridges during the course of a dial attempt. This
+ * may be because the channel changes from the dial bridge
+ * to a standard bridge, or it may move between standard
+ * bridges. In order to keep the dial timeout, we need
+ * to keep the timeout information local to the channel.
+ * That is what this datastore is for
+ */
+struct ast_datastore_info timeout_datastore = {
+ .type = "ARI dial timeout",
+};
+
+static int hangup_channel(struct stasis_app_control *control,
+ struct ast_channel *chan, void *data)
+{
+ ast_softhangup(chan, AST_SOFTHANGUP_EXPLICIT);
+ return 0;
+}
+
+/*!
+ * \brief Dial timeout
+ *
+ * This is a bridge interval hook callback. The interval hook triggering
+ * means that the dial timeout has been reached. If the channel has not
+ * been answered by the time this callback is called, then the channel
+ * is hung up
+ *
+ * \param bridge_channel Bridge channel on which interval hook has been called
+ * \param ignore Ignored
+ * \return -1 (i.e. remove the interval hook)
+ */
+static int bridge_timeout(struct ast_bridge_channel *bridge_channel, void *ignore)
+{
+ struct ast_datastore *datastore;
+ RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
+
+ control = stasis_app_control_find_by_channel(bridge_channel->chan);
+
+ ast_channel_lock(bridge_channel->chan);
+ if (ast_channel_state(bridge_channel->chan) != AST_STATE_UP) {
+ /* Don't bother removing the datastore because it will happen when the channel is hung up */
+ ast_channel_unlock(bridge_channel->chan);
+ stasis_app_send_command_async(control, hangup_channel, NULL, NULL);
+ return -1;
+ }
+
+ datastore = ast_channel_datastore_find(bridge_channel->chan, &timeout_datastore, NULL);
+ if (!datastore) {
+ ast_channel_unlock(bridge_channel->chan);
+ return -1;
+ }
+ ast_channel_datastore_remove(bridge_channel->chan, datastore);
+ ast_channel_unlock(bridge_channel->chan);
+ ast_datastore_free(datastore);
+
+ return -1;
+}
+
+/*!
+ * \brief Set a dial timeout interval hook on the channel.
+ *
+ * The absolute time that the timeout should occur is stored on
+ * a datastore on the channel. This time is converted into a relative
+ * number of milliseconds in the future. Then an interval hook is set
+ * to trigger in that number of milliseconds.
+ *
+ * \pre chan is locked
+ *
+ * \param chan The channel on which to set the interval hook
+ */
+static void set_interval_hook(struct ast_channel *chan)
+{
+ struct ast_datastore *datastore;
+ struct timeval *hangup_time;
+ int64_t ms;
+ struct ast_bridge_channel *bridge_channel;
+
+ datastore = ast_channel_datastore_find(chan, &timeout_datastore, NULL);
+ if (!datastore) {
+ return;
+ }
+
+ hangup_time = datastore->data;
+
+ ms = ast_tvdiff_ms(*hangup_time, ast_tvnow());
+ bridge_channel = ast_channel_get_bridge_channel(chan);
+ if (!bridge_channel) {
+ return;
+ }
+
+ if (ast_bridge_interval_hook(bridge_channel->features, 0, ms > 0 ? ms : 1,
+ bridge_timeout, NULL, NULL, 0)) {
+ return;
+ }
+
+ ast_queue_frame(bridge_channel->chan, &ast_null_frame);
+}
+
int control_swap_channel_in_bridge(struct stasis_app_control *control, struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap)
{
int res;
@@ -969,6 +1194,10 @@ int control_swap_channel_in_bridge(struct stasis_app_control *control, struct as
ast_assert(stasis_app_get_bridge(control) == NULL);
control->bridge = bridge;
+
+ ast_channel_lock(chan);
+ set_interval_hook(chan);
+ ast_channel_unlock(chan);
}
return 0;
}
@@ -1011,7 +1240,7 @@ static int app_control_remove_channel_from_bridge(
return -1;
}
- ast_bridge_depart(chan);
+ depart_channel(control, chan);
return 0;
}
@@ -1132,83 +1361,110 @@ struct stasis_app *control_app(struct stasis_app_control *control)
return control->app;
}
-static void app_control_dial_destroy(void *data)
+struct control_dial_args {
+ unsigned int timeout;
+ char dialstring[0];
+};
+
+static struct control_dial_args *control_dial_args_alloc(const char *dialstring,
+ unsigned int timeout)
{
- struct ast_dial *dial = data;
+ struct control_dial_args *args;
+
+ args = ast_malloc(sizeof(*args) + strlen(dialstring) + 1);
+ if (!args) {
+ return NULL;
+ }
+
+ args->timeout = timeout;
+ /* Safe */
+ strcpy(args->dialstring, dialstring);
- ast_dial_join(dial);
- ast_dial_destroy(dial);
+ return args;
}
-static int app_control_remove_dial(struct stasis_app_control *control,
- struct ast_channel *chan, void *data)
+static void control_dial_args_destroy(void *data)
{
- if (ast_dial_state(control->dial) != AST_DIAL_RESULT_ANSWERED) {
- ast_softhangup(chan, AST_SOFTHANGUP_EXPLICIT);
- }
- control->dial = NULL;
- return 0;
+ struct control_dial_args *args = data;
+
+ ast_free(args);
}
-static void on_dial_state(struct ast_dial *dial)
+/*!
+ * \brief Set dial timeout on a channel to be dialed.
+ *
+ * \param chan The channel on which to set the dial timeout
+ * \param timeout The timeout in seconds
+ */
+static int set_timeout(struct ast_channel *chan, unsigned int timeout)
{
- enum ast_dial_result state;
- struct stasis_app_control *control;
- struct ast_channel *chan;
+ struct ast_datastore *datastore;
+ struct timeval *hangup_time;
- state = ast_dial_state(dial);
- control = ast_dial_get_user_data(dial);
+ hangup_time = ast_malloc(sizeof(struct timeval));
- switch (state) {
- case AST_DIAL_RESULT_ANSWERED:
- /* Need to steal the reference to the answered channel so that dial doesn't
- * try to hang it up when we destroy the dial structure.
- */
- chan = ast_dial_answered_steal(dial);
- ast_channel_unref(chan);
- /* Fall through intentionally */
- case AST_DIAL_RESULT_INVALID:
- case AST_DIAL_RESULT_FAILED:
- case AST_DIAL_RESULT_TIMEOUT:
- case AST_DIAL_RESULT_HANGUP:
- case AST_DIAL_RESULT_UNANSWERED:
- /* The dial has completed, so we need to break the Stasis loop so
- * that the channel's frames are handled in the proper place now.
- */
- stasis_app_send_command_async(control, app_control_remove_dial, dial, app_control_dial_destroy);
- break;
- case AST_DIAL_RESULT_TRYING:
- case AST_DIAL_RESULT_RINGING:
- case AST_DIAL_RESULT_PROGRESS:
- case AST_DIAL_RESULT_PROCEEDING:
- break;
+ datastore = ast_datastore_alloc(&timeout_datastore, NULL);
+ if (!datastore) {
+ return -1;
+ }
+ *hangup_time = ast_tvadd(ast_tvnow(), ast_samp2tv(timeout, 1));
+ datastore->data = hangup_time;
+
+ ast_channel_lock(chan);
+ ast_channel_datastore_add(chan, datastore);
+
+ if (ast_channel_is_bridged(chan)) {
+ set_interval_hook(chan);
}
+ ast_channel_unlock(chan);
+
+ return 0;
}
static int app_control_dial(struct stasis_app_control *control,
struct ast_channel *chan, void *data)
{
- struct ast_dial *dial = data;
+ struct control_dial_args *args = data;
+ int bridged;
- ast_dial_set_state_callback(dial, on_dial_state);
- /* The dial API gives the option of providing a caller channel, but for
- * Stasis, we really don't want to do that. The Dial API will take liberties such
- * as passing frames along to the calling channel (think ringing, progress, etc.).
- * This is not desirable in ARI applications since application writers should have
- * control over what does/does not get indicated to the calling channel
- */
- ast_dial_run(dial, NULL, 1);
- control->dial = dial;
+ ast_channel_lock(chan);
+ bridged = ast_channel_is_bridged(chan);
+ ast_channel_unlock(chan);
+
+ if (!bridged && add_to_dial_bridge(control, chan)) {
+ return -1;
+ }
+
+ if (args->timeout && set_timeout(chan, args->timeout)) {
+ return -1;
+ }
+
+ if (ast_call(chan, args->dialstring, 0)) {
+ return -1;
+ }
return 0;
}
-struct ast_dial *stasis_app_get_dial(struct stasis_app_control *control)
+int stasis_app_control_dial(struct stasis_app_control *control,
+ const char *dialstring, unsigned int timeout)
{
- return control->dial;
+ struct control_dial_args *args;
+
+ args = control_dial_args_alloc(dialstring, timeout);
+ if (!args) {
+ return -1;
+ }
+
+ return stasis_app_send_command_async(control, app_control_dial,
+ args, control_dial_args_destroy);
}
-int stasis_app_control_dial(struct stasis_app_control *control, struct ast_dial *dial)
+void stasis_app_control_shutdown(void)
{
- return stasis_app_send_command_async(control, app_control_dial, dial, NULL);
+ ast_mutex_lock(&dial_bridge_lock);
+ shutting_down = 1;
+ ao2_cleanup(dial_bridge);
+ dial_bridge = NULL;
+ ast_mutex_unlock(&dial_bridge_lock);
}