2013-05-14 21:45:08 +00:00
|
|
|
/*
|
|
|
|
* Asterisk -- An open source telephony toolkit.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2013, Digium, Inc.
|
|
|
|
*
|
|
|
|
* David M. Lee, II <dlee@digium.com>
|
|
|
|
*
|
|
|
|
* See http://www.asterisk.org for more information about
|
|
|
|
* the Asterisk project. Please do not directly contact
|
|
|
|
* any of the maintainers of this project for assistance;
|
|
|
|
* the project provides a web site, mailing lists and IRC
|
|
|
|
* channels for your use.
|
|
|
|
*
|
|
|
|
* This program is free software, distributed under the terms of
|
|
|
|
* the GNU General Public License Version 2. See the LICENSE file
|
|
|
|
* at the top of the source tree.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*! \file
|
|
|
|
*
|
|
|
|
* \brief Stasis application control support.
|
|
|
|
*
|
|
|
|
* \author David M. Lee, II <dlee@digium.com>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "asterisk.h"
|
|
|
|
|
git migration: Refactor the ASTERISK_FILE_VERSION macro
Git does not support the ability to replace a token with a version
string during check-in. While it does have support for replacing a
token on clone, this is somewhat sub-optimal: the token is replaced
with the object hash, which is not particularly easy for human
consumption. What's more, in practice, the source file version was often
not terribly useful. Generally, when triaging bugs, the overall version
of Asterisk is far more useful than an individual SVN version of a file. As a
result, this patch removes Asterisk's support for showing source file
versions.
Specifically, it does the following:
* Rename ASTERISK_FILE_VERSION macro to ASTERISK_REGISTER_FILE, and
remove passing the version in with the macro. Other facilities
than 'core show file version' make use of the file names, such as
setting a debug level only on a specific file. As such, the act of
registering source files with the Asterisk core still has use. The
macro rename now reflects the new macro purpose.
* main/asterisk:
- Refactor the file_version structure to reflect that it no longer
tracks a version field.
- Remove the "core show file version" CLI command. Without the file
version, it is no longer useful.
- Remove the ast_file_version_find function. The file version is no
longer tracked.
- Rename ast_register_file_version/ast_unregister_file_version to
ast_register_file/ast_unregister_file, respectively.
* main/manager: Remove value from the Version key of the ModuleCheck
Action. The actual key itself has not been removed, as doing so would
absolutely constitute a backwards incompatible change. However, since
the file version is no longer tracked, there is no need to attempt to
include it in the Version key.
* UPGRADE: Add notes for:
- Modification to the ModuleCheck AMI Action
- Removal of the "core show file version" CLI command
Change-Id: I6cf0ff280e1668bf4957dc21f32a5ff43444a40e
2015-04-12 02:38:22 +00:00
|
|
|
ASTERISK_REGISTER_FILE()
|
2013-05-14 21:45:08 +00:00
|
|
|
|
|
|
|
#include "asterisk/stasis_channels.h"
|
|
|
|
|
|
|
|
#include "command.h"
|
|
|
|
#include "control.h"
|
2014-03-13 19:33:22 +00:00
|
|
|
#include "app.h"
|
2013-07-01 18:19:15 +00:00
|
|
|
#include "asterisk/dial.h"
|
2013-07-25 04:06:32 +00:00
|
|
|
#include "asterisk/bridge.h"
|
2013-08-13 15:27:32 +00:00
|
|
|
#include "asterisk/bridge_after.h"
|
2013-07-25 04:06:32 +00:00
|
|
|
#include "asterisk/bridge_basic.h"
|
2013-07-18 16:03:12 +00:00
|
|
|
#include "asterisk/frame.h"
|
2013-06-26 19:29:57 +00:00
|
|
|
#include "asterisk/pbx.h"
|
2013-07-19 19:40:27 +00:00
|
|
|
#include "asterisk/musiconhold.h"
|
2013-11-01 14:38:21 +00:00
|
|
|
#include "asterisk/app.h"
|
2013-05-14 21:45:08 +00:00
|
|
|
|
2013-12-13 16:38:57 +00:00
|
|
|
AST_LIST_HEAD(app_control_rules, stasis_app_control_rule);
|
|
|
|
|
2013-05-14 21:45:08 +00:00
|
|
|
struct stasis_app_control {
|
2013-08-13 15:27:32 +00:00
|
|
|
ast_cond_t wait_cond;
|
2013-05-14 21:45:08 +00:00
|
|
|
/*! Queue of commands to dispatch on the channel */
|
|
|
|
struct ao2_container *command_queue;
|
|
|
|
/*!
|
|
|
|
* The associated channel.
|
|
|
|
* Be very careful with the threading associated w/ manipulating
|
|
|
|
* the channel.
|
|
|
|
*/
|
|
|
|
struct ast_channel *channel;
|
2013-08-13 15:27:32 +00:00
|
|
|
/*!
|
|
|
|
* When a channel is in a bridge, the bridge that it is in.
|
|
|
|
*/
|
|
|
|
struct ast_bridge *bridge;
|
|
|
|
/*!
|
|
|
|
* Holding place for channel's PBX while imparted to a bridge.
|
|
|
|
*/
|
|
|
|
struct ast_pbx *pbx;
|
2013-12-13 16:38:57 +00:00
|
|
|
/*!
|
|
|
|
* A list of rules to check before adding a channel to a bridge.
|
|
|
|
*/
|
|
|
|
struct app_control_rules add_rules;
|
|
|
|
/*!
|
|
|
|
* A list of rules to check before removing a channel from a bridge.
|
|
|
|
*/
|
|
|
|
struct app_control_rules remove_rules;
|
2013-11-21 15:56:34 +00:00
|
|
|
/*!
|
|
|
|
* Silence generator, when silence is being generated.
|
|
|
|
*/
|
|
|
|
struct ast_silence_generator *silgen;
|
2014-03-13 19:33:22 +00:00
|
|
|
/*!
|
|
|
|
* The app for which this control was created
|
|
|
|
*/
|
|
|
|
struct stasis_app *app;
|
2013-08-13 15:27:32 +00:00
|
|
|
/*!
|
|
|
|
* When set, /c app_stasis should exit and continue in the dialplan.
|
|
|
|
*/
|
|
|
|
int is_done:1;
|
2013-05-14 21:45:08 +00:00
|
|
|
};
|
|
|
|
|
2013-08-13 15:27:32 +00:00
|
|
|
static void control_dtor(void *obj)
|
|
|
|
{
|
|
|
|
struct stasis_app_control *control = obj;
|
|
|
|
|
2013-12-13 16:38:57 +00:00
|
|
|
AST_LIST_HEAD_DESTROY(&control->add_rules);
|
|
|
|
AST_LIST_HEAD_DESTROY(&control->remove_rules);
|
|
|
|
|
2013-11-21 15:56:34 +00:00
|
|
|
/* We may have a lingering silence generator; free it */
|
|
|
|
ast_channel_stop_silence_generator(control->channel, control->silgen);
|
|
|
|
control->silgen = NULL;
|
|
|
|
|
2013-08-13 15:27:32 +00:00
|
|
|
ao2_cleanup(control->command_queue);
|
|
|
|
ast_cond_destroy(&control->wait_cond);
|
2014-03-13 19:33:22 +00:00
|
|
|
ao2_cleanup(control->app);
|
2013-08-13 15:27:32 +00:00
|
|
|
}
|
|
|
|
|
2014-03-13 19:33:22 +00:00
|
|
|
struct stasis_app_control *control_create(struct ast_channel *channel, struct stasis_app *app)
|
2013-05-14 21:45:08 +00:00
|
|
|
{
|
2013-08-13 15:27:32 +00:00
|
|
|
RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
|
|
|
|
int res;
|
2013-05-14 21:45:08 +00:00
|
|
|
|
2013-08-13 15:27:32 +00:00
|
|
|
control = ao2_alloc(sizeof(*control), control_dtor);
|
2013-05-14 21:45:08 +00:00
|
|
|
if (!control) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-03-13 19:33:22 +00:00
|
|
|
control->app = ao2_bump(app);
|
|
|
|
|
2013-08-13 15:27:32 +00:00
|
|
|
res = ast_cond_init(&control->wait_cond, NULL);
|
|
|
|
if (res != 0) {
|
|
|
|
ast_log(LOG_ERROR, "Error initializing ast_cond_t: %s\n",
|
|
|
|
strerror(errno));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-05-23 20:11:35 +00:00
|
|
|
control->command_queue = ao2_container_alloc_list(
|
|
|
|
AO2_ALLOC_OPT_LOCK_MUTEX, 0, NULL, NULL);
|
2013-05-14 21:45:08 +00:00
|
|
|
|
2013-07-19 19:35:21 +00:00
|
|
|
if (!control->command_queue) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-05-14 21:45:08 +00:00
|
|
|
control->channel = channel;
|
|
|
|
|
2013-12-13 16:38:57 +00:00
|
|
|
AST_LIST_HEAD_INIT(&control->add_rules);
|
|
|
|
AST_LIST_HEAD_INIT(&control->remove_rules);
|
|
|
|
|
2013-08-13 15:27:32 +00:00
|
|
|
ao2_ref(control, +1);
|
2013-05-14 21:45:08 +00:00
|
|
|
return control;
|
|
|
|
}
|
|
|
|
|
2013-12-13 16:38:57 +00:00
|
|
|
static void app_control_register_rule(
|
2015-06-02 17:27:28 +00:00
|
|
|
struct stasis_app_control *control,
|
2013-12-13 16:38:57 +00:00
|
|
|
struct app_control_rules *list, struct stasis_app_control_rule *obj)
|
|
|
|
{
|
|
|
|
SCOPED_AO2LOCK(lock, control->command_queue);
|
|
|
|
AST_LIST_INSERT_TAIL(list, obj, next);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void app_control_unregister_rule(
|
2015-06-02 17:27:28 +00:00
|
|
|
struct stasis_app_control *control,
|
2013-12-13 16:38:57 +00:00
|
|
|
struct app_control_rules *list, struct stasis_app_control_rule *obj)
|
|
|
|
{
|
|
|
|
struct stasis_app_control_rule *rule;
|
|
|
|
SCOPED_AO2LOCK(lock, control->command_queue);
|
|
|
|
AST_RWLIST_TRAVERSE_SAFE_BEGIN(list, rule, next) {
|
|
|
|
if (rule == obj) {
|
|
|
|
AST_RWLIST_REMOVE_CURRENT(next);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
AST_RWLIST_TRAVERSE_SAFE_END;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
* \brief Checks to make sure each rule in the given list passes.
|
|
|
|
*
|
|
|
|
* \details Loops over a list of rules checking for rejections or failures.
|
|
|
|
* If one rule fails its resulting error code is returned.
|
|
|
|
*
|
|
|
|
* \note Command queue should be locked before calling this function.
|
|
|
|
*
|
|
|
|
* \param control The stasis application control
|
|
|
|
* \param list The list of rules to check
|
|
|
|
*
|
|
|
|
* \retval 0 if all rules pass
|
|
|
|
* \retval non-zero error code if a rule fails
|
|
|
|
*/
|
|
|
|
static enum stasis_app_control_channel_result app_control_check_rules(
|
|
|
|
const struct stasis_app_control *control,
|
|
|
|
struct app_control_rules *list)
|
|
|
|
{
|
|
|
|
int res = 0;
|
|
|
|
struct stasis_app_control_rule *rule;
|
|
|
|
AST_LIST_TRAVERSE(list, rule, next) {
|
|
|
|
if ((res = rule->check_rule(control))) {
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
void stasis_app_control_register_add_rule(
|
|
|
|
struct stasis_app_control *control,
|
|
|
|
struct stasis_app_control_rule *rule)
|
|
|
|
{
|
|
|
|
return app_control_register_rule(control, &control->add_rules, rule);
|
|
|
|
}
|
|
|
|
|
|
|
|
void stasis_app_control_unregister_add_rule(
|
|
|
|
struct stasis_app_control *control,
|
|
|
|
struct stasis_app_control_rule *rule)
|
|
|
|
{
|
|
|
|
app_control_unregister_rule(control, &control->add_rules, rule);
|
|
|
|
}
|
|
|
|
|
|
|
|
void stasis_app_control_register_remove_rule(
|
|
|
|
struct stasis_app_control *control,
|
|
|
|
struct stasis_app_control_rule *rule)
|
|
|
|
{
|
|
|
|
return app_control_register_rule(control, &control->remove_rules, rule);
|
|
|
|
}
|
|
|
|
|
|
|
|
void stasis_app_control_unregister_remove_rule(
|
|
|
|
struct stasis_app_control *control,
|
|
|
|
struct stasis_app_control_rule *rule)
|
|
|
|
{
|
|
|
|
app_control_unregister_rule(control, &control->remove_rules, rule);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int app_control_can_add_channel_to_bridge(
|
|
|
|
struct stasis_app_control *control)
|
|
|
|
{
|
|
|
|
return app_control_check_rules(control, &control->add_rules);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int app_control_can_remove_channel_from_bridge(
|
|
|
|
struct stasis_app_control *control)
|
|
|
|
{
|
|
|
|
return app_control_check_rules(control, &control->remove_rules);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int noop_cb(struct stasis_app_control *control,
|
2013-08-13 15:27:32 +00:00
|
|
|
struct ast_channel *chan, void *data)
|
|
|
|
{
|
2013-12-13 16:38:57 +00:00
|
|
|
return 0;
|
2013-08-13 15:27:32 +00:00
|
|
|
}
|
|
|
|
|
2013-12-13 16:38:57 +00:00
|
|
|
/*! Callback type to see if the command can execute
|
|
|
|
note: command_queue is locked during callback */
|
|
|
|
typedef int (*app_command_can_exec_cb)(struct stasis_app_control *control);
|
2013-08-13 15:27:32 +00:00
|
|
|
|
2013-12-13 16:38:57 +00:00
|
|
|
static struct stasis_app_command *exec_command_on_condition(
|
2013-05-14 21:45:08 +00:00
|
|
|
struct stasis_app_control *control, stasis_app_command_cb command_fn,
|
2014-08-22 16:52:51 +00:00
|
|
|
void *data, command_data_destructor_fn data_destructor,
|
|
|
|
app_command_can_exec_cb can_exec_fn)
|
2013-05-14 21:45:08 +00:00
|
|
|
{
|
2013-12-13 16:38:57 +00:00
|
|
|
int retval;
|
|
|
|
struct stasis_app_command *command;
|
2013-05-14 21:45:08 +00:00
|
|
|
|
2013-08-13 15:27:32 +00:00
|
|
|
command_fn = command_fn ? : noop_cb;
|
|
|
|
|
2014-08-22 16:52:51 +00:00
|
|
|
command = command_create(command_fn, data, data_destructor);
|
2013-05-14 21:45:08 +00:00
|
|
|
if (!command) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-08-13 15:27:32 +00:00
|
|
|
ao2_lock(control->command_queue);
|
2013-12-13 16:38:57 +00:00
|
|
|
if (can_exec_fn && (retval = can_exec_fn(control))) {
|
|
|
|
ao2_unlock(control->command_queue);
|
|
|
|
command_complete(command, retval);
|
|
|
|
return command;
|
|
|
|
}
|
|
|
|
|
2013-08-13 15:27:32 +00:00
|
|
|
ao2_link_flags(control->command_queue, command, OBJ_NOLOCK);
|
|
|
|
ast_cond_signal(&control->wait_cond);
|
|
|
|
ao2_unlock(control->command_queue);
|
2013-05-14 21:45:08 +00:00
|
|
|
|
|
|
|
return command;
|
|
|
|
}
|
|
|
|
|
2013-12-13 16:38:57 +00:00
|
|
|
static struct stasis_app_command *exec_command(
|
|
|
|
struct stasis_app_control *control, stasis_app_command_cb command_fn,
|
2014-08-22 16:52:51 +00:00
|
|
|
void *data, command_data_destructor_fn data_destructor)
|
2013-12-13 16:38:57 +00:00
|
|
|
{
|
2014-08-22 16:52:51 +00:00
|
|
|
return exec_command_on_condition(control, command_fn, data, data_destructor, NULL);
|
2013-12-13 16:38:57 +00:00
|
|
|
}
|
|
|
|
|
2013-07-01 18:19:15 +00:00
|
|
|
struct stasis_app_control_dial_data {
|
|
|
|
char endpoint[AST_CHANNEL_NAME];
|
|
|
|
int timeout;
|
|
|
|
};
|
|
|
|
|
2013-12-13 16:38:57 +00:00
|
|
|
static int app_control_dial(struct stasis_app_control *control,
|
2013-07-01 18:19:15 +00:00
|
|
|
struct ast_channel *chan, void *data)
|
|
|
|
{
|
|
|
|
RAII_VAR(struct ast_dial *, dial, ast_dial_create(), ast_dial_destroy);
|
2014-08-22 16:52:51 +00:00
|
|
|
struct stasis_app_control_dial_data *dial_data = data;
|
2013-07-01 18:19:15 +00:00
|
|
|
enum ast_dial_result res;
|
|
|
|
char *tech, *resource;
|
|
|
|
struct ast_channel *new_chan;
|
2013-10-02 15:24:16 +00:00
|
|
|
RAII_VAR(struct ast_bridge *, bridge, NULL, ao2_cleanup);
|
2013-07-01 18:19:15 +00:00
|
|
|
|
|
|
|
tech = dial_data->endpoint;
|
|
|
|
if (!(resource = strchr(tech, '/'))) {
|
2013-12-13 16:38:57 +00:00
|
|
|
return -1;
|
2013-07-01 18:19:15 +00:00
|
|
|
}
|
|
|
|
*resource++ = '\0';
|
|
|
|
|
|
|
|
if (!dial) {
|
|
|
|
ast_log(LOG_ERROR, "Failed to create dialing structure.\n");
|
2013-12-13 16:38:57 +00:00
|
|
|
return -1;
|
2013-07-01 18:19:15 +00:00
|
|
|
}
|
|
|
|
|
uniqueid: channel linkedid, ami, ari object creation with id's
Much needed was a way to assign id to objects on creation, and
much change was necessary to accomplish it. Channel uniqueids
and linkedids are split into separate string and creation time
components without breaking linkedid propgation. This allowed
the uniqueid to be specified by the user interface - and those
values are now carried through to channel creation, adding the
assignedids value to every function in the chain including the
channel drivers. For local channels, the second channel can be
specified or left to default to a ;2 suffix of first. In ARI,
bridge, playback, and snoop objects can also be created with a
specified uniqueid.
Along the way, the args order to allocating channels was fixed
in chan_mgcp and chan_gtalk, and linkedid is no longer lost as
masquerade occurs.
(closes issue ASTERISK-23120)
Review: https://reviewboard.asterisk.org/r/3191/
........
Merged revisions 410157 from http://svn.asterisk.org/svn/asterisk/branches/12
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@410158 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-03-07 15:47:55 +00:00
|
|
|
if (ast_dial_append(dial, tech, resource, NULL) < 0) {
|
2013-07-01 18:19:15 +00:00
|
|
|
ast_log(LOG_ERROR, "Failed to add %s/%s to dialing structure.\n", tech, resource);
|
2013-12-13 16:38:57 +00:00
|
|
|
return -1;
|
2013-07-01 18:19:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ast_dial_set_global_timeout(dial, dial_data->timeout);
|
|
|
|
|
|
|
|
res = ast_dial_run(dial, NULL, 0);
|
|
|
|
if (res != AST_DIAL_RESULT_ANSWERED || !(new_chan = ast_dial_answered_steal(dial))) {
|
2013-12-13 16:38:57 +00:00
|
|
|
return -1;
|
2013-07-01 18:19:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!(bridge = ast_bridge_basic_new())) {
|
|
|
|
ast_log(LOG_ERROR, "Failed to create basic bridge.\n");
|
2013-12-13 16:38:57 +00:00
|
|
|
return -1;
|
2013-07-01 18:19:15 +00:00
|
|
|
}
|
|
|
|
|
2013-09-13 22:19:23 +00:00
|
|
|
if (ast_bridge_impart(bridge, new_chan, NULL, NULL,
|
|
|
|
AST_BRIDGE_IMPART_CHAN_INDEPENDENT)) {
|
|
|
|
ast_hangup(new_chan);
|
|
|
|
} else {
|
2014-08-07 15:30:19 +00:00
|
|
|
control_add_channel_to_bridge(control, chan, bridge);
|
2013-09-13 22:19:23 +00:00
|
|
|
}
|
2013-07-01 18:19:15 +00:00
|
|
|
|
2013-12-13 16:38:57 +00:00
|
|
|
return 0;
|
2013-07-01 18:19:15 +00:00
|
|
|
}
|
|
|
|
|
2013-10-02 15:24:16 +00:00
|
|
|
int stasis_app_control_dial(struct stasis_app_control *control, const char *endpoint, const char *exten, const char *context,
|
|
|
|
int timeout)
|
2013-07-01 18:19:15 +00:00
|
|
|
{
|
|
|
|
struct stasis_app_control_dial_data *dial_data;
|
|
|
|
|
|
|
|
if (!(dial_data = ast_calloc(1, sizeof(*dial_data)))) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-10-02 15:24:16 +00:00
|
|
|
if (!ast_strlen_zero(endpoint)) {
|
|
|
|
ast_copy_string(dial_data->endpoint, endpoint, sizeof(dial_data->endpoint));
|
|
|
|
} else if (!ast_strlen_zero(exten) && !ast_strlen_zero(context)) {
|
|
|
|
snprintf(dial_data->endpoint, sizeof(dial_data->endpoint), "Local/%s@%s", exten, context);
|
|
|
|
} else {
|
|
|
|
return -1;
|
|
|
|
}
|
2013-07-01 18:19:15 +00:00
|
|
|
|
|
|
|
if (timeout > 0) {
|
|
|
|
dial_data->timeout = timeout * 1000;
|
|
|
|
} else if (timeout == -1) {
|
|
|
|
dial_data->timeout = -1;
|
|
|
|
} else {
|
|
|
|
dial_data->timeout = 30000;
|
|
|
|
}
|
|
|
|
|
2014-08-22 16:52:51 +00:00
|
|
|
stasis_app_send_command_async(control, app_control_dial, dial_data, ast_free_ptr);
|
2013-07-01 18:19:15 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-08-05 16:59:13 +00:00
|
|
|
int stasis_app_control_add_role(struct stasis_app_control *control, const char *role)
|
|
|
|
{
|
|
|
|
return ast_channel_add_bridge_role(control->channel, role);
|
|
|
|
}
|
|
|
|
|
|
|
|
void stasis_app_control_clear_roles(struct stasis_app_control *control)
|
|
|
|
{
|
|
|
|
ast_channel_clear_bridge_roles(control->channel);
|
|
|
|
}
|
|
|
|
|
2014-04-18 20:09:24 +00:00
|
|
|
int control_command_count(struct stasis_app_control *control)
|
|
|
|
{
|
|
|
|
return ao2_container_count(control->command_queue);
|
|
|
|
}
|
|
|
|
|
2013-05-14 21:45:08 +00:00
|
|
|
int control_is_done(struct stasis_app_control *control)
|
|
|
|
{
|
|
|
|
/* Called from stasis_app_exec thread; no lock needed */
|
|
|
|
return control->is_done;
|
|
|
|
}
|
|
|
|
|
2014-04-18 20:09:24 +00:00
|
|
|
void control_mark_done(struct stasis_app_control *control)
|
|
|
|
{
|
|
|
|
control->is_done = 1;
|
|
|
|
}
|
|
|
|
|
2013-06-26 19:29:57 +00:00
|
|
|
struct stasis_app_control_continue_data {
|
|
|
|
char context[AST_MAX_CONTEXT];
|
|
|
|
char extension[AST_MAX_EXTENSION];
|
|
|
|
int priority;
|
|
|
|
};
|
|
|
|
|
2013-12-13 16:38:57 +00:00
|
|
|
static int app_control_continue(struct stasis_app_control *control,
|
2013-05-14 21:45:08 +00:00
|
|
|
struct ast_channel *chan, void *data)
|
|
|
|
{
|
2014-08-22 16:52:51 +00:00
|
|
|
struct stasis_app_control_continue_data *continue_data = data;
|
2013-06-26 19:29:57 +00:00
|
|
|
|
2013-08-13 15:27:32 +00:00
|
|
|
ast_assert(control->channel != NULL);
|
|
|
|
|
|
|
|
/* If we're in a Stasis bridge, depart it before going back to the
|
|
|
|
* dialplan */
|
|
|
|
if (stasis_app_get_bridge(control)) {
|
|
|
|
ast_bridge_depart(control->channel);
|
|
|
|
}
|
|
|
|
|
2013-05-14 21:45:08 +00:00
|
|
|
/* Called from stasis_app_exec thread; no lock needed */
|
2013-06-26 19:29:57 +00:00
|
|
|
ast_explicit_goto(control->channel, continue_data->context, continue_data->extension, continue_data->priority);
|
|
|
|
|
2013-05-14 21:45:08 +00:00
|
|
|
control->is_done = 1;
|
2013-06-26 19:29:57 +00:00
|
|
|
|
2013-12-13 16:38:57 +00:00
|
|
|
return 0;
|
2013-05-14 21:45:08 +00:00
|
|
|
}
|
|
|
|
|
2013-06-26 19:29:57 +00:00
|
|
|
int stasis_app_control_continue(struct stasis_app_control *control, const char *context, const char *extension, int priority)
|
2013-05-14 21:45:08 +00:00
|
|
|
{
|
2013-06-26 19:29:57 +00:00
|
|
|
struct stasis_app_control_continue_data *continue_data;
|
|
|
|
|
|
|
|
if (!(continue_data = ast_calloc(1, sizeof(*continue_data)))) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
ast_copy_string(continue_data->context, S_OR(context, ""), sizeof(continue_data->context));
|
|
|
|
ast_copy_string(continue_data->extension, S_OR(extension, ""), sizeof(continue_data->extension));
|
|
|
|
if (priority > 0) {
|
|
|
|
continue_data->priority = priority;
|
|
|
|
} else {
|
|
|
|
continue_data->priority = -1;
|
|
|
|
}
|
|
|
|
|
2014-08-22 16:52:51 +00:00
|
|
|
stasis_app_send_command_async(control, app_control_continue, continue_data, ast_free_ptr);
|
2013-06-26 19:29:57 +00:00
|
|
|
|
|
|
|
return 0;
|
2013-05-14 21:45:08 +00:00
|
|
|
}
|
|
|
|
|
ARI/PJSIP: Add the ability to redirect (transfer) a channel in a Stasis app
This patch adds a new feature to ARI to redirect a channel to another server,
and fixes a few bugs in PJSIP's handling of the Transfer dialplan
application/ARI redirect capability.
*New Feature*
A new operation has been added to the ARI channels resource, redirect. With
this, a channel in a Stasis application can be redirected to another endpoint
of the same underlying channel technology.
*Bug fixes*
In the process of writing this new feature, two bugs were fixed in the PJSIP
stack:
(1) The existing .transfer channel callback had the limitation that it could
only transfer channels to a SIP URI, i.e., you had to pass
'PJSIP/sip:foo@my_provider.com' to the dialplan application. While this is
still supported, it is somewhat unintuitive - particularly in a world full
of endpoints. As such, we now also support specifying the PJSIP endpoint to
transfer to.
(2) res_pjsip_multihomed was, unfortunately, trying to 'help' a 302 redirect by
updating its Contact header. Alas, that resulted in the forwarding
destination set by the dialplan application/ARI resource/whatever being
rewritten with very incorrect information. Hence, we now don't bother
updating an outgoing response if it is a 302. Since this took a looong time
to find, some additional debug statements have been added to those modules
that update the Contact headers.
Review: https://reviewboard.asterisk.org/r/4316/
ASTERISK-24015 #close
Reported by: Private Name
ASTERISK-24703 #close
Reported by: Matt Jordan
........
Merged revisions 431717 from http://svn.asterisk.org/svn/asterisk/branches/13
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@431718 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2015-02-12 20:34:37 +00:00
|
|
|
static int app_control_redirect(struct stasis_app_control *control,
|
|
|
|
struct ast_channel *chan, void *data)
|
|
|
|
{
|
|
|
|
char *endpoint = data;
|
|
|
|
int res;
|
|
|
|
|
|
|
|
ast_assert(control->channel != NULL);
|
|
|
|
ast_assert(endpoint != NULL);
|
|
|
|
|
|
|
|
res = ast_transfer(control->channel, endpoint);
|
|
|
|
if (!res) {
|
|
|
|
ast_log(LOG_NOTICE, "Unsupported transfer requested on channel '%s'\n",
|
|
|
|
ast_channel_name(control->channel));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int stasis_app_control_redirect(struct stasis_app_control *control, const char *endpoint)
|
|
|
|
{
|
|
|
|
char *endpoint_data = ast_strdup(endpoint);
|
|
|
|
|
|
|
|
if (!endpoint_data) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
stasis_app_send_command_async(control, app_control_redirect, endpoint_data, ast_free_ptr);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-11-01 14:38:21 +00:00
|
|
|
struct stasis_app_control_dtmf_data {
|
|
|
|
int before;
|
|
|
|
int between;
|
|
|
|
unsigned int duration;
|
|
|
|
int after;
|
|
|
|
char dtmf[];
|
|
|
|
};
|
|
|
|
|
2013-12-13 16:38:57 +00:00
|
|
|
static int app_control_dtmf(struct stasis_app_control *control,
|
2013-11-01 14:38:21 +00:00
|
|
|
struct ast_channel *chan, void *data)
|
|
|
|
{
|
2014-08-22 16:52:51 +00:00
|
|
|
struct stasis_app_control_dtmf_data *dtmf_data = data;
|
2013-11-01 14:38:21 +00:00
|
|
|
|
2014-04-30 12:42:42 +00:00
|
|
|
if (ast_channel_state(chan) != AST_STATE_UP) {
|
|
|
|
ast_indicate(chan, AST_CONTROL_PROGRESS);
|
|
|
|
}
|
|
|
|
|
2013-11-01 14:38:21 +00:00
|
|
|
if (dtmf_data->before) {
|
|
|
|
ast_safe_sleep(chan, dtmf_data->before);
|
|
|
|
}
|
|
|
|
|
|
|
|
ast_dtmf_stream(chan, NULL, dtmf_data->dtmf, dtmf_data->between, dtmf_data->duration);
|
|
|
|
|
|
|
|
if (dtmf_data->after) {
|
|
|
|
ast_safe_sleep(chan, dtmf_data->after);
|
|
|
|
}
|
|
|
|
|
2013-12-13 16:38:57 +00:00
|
|
|
return 0;
|
2013-11-01 14:38:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int stasis_app_control_dtmf(struct stasis_app_control *control, const char *dtmf, int before, int between, unsigned int duration, int after)
|
|
|
|
{
|
|
|
|
struct stasis_app_control_dtmf_data *dtmf_data;
|
|
|
|
|
|
|
|
if (!(dtmf_data = ast_calloc(1, sizeof(*dtmf_data) + strlen(dtmf) + 1))) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
dtmf_data->before = before;
|
|
|
|
dtmf_data->between = between;
|
|
|
|
dtmf_data->duration = duration;
|
|
|
|
dtmf_data->after = after;
|
|
|
|
strcpy(dtmf_data->dtmf, dtmf);
|
|
|
|
|
2014-08-22 16:52:51 +00:00
|
|
|
stasis_app_send_command_async(control, app_control_dtmf, dtmf_data, ast_free_ptr);
|
2013-11-01 14:38:21 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-12-13 16:38:57 +00:00
|
|
|
static int app_control_ring(struct stasis_app_control *control,
|
2013-11-01 14:38:21 +00:00
|
|
|
struct ast_channel *chan, void *data)
|
|
|
|
{
|
|
|
|
ast_indicate(control->channel, AST_CONTROL_RINGING);
|
|
|
|
|
2013-12-13 16:38:57 +00:00
|
|
|
return 0;
|
2013-11-01 14:38:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int stasis_app_control_ring(struct stasis_app_control *control)
|
|
|
|
{
|
2014-08-22 16:52:51 +00:00
|
|
|
stasis_app_send_command_async(control, app_control_ring, NULL, NULL);
|
2013-11-01 14:38:21 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-12-13 16:38:57 +00:00
|
|
|
static int app_control_ring_stop(struct stasis_app_control *control,
|
2013-11-13 23:11:32 +00:00
|
|
|
struct ast_channel *chan, void *data)
|
|
|
|
{
|
|
|
|
ast_indicate(control->channel, -1);
|
|
|
|
|
2013-12-13 16:38:57 +00:00
|
|
|
return 0;
|
2013-11-13 23:11:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int stasis_app_control_ring_stop(struct stasis_app_control *control)
|
|
|
|
{
|
2014-08-22 16:52:51 +00:00
|
|
|
stasis_app_send_command_async(control, app_control_ring_stop, NULL, NULL);
|
2013-11-13 23:11:32 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-07-18 16:03:12 +00:00
|
|
|
struct stasis_app_control_mute_data {
|
|
|
|
enum ast_frame_type frametype;
|
|
|
|
unsigned int direction;
|
|
|
|
};
|
|
|
|
|
2013-12-13 16:38:57 +00:00
|
|
|
static int app_control_mute(struct stasis_app_control *control,
|
2013-07-18 16:03:12 +00:00
|
|
|
struct ast_channel *chan, void *data)
|
|
|
|
{
|
2014-08-22 16:52:51 +00:00
|
|
|
struct stasis_app_control_mute_data *mute_data = data;
|
2013-07-18 16:03:12 +00:00
|
|
|
SCOPED_CHANNELLOCK(lockvar, chan);
|
|
|
|
|
|
|
|
ast_channel_suppress(control->channel, mute_data->direction, mute_data->frametype);
|
|
|
|
|
2013-12-13 16:38:57 +00:00
|
|
|
return 0;
|
2013-07-18 16:03:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int stasis_app_control_mute(struct stasis_app_control *control, unsigned int direction, enum ast_frame_type frametype)
|
|
|
|
{
|
|
|
|
struct stasis_app_control_mute_data *mute_data;
|
|
|
|
|
|
|
|
if (!(mute_data = ast_calloc(1, sizeof(*mute_data)))) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
mute_data->direction = direction;
|
|
|
|
mute_data->frametype = frametype;
|
|
|
|
|
2014-08-22 16:52:51 +00:00
|
|
|
stasis_app_send_command_async(control, app_control_mute, mute_data, ast_free_ptr);
|
2013-07-18 16:03:12 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-12-13 16:38:57 +00:00
|
|
|
static int app_control_unmute(struct stasis_app_control *control,
|
2013-07-18 16:03:12 +00:00
|
|
|
struct ast_channel *chan, void *data)
|
|
|
|
{
|
2014-08-22 16:52:51 +00:00
|
|
|
struct stasis_app_control_mute_data *mute_data = data;
|
2013-07-18 16:03:12 +00:00
|
|
|
SCOPED_CHANNELLOCK(lockvar, chan);
|
|
|
|
|
|
|
|
ast_channel_unsuppress(control->channel, mute_data->direction, mute_data->frametype);
|
|
|
|
|
2013-12-13 16:38:57 +00:00
|
|
|
return 0;
|
2013-07-18 16:03:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int stasis_app_control_unmute(struct stasis_app_control *control, unsigned int direction, enum ast_frame_type frametype)
|
|
|
|
{
|
|
|
|
struct stasis_app_control_mute_data *mute_data;
|
|
|
|
|
|
|
|
if (!(mute_data = ast_calloc(1, sizeof(*mute_data)))) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
mute_data->direction = direction;
|
|
|
|
mute_data->frametype = frametype;
|
|
|
|
|
2014-08-22 16:52:51 +00:00
|
|
|
stasis_app_send_command_async(control, app_control_unmute, mute_data, ast_free_ptr);
|
2013-07-18 16:03:12 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-07-08 14:46:20 +00:00
|
|
|
int stasis_app_control_set_channel_var(struct stasis_app_control *control, const char *variable, const char *value)
|
|
|
|
{
|
|
|
|
return pbx_builtin_setvar_helper(control->channel, variable, value);
|
|
|
|
}
|
|
|
|
|
2013-12-13 16:38:57 +00:00
|
|
|
static int app_control_hold(struct stasis_app_control *control,
|
2013-07-01 18:56:21 +00:00
|
|
|
struct ast_channel *chan, void *data)
|
|
|
|
{
|
|
|
|
ast_indicate(control->channel, AST_CONTROL_HOLD);
|
|
|
|
|
2013-12-13 16:38:57 +00:00
|
|
|
return 0;
|
2013-07-01 18:56:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void stasis_app_control_hold(struct stasis_app_control *control)
|
|
|
|
{
|
2014-08-22 16:52:51 +00:00
|
|
|
stasis_app_send_command_async(control, app_control_hold, NULL, NULL);
|
2013-07-01 18:56:21 +00:00
|
|
|
}
|
|
|
|
|
2013-12-13 16:38:57 +00:00
|
|
|
static int app_control_unhold(struct stasis_app_control *control,
|
2013-07-01 18:56:21 +00:00
|
|
|
struct ast_channel *chan, void *data)
|
|
|
|
{
|
|
|
|
ast_indicate(control->channel, AST_CONTROL_UNHOLD);
|
|
|
|
|
2013-12-13 16:38:57 +00:00
|
|
|
return 0;
|
2013-07-01 18:56:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void stasis_app_control_unhold(struct stasis_app_control *control)
|
|
|
|
{
|
2014-08-22 16:52:51 +00:00
|
|
|
stasis_app_send_command_async(control, app_control_unhold, NULL, NULL);
|
2013-07-01 18:56:21 +00:00
|
|
|
}
|
|
|
|
|
2013-12-13 16:38:57 +00:00
|
|
|
static int app_control_moh_start(struct stasis_app_control *control,
|
2013-07-19 19:40:27 +00:00
|
|
|
struct ast_channel *chan, void *data)
|
|
|
|
{
|
|
|
|
char *moh_class = data;
|
|
|
|
|
2014-04-30 12:42:42 +00:00
|
|
|
if (ast_channel_state(chan) != AST_STATE_UP) {
|
|
|
|
ast_indicate(chan, AST_CONTROL_PROGRESS);
|
|
|
|
}
|
|
|
|
|
2013-07-19 19:40:27 +00:00
|
|
|
ast_moh_start(chan, moh_class, NULL);
|
|
|
|
|
2013-12-13 16:38:57 +00:00
|
|
|
return 0;
|
2013-07-19 19:40:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void stasis_app_control_moh_start(struct stasis_app_control *control, const char *moh_class)
|
|
|
|
{
|
|
|
|
char *data = NULL;
|
|
|
|
|
|
|
|
if (!ast_strlen_zero(moh_class)) {
|
|
|
|
data = ast_strdup(moh_class);
|
|
|
|
}
|
|
|
|
|
2014-08-22 16:52:51 +00:00
|
|
|
stasis_app_send_command_async(control, app_control_moh_start, data, ast_free_ptr);
|
2013-07-19 19:40:27 +00:00
|
|
|
}
|
|
|
|
|
2013-12-13 16:38:57 +00:00
|
|
|
static int app_control_moh_stop(struct stasis_app_control *control,
|
2013-07-19 19:40:27 +00:00
|
|
|
struct ast_channel *chan, void *data)
|
|
|
|
{
|
|
|
|
ast_moh_stop(chan);
|
2013-12-13 16:38:57 +00:00
|
|
|
return 0;
|
2013-07-19 19:40:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void stasis_app_control_moh_stop(struct stasis_app_control *control)
|
|
|
|
{
|
2014-08-22 16:52:51 +00:00
|
|
|
stasis_app_send_command_async(control, app_control_moh_stop, NULL, NULL);
|
2013-07-19 19:40:27 +00:00
|
|
|
}
|
|
|
|
|
2013-12-13 16:38:57 +00:00
|
|
|
static int app_control_silence_start(struct stasis_app_control *control,
|
2013-11-21 15:56:34 +00:00
|
|
|
struct ast_channel *chan, void *data)
|
|
|
|
{
|
2014-04-30 12:42:42 +00:00
|
|
|
if (ast_channel_state(chan) != AST_STATE_UP) {
|
|
|
|
ast_indicate(chan, AST_CONTROL_PROGRESS);
|
|
|
|
}
|
|
|
|
|
2013-11-21 15:56:34 +00:00
|
|
|
if (control->silgen) {
|
|
|
|
/* We have a silence generator, but it may have been implicitly
|
|
|
|
* disabled by media actions (music on hold, playing media,
|
|
|
|
* etc.) Just stop it and restart a new one.
|
|
|
|
*/
|
|
|
|
ast_channel_stop_silence_generator(
|
|
|
|
control->channel, control->silgen);
|
|
|
|
}
|
|
|
|
|
|
|
|
ast_debug(3, "%s: Starting silence generator\n",
|
|
|
|
stasis_app_control_get_channel_id(control));
|
|
|
|
control->silgen = ast_channel_start_silence_generator(control->channel);
|
|
|
|
|
|
|
|
if (!control->silgen) {
|
|
|
|
ast_log(LOG_WARNING,
|
|
|
|
"%s: Failed to start silence generator.\n",
|
|
|
|
stasis_app_control_get_channel_id(control));
|
|
|
|
}
|
|
|
|
|
2013-12-13 16:38:57 +00:00
|
|
|
return 0;
|
2013-11-21 15:56:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void stasis_app_control_silence_start(struct stasis_app_control *control)
|
|
|
|
{
|
2014-08-22 16:52:51 +00:00
|
|
|
stasis_app_send_command_async(control, app_control_silence_start, NULL, NULL);
|
2013-11-21 15:56:34 +00:00
|
|
|
}
|
|
|
|
|
2013-12-13 16:38:57 +00:00
|
|
|
static int app_control_silence_stop(struct stasis_app_control *control,
|
2013-11-21 15:56:34 +00:00
|
|
|
struct ast_channel *chan, void *data)
|
|
|
|
{
|
|
|
|
if (control->silgen) {
|
|
|
|
ast_debug(3, "%s: Stopping silence generator\n",
|
|
|
|
stasis_app_control_get_channel_id(control));
|
|
|
|
ast_channel_stop_silence_generator(
|
|
|
|
control->channel, control->silgen);
|
|
|
|
control->silgen = NULL;
|
|
|
|
}
|
|
|
|
|
2013-12-13 16:38:57 +00:00
|
|
|
return 0;
|
2013-11-21 15:56:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void stasis_app_control_silence_stop(struct stasis_app_control *control)
|
|
|
|
{
|
2014-08-22 16:52:51 +00:00
|
|
|
stasis_app_send_command_async(control, app_control_silence_stop, NULL, NULL);
|
2013-11-21 15:56:34 +00:00
|
|
|
}
|
|
|
|
|
2013-05-14 21:45:08 +00:00
|
|
|
struct ast_channel_snapshot *stasis_app_control_get_snapshot(
|
|
|
|
const struct stasis_app_control *control)
|
|
|
|
{
|
|
|
|
RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
|
|
|
|
struct ast_channel_snapshot *snapshot;
|
|
|
|
|
2013-08-01 13:49:34 +00:00
|
|
|
msg = stasis_cache_get(ast_channel_cache(), ast_channel_snapshot_type(),
|
2013-05-14 21:45:08 +00:00
|
|
|
stasis_app_control_get_channel_id(control));
|
|
|
|
if (!msg) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
snapshot = stasis_message_data(msg);
|
|
|
|
ast_assert(snapshot != NULL);
|
|
|
|
|
|
|
|
ao2_ref(snapshot, +1);
|
|
|
|
return snapshot;
|
|
|
|
}
|
|
|
|
|
2013-12-13 16:38:57 +00:00
|
|
|
static int app_send_command_on_condition(struct stasis_app_control *control,
|
|
|
|
stasis_app_command_cb command_fn, void *data,
|
2014-08-22 16:52:51 +00:00
|
|
|
command_data_destructor_fn data_destructor,
|
2013-12-13 16:38:57 +00:00
|
|
|
app_command_can_exec_cb can_exec_fn)
|
2013-05-14 21:45:08 +00:00
|
|
|
{
|
|
|
|
RAII_VAR(struct stasis_app_command *, command, NULL, ao2_cleanup);
|
|
|
|
|
|
|
|
if (control == NULL) {
|
2013-12-13 16:38:57 +00:00
|
|
|
return -1;
|
2013-05-14 21:45:08 +00:00
|
|
|
}
|
|
|
|
|
2013-12-13 16:38:57 +00:00
|
|
|
command = exec_command_on_condition(
|
2014-08-22 16:52:51 +00:00
|
|
|
control, command_fn, data, data_destructor, can_exec_fn);
|
2013-05-14 21:45:08 +00:00
|
|
|
if (!command) {
|
2013-12-13 16:38:57 +00:00
|
|
|
return -1;
|
2013-05-14 21:45:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return command_join(command);
|
|
|
|
}
|
|
|
|
|
2013-12-13 16:38:57 +00:00
|
|
|
int stasis_app_send_command(struct stasis_app_control *control,
|
2014-08-22 16:52:51 +00:00
|
|
|
stasis_app_command_cb command_fn, void *data, command_data_destructor_fn data_destructor)
|
2013-12-13 16:38:57 +00:00
|
|
|
{
|
2014-08-22 16:52:51 +00:00
|
|
|
return app_send_command_on_condition(control, command_fn, data, data_destructor, NULL);
|
2013-12-13 16:38:57 +00:00
|
|
|
}
|
|
|
|
|
2013-05-14 21:45:08 +00:00
|
|
|
int stasis_app_send_command_async(struct stasis_app_control *control,
|
2014-08-22 16:52:51 +00:00
|
|
|
stasis_app_command_cb command_fn, void *data,
|
|
|
|
command_data_destructor_fn data_destructor)
|
2013-05-14 21:45:08 +00:00
|
|
|
{
|
|
|
|
RAII_VAR(struct stasis_app_command *, command, NULL, ao2_cleanup);
|
|
|
|
|
|
|
|
if (control == NULL) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-08-22 16:52:51 +00:00
|
|
|
command = exec_command(control, command_fn, data, data_destructor);
|
2013-05-14 21:45:08 +00:00
|
|
|
if (!command) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-08-13 15:27:32 +00:00
|
|
|
struct ast_bridge *stasis_app_get_bridge(struct stasis_app_control *control)
|
|
|
|
{
|
|
|
|
if (!control) {
|
|
|
|
return NULL;
|
|
|
|
} else {
|
|
|
|
SCOPED_AO2LOCK(lock, control);
|
|
|
|
return control->bridge;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-13 16:38:57 +00:00
|
|
|
static int bridge_channel_depart(struct stasis_app_control *control,
|
2013-11-01 12:13:09 +00:00
|
|
|
struct ast_channel *chan, void *data)
|
|
|
|
{
|
2014-08-22 16:52:51 +00:00
|
|
|
struct ast_bridge_channel *bridge_channel = data;
|
2013-11-01 12:13:09 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
SCOPED_CHANNELLOCK(lock, chan);
|
|
|
|
|
|
|
|
if (bridge_channel != ast_channel_internal_bridge_channel(chan)) {
|
|
|
|
ast_debug(3, "%s: Channel is no longer in departable state\n",
|
|
|
|
ast_channel_uniqueid(chan));
|
2013-12-13 16:38:57 +00:00
|
|
|
return -1;
|
2013-11-01 12:13:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ast_debug(3, "%s: Channel departing bridge\n",
|
|
|
|
ast_channel_uniqueid(chan));
|
|
|
|
|
|
|
|
ast_bridge_depart(chan);
|
|
|
|
|
2013-12-13 16:38:57 +00:00
|
|
|
return 0;
|
2013-11-01 12:13:09 +00:00
|
|
|
}
|
2013-08-13 15:27:32 +00:00
|
|
|
|
|
|
|
static void bridge_after_cb(struct ast_channel *chan, void *data)
|
|
|
|
{
|
|
|
|
struct stasis_app_control *control = data;
|
|
|
|
SCOPED_AO2LOCK(lock, control);
|
2013-11-01 12:13:09 +00:00
|
|
|
struct ast_bridge_channel *bridge_channel;
|
2013-08-13 15:27:32 +00:00
|
|
|
|
|
|
|
ast_debug(3, "%s, %s: Channel leaving bridge\n",
|
|
|
|
ast_channel_uniqueid(chan), control->bridge->uniqueid);
|
|
|
|
|
|
|
|
ast_assert(chan == control->channel);
|
|
|
|
|
|
|
|
/* Restore the channel's PBX */
|
|
|
|
ast_channel_pbx_set(control->channel, control->pbx);
|
|
|
|
control->pbx = NULL;
|
|
|
|
|
2014-03-13 19:33:22 +00:00
|
|
|
app_unsubscribe_bridge(control->app, control->bridge);
|
|
|
|
|
2013-08-13 15:27:32 +00:00
|
|
|
/* No longer in the bridge */
|
|
|
|
control->bridge = NULL;
|
|
|
|
|
2013-11-01 12:13:09 +00:00
|
|
|
/* Get the bridge channel so we don't depart from the wrong bridge */
|
|
|
|
ast_channel_lock(chan);
|
|
|
|
bridge_channel = ast_channel_get_bridge_channel(chan);
|
|
|
|
ast_channel_unlock(chan);
|
|
|
|
|
|
|
|
/* Depart this channel from the bridge using the command queue if possible */
|
2014-08-22 16:52:51 +00:00
|
|
|
stasis_app_send_command_async(control, bridge_channel_depart, bridge_channel, __ao2_cleanup);
|
2014-08-21 21:37:03 +00:00
|
|
|
if (stasis_app_channel_is_stasis_end_published(chan)) {
|
|
|
|
/* The channel has had a StasisEnd published on it, but until now had remained in
|
|
|
|
* the bridging system. This means that the channel moved from a Stasis bridge to a
|
|
|
|
* non-Stasis bridge and is now exiting the bridging system. Because of this, the
|
|
|
|
* channel needs to exit the Stasis application and go to wherever the non-Stasis
|
|
|
|
* bridge has directed it to go. If the non-Stasis bridge has not set up an after
|
|
|
|
* bridge destination, then the channel should be hung up.
|
|
|
|
*/
|
|
|
|
int hangup_flag;
|
|
|
|
|
|
|
|
hangup_flag = ast_bridge_setup_after_goto(chan) ? AST_SOFTHANGUP_DEV : AST_SOFTHANGUP_ASYNCGOTO;
|
|
|
|
ast_channel_lock(chan);
|
|
|
|
ast_softhangup_nolock(chan, hangup_flag);
|
|
|
|
ast_channel_unlock(chan);
|
|
|
|
}
|
2013-08-13 15:27:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void bridge_after_cb_failed(enum ast_bridge_after_cb_reason reason,
|
|
|
|
void *data)
|
|
|
|
{
|
|
|
|
struct stasis_app_control *control = data;
|
|
|
|
|
|
|
|
bridge_after_cb(control->channel, data);
|
|
|
|
|
|
|
|
ast_debug(3, " reason: %s\n",
|
|
|
|
ast_bridge_after_cb_reason_string(reason));
|
|
|
|
}
|
|
|
|
|
2014-08-07 15:30:19 +00:00
|
|
|
int control_add_channel_to_bridge(
|
2013-08-13 15:27:32 +00:00
|
|
|
struct stasis_app_control *control,
|
|
|
|
struct ast_channel *chan, void *data)
|
|
|
|
{
|
|
|
|
struct ast_bridge *bridge = data;
|
|
|
|
int res;
|
|
|
|
|
|
|
|
if (!control || !bridge) {
|
2013-12-13 16:38:57 +00:00
|
|
|
return -1;
|
2013-08-13 15:27:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ast_debug(3, "%s: Adding to bridge %s\n",
|
|
|
|
stasis_app_control_get_channel_id(control),
|
|
|
|
bridge->uniqueid);
|
|
|
|
|
|
|
|
ast_assert(chan != NULL);
|
|
|
|
|
|
|
|
/* Depart whatever Stasis bridge we're currently in. */
|
|
|
|
if (stasis_app_get_bridge(control)) {
|
|
|
|
/* Note that it looks like there's a race condition here, since
|
|
|
|
* we don't have control locked. But this happens from the
|
|
|
|
* control callback thread, so there won't be any other
|
|
|
|
* concurrent attempts to bridge.
|
|
|
|
*/
|
|
|
|
ast_bridge_depart(chan);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
res = ast_bridge_set_after_callback(chan, bridge_after_cb,
|
|
|
|
bridge_after_cb_failed, control);
|
|
|
|
if (res != 0) {
|
|
|
|
ast_log(LOG_ERROR, "Error setting after-bridge callback\n");
|
2013-12-13 16:38:57 +00:00
|
|
|
return -1;
|
2013-08-13 15:27:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
/* pbx and bridge are modified by the bridging impart thread.
|
|
|
|
* It shouldn't happen concurrently, but we still need to lock
|
|
|
|
* for the memory fence.
|
|
|
|
*/
|
|
|
|
SCOPED_AO2LOCK(lock, control);
|
|
|
|
|
2014-03-13 19:33:22 +00:00
|
|
|
/* Ensure the controlling application is subscribed early enough
|
|
|
|
* to receive the ChannelEnteredBridge message. This works in concert
|
|
|
|
* with the subscription handled in the Stasis application execution
|
|
|
|
* loop */
|
|
|
|
app_subscribe_bridge(control->app, bridge);
|
|
|
|
|
2013-08-13 15:27:32 +00:00
|
|
|
/* Save off the channel's PBX */
|
|
|
|
ast_assert(control->pbx == NULL);
|
|
|
|
if (!control->pbx) {
|
|
|
|
control->pbx = ast_channel_pbx(chan);
|
|
|
|
ast_channel_pbx_set(chan, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
res = ast_bridge_impart(bridge,
|
|
|
|
chan,
|
|
|
|
NULL, /* swap channel */
|
|
|
|
NULL, /* features */
|
2013-09-13 22:19:23 +00:00
|
|
|
AST_BRIDGE_IMPART_CHAN_DEPARTABLE);
|
2013-08-13 15:27:32 +00:00
|
|
|
if (res != 0) {
|
|
|
|
ast_log(LOG_ERROR, "Error adding channel to bridge\n");
|
|
|
|
ast_channel_pbx_set(chan, control->pbx);
|
|
|
|
control->pbx = NULL;
|
2013-12-13 16:38:57 +00:00
|
|
|
return -1;
|
2013-08-13 15:27:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ast_assert(stasis_app_get_bridge(control) == NULL);
|
|
|
|
control->bridge = bridge;
|
|
|
|
}
|
2013-12-13 16:38:57 +00:00
|
|
|
return 0;
|
2013-08-13 15:27:32 +00:00
|
|
|
}
|
|
|
|
|
2013-08-23 17:19:02 +00:00
|
|
|
int stasis_app_control_add_channel_to_bridge(
|
2013-08-13 15:27:32 +00:00
|
|
|
struct stasis_app_control *control, struct ast_bridge *bridge)
|
|
|
|
{
|
|
|
|
ast_debug(3, "%s: Sending channel add_to_bridge command\n",
|
|
|
|
stasis_app_control_get_channel_id(control));
|
2013-12-13 16:38:57 +00:00
|
|
|
|
|
|
|
return app_send_command_on_condition(
|
2014-08-22 16:52:51 +00:00
|
|
|
control, control_add_channel_to_bridge, bridge, NULL,
|
2013-12-13 16:38:57 +00:00
|
|
|
app_control_can_add_channel_to_bridge);
|
2013-08-13 15:27:32 +00:00
|
|
|
}
|
|
|
|
|
2013-12-13 16:38:57 +00:00
|
|
|
static int app_control_remove_channel_from_bridge(
|
2013-08-13 15:27:32 +00:00
|
|
|
struct stasis_app_control *control,
|
|
|
|
struct ast_channel *chan, void *data)
|
|
|
|
{
|
|
|
|
struct ast_bridge *bridge = data;
|
|
|
|
|
|
|
|
if (!control) {
|
2013-12-13 16:38:57 +00:00
|
|
|
return -1;
|
2013-08-13 15:27:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* We should only depart from our own bridge */
|
|
|
|
ast_debug(3, "%s: Departing bridge %s\n",
|
|
|
|
stasis_app_control_get_channel_id(control),
|
|
|
|
bridge->uniqueid);
|
|
|
|
|
|
|
|
if (bridge != stasis_app_get_bridge(control)) {
|
|
|
|
ast_log(LOG_WARNING, "%s: Not in bridge %s; not removing\n",
|
|
|
|
stasis_app_control_get_channel_id(control),
|
|
|
|
bridge->uniqueid);
|
2013-12-13 16:38:57 +00:00
|
|
|
return -1;
|
2013-08-13 15:27:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ast_bridge_depart(chan);
|
2013-12-13 16:38:57 +00:00
|
|
|
return 0;
|
2013-08-13 15:27:32 +00:00
|
|
|
}
|
|
|
|
|
2013-08-23 17:19:02 +00:00
|
|
|
int stasis_app_control_remove_channel_from_bridge(
|
2013-08-13 15:27:32 +00:00
|
|
|
struct stasis_app_control *control, struct ast_bridge *bridge)
|
|
|
|
{
|
|
|
|
ast_debug(3, "%s: Sending channel remove_from_bridge command\n",
|
|
|
|
stasis_app_control_get_channel_id(control));
|
2013-12-13 16:38:57 +00:00
|
|
|
return app_send_command_on_condition(
|
2014-08-22 16:52:51 +00:00
|
|
|
control, app_control_remove_channel_from_bridge, bridge, NULL,
|
2013-12-13 16:38:57 +00:00
|
|
|
app_control_can_remove_channel_from_bridge);
|
2013-08-13 15:27:32 +00:00
|
|
|
}
|
|
|
|
|
2013-05-14 21:45:08 +00:00
|
|
|
const char *stasis_app_control_get_channel_id(
|
|
|
|
const struct stasis_app_control *control)
|
|
|
|
{
|
|
|
|
return ast_channel_uniqueid(control->channel);
|
|
|
|
}
|
|
|
|
|
|
|
|
void stasis_app_control_publish(
|
|
|
|
struct stasis_app_control *control, struct stasis_message *message)
|
|
|
|
{
|
|
|
|
if (!control || !control->channel || !message) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
stasis_publish(ast_channel_topic(control->channel), message);
|
|
|
|
}
|
|
|
|
|
2013-05-23 20:21:16 +00:00
|
|
|
int stasis_app_control_queue_control(struct stasis_app_control *control,
|
|
|
|
enum ast_control_frame_type frame_type)
|
|
|
|
{
|
|
|
|
return ast_queue_control(control->channel, frame_type);
|
|
|
|
}
|
|
|
|
|
2013-05-14 21:45:08 +00:00
|
|
|
int control_dispatch_all(struct stasis_app_control *control,
|
|
|
|
struct ast_channel *chan)
|
|
|
|
{
|
|
|
|
int count = 0;
|
|
|
|
struct ao2_iterator i;
|
|
|
|
void *obj;
|
|
|
|
|
|
|
|
ast_assert(control->channel == chan);
|
|
|
|
|
|
|
|
i = ao2_iterator_init(control->command_queue, AO2_ITERATOR_UNLINK);
|
|
|
|
|
|
|
|
while ((obj = ao2_iterator_next(&i))) {
|
|
|
|
RAII_VAR(struct stasis_app_command *, command, obj, ao2_cleanup);
|
|
|
|
command_invoke(command, control, chan);
|
|
|
|
++count;
|
|
|
|
}
|
|
|
|
|
|
|
|
ao2_iterator_destroy(&i);
|
|
|
|
return count;
|
|
|
|
}
|
2013-06-10 13:07:11 +00:00
|
|
|
|
2013-08-13 15:27:32 +00:00
|
|
|
void control_wait(struct stasis_app_control *control)
|
2013-06-10 13:07:11 +00:00
|
|
|
{
|
2013-08-13 15:27:32 +00:00
|
|
|
if (!control) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ast_assert(control->command_queue != NULL);
|
|
|
|
|
|
|
|
ao2_lock(control->command_queue);
|
|
|
|
while (ao2_container_count(control->command_queue) == 0) {
|
|
|
|
int res = ast_cond_wait(&control->wait_cond,
|
|
|
|
ao2_object_get_lockaddr(control->command_queue));
|
|
|
|
if (res < 0) {
|
|
|
|
ast_log(LOG_ERROR, "Error waiting on command queue\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ao2_unlock(control->command_queue);
|
2013-06-10 13:07:11 +00:00
|
|
|
}
|
2014-08-07 15:30:19 +00:00
|
|
|
|
|
|
|
int control_prestart_dispatch_all(struct stasis_app_control *control,
|
|
|
|
struct ast_channel *chan)
|
|
|
|
{
|
|
|
|
struct ao2_container *command_queue;
|
|
|
|
int count = 0;
|
|
|
|
struct ao2_iterator iter;
|
|
|
|
struct stasis_app_command *command;
|
|
|
|
|
|
|
|
ast_channel_lock(chan);
|
|
|
|
command_queue = command_prestart_get_container(chan);
|
|
|
|
ast_channel_unlock(chan);
|
|
|
|
if (!command_queue) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
iter = ao2_iterator_init(command_queue, AO2_ITERATOR_UNLINK);
|
|
|
|
|
|
|
|
while ((command = ao2_iterator_next(&iter))) {
|
|
|
|
command_invoke(command, control, chan);
|
|
|
|
ao2_cleanup(command);
|
|
|
|
++count;
|
|
|
|
}
|
|
|
|
|
|
|
|
ao2_iterator_destroy(&iter);
|
|
|
|
ao2_cleanup(command_queue);
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct stasis_app *control_app(struct stasis_app_control *control)
|
|
|
|
{
|
|
|
|
return control->app;
|
|
|
|
}
|