summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/asterisk/stasis_app.h64
-rw-r--r--include/asterisk/stasis_app_impl.h9
-rw-r--r--res/ari/resource_bridges.c27
-rw-r--r--res/res_ari_bridges.c2
-rw-r--r--res/res_stasis_answer.c11
-rw-r--r--res/res_stasis_playback.c6
-rw-r--r--res/res_stasis_recording.c56
-rw-r--r--res/stasis/command.c8
-rw-r--r--res/stasis/command.h4
-rw-r--r--res/stasis/control.c260
-rw-r--r--rest-api/api-docs/bridges.json2
11 files changed, 343 insertions, 106 deletions
diff --git a/include/asterisk/stasis_app.h b/include/asterisk/stasis_app.h
index 0c22a6c30..56e039b43 100644
--- a/include/asterisk/stasis_app.h
+++ b/include/asterisk/stasis_app.h
@@ -277,6 +277,60 @@ enum stasis_app_subscribe_res stasis_app_unsubscribe(const char *app_name,
/*! \brief Handler for controlling a channel that's in a Stasis application */
struct stasis_app_control;
+/*! \brief Rule to check to see if an operation is allowed */
+struct stasis_app_control_rule {
+ /*!
+ * \brief Checks to see if an operation is allowed on the control
+ *
+ * \param control Control object to check
+ * \return 0 on success, otherwise a failure code
+ */
+ enum stasis_app_control_channel_result (*check_rule)(
+ const struct stasis_app_control *control);
+ /*! Next item in the list */
+ AST_LIST_ENTRY(stasis_app_control_rule) next;
+};
+
+/*!
+ * \brief Registers an add channel to bridge rule.
+ *
+ * \param control Control object
+ * \param rule The rule to register
+ */
+void stasis_app_control_register_add_rule(
+ struct stasis_app_control *control,
+ struct stasis_app_control_rule *rule);
+
+/*!
+ * \brief UnRegister an add channel to bridge rule.
+ *
+ * \param control Control object
+ * \param rule The rule to unregister
+ */
+void stasis_app_control_unregister_add_rule(
+ struct stasis_app_control *control,
+ struct stasis_app_control_rule *rule);
+
+/*!
+ * \brief Registers a remove channel from bridge rule.
+ *
+ * \param control Control object
+ * \param rule The rule to register
+ */
+void stasis_app_control_register_remove_rule(
+ struct stasis_app_control *control,
+ struct stasis_app_control_rule *rule);
+
+/*!
+ * \brief Unregisters a remove channel from bridge rule.
+ *
+ * \param control Control object
+ * \param rule The rule to unregister
+ */
+void stasis_app_control_unregister_remove_rule(
+ struct stasis_app_control *control,
+ struct stasis_app_control_rule *rule);
+
/*!
* \brief Returns the handler for the given channel.
* \param chan Channel to handle.
@@ -582,6 +636,16 @@ int stasis_app_bridge_moh_stop(
struct ast_bridge *bridge);
/*!
+ * \brief Result codes used when adding/removing channels to/from bridges.
+ */
+enum stasis_app_control_channel_result {
+ /*! The channel is okay to be added/removed */
+ STASIS_APP_CHANNEL_OKAY = 0,
+ /*! The channel is currently recording */
+ STASIS_APP_CHANNEL_RECORDING
+};
+
+/*!
* \brief Add a channel to the bridge.
*
* \param control Control whose channel should be added to the bridge
diff --git a/include/asterisk/stasis_app_impl.h b/include/asterisk/stasis_app_impl.h
index d4b467756..a8c8c0586 100644
--- a/include/asterisk/stasis_app_impl.h
+++ b/include/asterisk/stasis_app_impl.h
@@ -49,7 +49,7 @@ int stasis_app_exec(struct ast_channel *chan, const char *app_name, int argc,
char *argv[]);
/*! Callback type for stasis app commands */
-typedef void *(*stasis_app_command_cb)(struct stasis_app_control *control,
+typedef int (*stasis_app_command_cb)(struct stasis_app_control *control,
struct ast_channel *chan, void *data);
/*!
@@ -63,10 +63,11 @@ typedef void *(*stasis_app_command_cb)(struct stasis_app_control *control,
* \param control Control object for the channel to send the command to.
* \param command Command function to execute.
* \param data Optional data to pass along with the control function.
- * \return Return value from \a command.
- * \return \c NULL on error.
+ *
+ * \return zero on success.
+ * \return error code otherwise.
*/
-void *stasis_app_send_command(struct stasis_app_control *control,
+int stasis_app_send_command(struct stasis_app_control *control,
stasis_app_command_cb command, void *data);
/*!
diff --git a/res/ari/resource_bridges.c b/res/ari/resource_bridges.c
index e09bea6b5..c07471816 100644
--- a/res/ari/resource_bridges.c
+++ b/res/ari/resource_bridges.c
@@ -172,6 +172,22 @@ static struct control_list *control_list_create(struct ast_ari_response *respons
return list;
}
+static int check_add_remove_channel(struct ast_ari_response *response,
+ struct stasis_app_control *control,
+ enum stasis_app_control_channel_result result)
+{
+ switch (result) {
+ case STASIS_APP_CHANNEL_RECORDING :
+ ast_ari_response_error(
+ response, 409, "Conflict", "Channel %s currently recording",
+ stasis_app_control_get_channel_id(control));
+ return -1;
+ case STASIS_APP_CHANNEL_OKAY:
+ return 0;
+ }
+ return 0;
+}
+
void ast_ari_bridges_add_channel(struct ast_variable *headers,
struct ast_ari_bridges_add_channel_args *args,
struct ast_ari_response *response)
@@ -179,6 +195,7 @@ void ast_ari_bridges_add_channel(struct ast_variable *headers,
RAII_VAR(struct ast_bridge *, bridge, find_bridge(response, args->bridge_id), ao2_cleanup);
RAII_VAR(struct control_list *, list, NULL, ao2_cleanup);
size_t i;
+ int has_error = 0;
if (!bridge) {
/* Response filled in by find_bridge() */
@@ -202,10 +219,16 @@ void ast_ari_bridges_add_channel(struct ast_variable *headers,
}
for (i = 0; i < list->count; ++i) {
- stasis_app_control_add_channel_to_bridge(list->controls[i], bridge);
+ if ((has_error = check_add_remove_channel(response, list->controls[i],
+ stasis_app_control_add_channel_to_bridge(
+ list->controls[i], bridge)))) {
+ break;
+ }
}
- ast_ari_response_no_content(response);
+ if (!has_error) {
+ ast_ari_response_no_content(response);
+ }
}
void ast_ari_bridges_remove_channel(struct ast_variable *headers,
diff --git a/res/res_ari_bridges.c b/res/res_ari_bridges.c
index f72a72805..a68fbf6ac 100644
--- a/res/res_ari_bridges.c
+++ b/res/res_ari_bridges.c
@@ -438,7 +438,7 @@ static void ast_ari_bridges_add_channel_cb(
case 501: /* Not Implemented */
case 400: /* Channel not found */
case 404: /* Bridge not found */
- case 409: /* Bridge not in Stasis application */
+ case 409: /* Bridge not in Stasis application; Channel currently recording */
case 422: /* Channel not in Stasis application */
is_valid = 1;
break;
diff --git a/res/res_stasis_answer.c b/res/res_stasis_answer.c
index 257afb5c6..bc7d2fba3 100644
--- a/res/res_stasis_answer.c
+++ b/res/res_stasis_answer.c
@@ -35,28 +35,25 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/module.h"
#include "asterisk/stasis_app_impl.h"
-static int OK = 0;
-static int FAIL = -1;
-
-static void *app_control_answer(struct stasis_app_control *control,
+static int app_control_answer(struct stasis_app_control *control,
struct ast_channel *chan, void *data)
{
const int delay = 0;
ast_debug(3, "%s: Answering",
stasis_app_control_get_channel_id(control));
- return __ast_answer(chan, delay) == 0 ? &OK : &FAIL;
+ return __ast_answer(chan, delay);
}
int stasis_app_control_answer(struct stasis_app_control *control)
{
- int *retval;
+ int retval;
ast_debug(3, "%s: Sending answer command\n",
stasis_app_control_get_channel_id(control));
retval = stasis_app_send_command(control, app_control_answer, NULL);
- if (retval == NULL || *retval != 0) {
+ if (retval != 0) {
ast_log(LOG_WARNING, "%s: Failed to answer channel",
stasis_app_control_get_channel_id(control));
return -1;
diff --git a/res/res_stasis_playback.c b/res/res_stasis_playback.c
index ce29de12c..f6cd5404e 100644
--- a/res/res_stasis_playback.c
+++ b/res/res_stasis_playback.c
@@ -389,7 +389,7 @@ static void remove_from_playbacks(struct stasis_app_playback *playback)
OBJ_POINTER | OBJ_UNLINK | OBJ_NODATA);
}
-static void *play_uri(struct stasis_app_control *control,
+static int play_uri(struct stasis_app_control *control,
struct ast_channel *chan, void *data)
{
RAII_VAR(struct stasis_app_playback *, playback, NULL,
@@ -400,7 +400,7 @@ static void *play_uri(struct stasis_app_control *control,
playback = data;
if (!control) {
- return NULL;
+ return -1;
}
bridge = stasis_app_get_bridge(control);
@@ -435,7 +435,7 @@ static void *play_uri(struct stasis_app_control *control,
play_on_channel(playback, chan);
}
- return NULL;
+ return 0;
}
static void set_target_uri(
diff --git a/res/res_stasis_recording.c b/res/res_stasis_recording.c
index ecf0cfa49..f0fa1e5f6 100644
--- a/res/res_stasis_recording.c
+++ b/res/res_stasis_recording.c
@@ -244,20 +244,43 @@ static void recording_publish(struct stasis_app_recording *recording, const char
stasis_app_control_publish(recording->control, message);
}
-static void recording_fail(struct stasis_app_recording *recording, const char *cause)
+
+static void recording_set_state(struct stasis_app_recording *recording,
+ enum stasis_app_recording_state state,
+ const char *cause)
{
SCOPED_AO2LOCK(lock, recording);
- recording->state = STASIS_APP_RECORDING_STATE_FAILED;
+ recording->state = state;
recording_publish(recording, cause);
}
+static enum stasis_app_control_channel_result check_rule_recording(
+ const struct stasis_app_control *control)
+{
+ return STASIS_APP_CHANNEL_RECORDING;
+}
+
+struct stasis_app_control_rule rule_recording = {
+ .check_rule = check_rule_recording
+};
+
+static void recording_fail(struct stasis_app_control *control,
+ struct stasis_app_recording *recording,
+ const char *cause)
+{
+ stasis_app_control_unregister_add_rule(control, &rule_recording);
+
+ recording_set_state(
+ recording, STASIS_APP_RECORDING_STATE_FAILED, cause);
+}
+
static void recording_cleanup(struct stasis_app_recording *recording)
{
ao2_unlink_flags(recordings, recording,
OBJ_POINTER | OBJ_UNLINK | OBJ_NODATA);
}
-static void *record_file(struct stasis_app_control *control,
+static int record_file(struct stasis_app_control *control,
struct ast_channel *chan, void *data)
{
RAII_VAR(struct stasis_app_recording *, recording,
@@ -271,8 +294,8 @@ static void *record_file(struct stasis_app_control *control,
if (stasis_app_get_bridge(control)) {
ast_log(LOG_ERROR, "Cannot record channel while in bridge\n");
- recording_fail(recording, "Cannot record channel while in bridge");
- return NULL;
+ recording_fail(control, recording, "Cannot record channel while in bridge");
+ return -1;
}
switch (recording->options->terminate_on) {
@@ -293,15 +316,12 @@ static void *record_file(struct stasis_app_control *control,
if (res != 0) {
ast_debug(3, "%s: Failed to answer\n",
ast_channel_uniqueid(chan));
- recording_fail(recording, "Failed to answer channel");
- return NULL;
+ recording_fail(control, recording, "Failed to answer channel");
+ return -1;
}
- ao2_lock(recording);
- recording->state = STASIS_APP_RECORDING_STATE_RECORDING;
- recording_publish(recording, NULL);
- ao2_unlock(recording);
-
+ recording_set_state(
+ recording, STASIS_APP_RECORDING_STATE_RECORDING, NULL);
ast_play_and_record_full(chan,
NULL, /* playfile */
recording->absolute_name,
@@ -320,12 +340,12 @@ static void *record_file(struct stasis_app_control *control,
ast_debug(3, "%s: Recording complete\n", ast_channel_uniqueid(chan));
- ao2_lock(recording);
- recording->state = STASIS_APP_RECORDING_STATE_COMPLETE;
- recording_publish(recording, NULL);
- ao2_unlock(recording);
+ recording_set_state(
+ recording, STASIS_APP_RECORDING_STATE_COMPLETE, NULL);
+
+ stasis_app_control_unregister_add_rule(control, &rule_recording);
- return NULL;
+ return 0;
}
static void recording_dtor(void *obj)
@@ -412,6 +432,8 @@ struct stasis_app_recording *stasis_app_control_record(
ao2_link(recordings, recording);
}
+ stasis_app_control_register_add_rule(control, &rule_recording);
+
/* A ref is kept in the recordings container; no need to bump */
stasis_app_send_command_async(control, record_file, recording);
diff --git a/res/stasis/command.c b/res/stasis/command.c
index f1f7f8f3b..a9e53af12 100644
--- a/res/stasis/command.c
+++ b/res/stasis/command.c
@@ -37,7 +37,7 @@ struct stasis_app_command {
ast_cond_t condition;
stasis_app_command_cb callback;
void *data;
- void *retval;
+ int retval;
int is_done:1;
};
@@ -67,7 +67,7 @@ struct stasis_app_command *command_create(
return command;
}
-static void command_complete(struct stasis_app_command *command, void *retval)
+void command_complete(struct stasis_app_command *command, int retval)
{
SCOPED_MUTEX(lock, &command->lock);
@@ -76,7 +76,7 @@ static void command_complete(struct stasis_app_command *command, void *retval)
ast_cond_signal(&command->condition);
}
-void *command_join(struct stasis_app_command *command)
+int command_join(struct stasis_app_command *command)
{
SCOPED_MUTEX(lock, &command->lock);
while (!command->is_done) {
@@ -89,7 +89,7 @@ void *command_join(struct stasis_app_command *command)
void command_invoke(struct stasis_app_command *command,
struct stasis_app_control *control, struct ast_channel *chan)
{
- void *retval = command->callback(control, chan, command->data);
+ int retval = command->callback(control, chan, command->data);
command_complete(command, retval);
}
diff --git a/res/stasis/command.h b/res/stasis/command.h
index 21f4df0c0..a99d40d0a 100644
--- a/res/stasis/command.h
+++ b/res/stasis/command.h
@@ -34,9 +34,11 @@ struct stasis_app_command;
struct stasis_app_command *command_create(
stasis_app_command_cb callback, void *data);
+void command_complete(struct stasis_app_command *command, int retval);
+
void command_invoke(struct stasis_app_command *command,
struct stasis_app_control *control, struct ast_channel *chan);
-void *command_join(struct stasis_app_command *command);
+int command_join(struct stasis_app_command *command);
#endif /* _ASTERISK_RES_STASIS_CONTROL_H */
diff --git a/res/stasis/control.c b/res/stasis/control.c
index 14c9f57f5..6b092918e 100644
--- a/res/stasis/control.c
+++ b/res/stasis/control.c
@@ -40,6 +40,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/musiconhold.h"
#include "asterisk/app.h"
+AST_LIST_HEAD(app_control_rules, stasis_app_control_rule);
+
struct stasis_app_control {
ast_cond_t wait_cond;
/*! Queue of commands to dispatch on the channel */
@@ -59,6 +61,14 @@ struct stasis_app_control {
*/
struct ast_pbx *pbx;
/*!
+ * 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;
+ /*!
* Silence generator, when silence is being generated.
*/
struct ast_silence_generator *silgen;
@@ -72,6 +82,9 @@ static void control_dtor(void *obj)
{
struct stasis_app_control *control = obj;
+ AST_LIST_HEAD_DESTROY(&control->add_rules);
+ AST_LIST_HEAD_DESTROY(&control->remove_rules);
+
/* We may have a lingering silence generator; free it */
ast_channel_stop_silence_generator(control->channel, control->silgen);
control->silgen = NULL;
@@ -106,22 +119,121 @@ struct stasis_app_control *control_create(struct ast_channel *channel)
control->channel = channel;
+ AST_LIST_HEAD_INIT(&control->add_rules);
+ AST_LIST_HEAD_INIT(&control->remove_rules);
+
ao2_ref(control, +1);
return control;
}
-static void *noop_cb(struct stasis_app_control *control,
+static void app_control_register_rule(
+ const struct stasis_app_control *control,
+ 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(
+ const struct stasis_app_control *control,
+ 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,
struct ast_channel *chan, void *data)
{
- return NULL;
+ return 0;
}
+/*! 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);
-static struct stasis_app_command *exec_command(
+static struct stasis_app_command *exec_command_on_condition(
struct stasis_app_control *control, stasis_app_command_cb command_fn,
- void *data)
+ void *data, app_command_can_exec_cb can_exec_fn)
{
- RAII_VAR(struct stasis_app_command *, command, NULL, ao2_cleanup);
+ int retval;
+ struct stasis_app_command *command;
command_fn = command_fn ? : noop_cb;
@@ -131,24 +243,36 @@ static struct stasis_app_command *exec_command(
}
ao2_lock(control->command_queue);
+ if (can_exec_fn && (retval = can_exec_fn(control))) {
+ ao2_unlock(control->command_queue);
+ command_complete(command, retval);
+ return command;
+ }
+
ao2_link_flags(control->command_queue, command, OBJ_NOLOCK);
ast_cond_signal(&control->wait_cond);
ao2_unlock(control->command_queue);
- ao2_ref(command, +1);
return command;
}
+static struct stasis_app_command *exec_command(
+ struct stasis_app_control *control, stasis_app_command_cb command_fn,
+ void *data)
+{
+ return exec_command_on_condition(control, command_fn, data, NULL);
+}
+
struct stasis_app_control_dial_data {
char endpoint[AST_CHANNEL_NAME];
int timeout;
};
-static void *app_control_add_channel_to_bridge(
+static int app_control_add_channel_to_bridge(
struct stasis_app_control *control,
struct ast_channel *chan, void *data);
-static void *app_control_dial(struct stasis_app_control *control,
+static int app_control_dial(struct stasis_app_control *control,
struct ast_channel *chan, void *data)
{
RAII_VAR(struct ast_dial *, dial, ast_dial_create(), ast_dial_destroy);
@@ -160,30 +284,30 @@ static void *app_control_dial(struct stasis_app_control *control,
tech = dial_data->endpoint;
if (!(resource = strchr(tech, '/'))) {
- return NULL;
+ return -1;
}
*resource++ = '\0';
if (!dial) {
ast_log(LOG_ERROR, "Failed to create dialing structure.\n");
- return NULL;
+ return -1;
}
if (ast_dial_append(dial, tech, resource) < 0) {
ast_log(LOG_ERROR, "Failed to add %s/%s to dialing structure.\n", tech, resource);
- return NULL;
+ return -1;
}
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))) {
- return NULL;
+ return -1;
}
if (!(bridge = ast_bridge_basic_new())) {
ast_log(LOG_ERROR, "Failed to create basic bridge.\n");
- return NULL;
+ return -1;
}
if (ast_bridge_impart(bridge, new_chan, NULL, NULL,
@@ -193,7 +317,7 @@ static void *app_control_dial(struct stasis_app_control *control,
app_control_add_channel_to_bridge(control, chan, bridge);
}
- return NULL;
+ return 0;
}
int stasis_app_control_dial(struct stasis_app_control *control, const char *endpoint, const char *exten, const char *context,
@@ -248,7 +372,7 @@ struct stasis_app_control_continue_data {
int priority;
};
-static void *app_control_continue(struct stasis_app_control *control,
+static int app_control_continue(struct stasis_app_control *control,
struct ast_channel *chan, void *data)
{
RAII_VAR(struct stasis_app_control_continue_data *, continue_data, data, ast_free);
@@ -266,7 +390,7 @@ static void *app_control_continue(struct stasis_app_control *control,
control->is_done = 1;
- return NULL;
+ return 0;
}
int stasis_app_control_continue(struct stasis_app_control *control, const char *context, const char *extension, int priority)
@@ -297,7 +421,7 @@ struct stasis_app_control_dtmf_data {
char dtmf[];
};
-static void *app_control_dtmf(struct stasis_app_control *control,
+static int app_control_dtmf(struct stasis_app_control *control,
struct ast_channel *chan, void *data)
{
RAII_VAR(struct stasis_app_control_dtmf_data *, dtmf_data, data, ast_free);
@@ -312,7 +436,7 @@ static void *app_control_dtmf(struct stasis_app_control *control,
ast_safe_sleep(chan, dtmf_data->after);
}
- return NULL;
+ return 0;
}
int stasis_app_control_dtmf(struct stasis_app_control *control, const char *dtmf, int before, int between, unsigned int duration, int after)
@@ -334,12 +458,12 @@ int stasis_app_control_dtmf(struct stasis_app_control *control, const char *dtmf
return 0;
}
-static void *app_control_ring(struct stasis_app_control *control,
+static int app_control_ring(struct stasis_app_control *control,
struct ast_channel *chan, void *data)
{
ast_indicate(control->channel, AST_CONTROL_RINGING);
- return NULL;
+ return 0;
}
int stasis_app_control_ring(struct stasis_app_control *control)
@@ -349,12 +473,12 @@ int stasis_app_control_ring(struct stasis_app_control *control)
return 0;
}
-static void *app_control_ring_stop(struct stasis_app_control *control,
+static int app_control_ring_stop(struct stasis_app_control *control,
struct ast_channel *chan, void *data)
{
ast_indicate(control->channel, -1);
- return NULL;
+ return 0;
}
int stasis_app_control_ring_stop(struct stasis_app_control *control)
@@ -369,7 +493,7 @@ struct stasis_app_control_mute_data {
unsigned int direction;
};
-static void *app_control_mute(struct stasis_app_control *control,
+static int app_control_mute(struct stasis_app_control *control,
struct ast_channel *chan, void *data)
{
RAII_VAR(struct stasis_app_control_mute_data *, mute_data, data, ast_free);
@@ -377,7 +501,7 @@ static void *app_control_mute(struct stasis_app_control *control,
ast_channel_suppress(control->channel, mute_data->direction, mute_data->frametype);
- return NULL;
+ return 0;
}
int stasis_app_control_mute(struct stasis_app_control *control, unsigned int direction, enum ast_frame_type frametype)
@@ -396,7 +520,7 @@ int stasis_app_control_mute(struct stasis_app_control *control, unsigned int dir
return 0;
}
-static void *app_control_unmute(struct stasis_app_control *control,
+static int app_control_unmute(struct stasis_app_control *control,
struct ast_channel *chan, void *data)
{
RAII_VAR(struct stasis_app_control_mute_data *, mute_data, data, ast_free);
@@ -404,7 +528,7 @@ static void *app_control_unmute(struct stasis_app_control *control,
ast_channel_unsuppress(control->channel, mute_data->direction, mute_data->frametype);
- return NULL;
+ return 0;
}
int stasis_app_control_unmute(struct stasis_app_control *control, unsigned int direction, enum ast_frame_type frametype)
@@ -456,12 +580,12 @@ int stasis_app_control_set_channel_var(struct stasis_app_control *control, const
return pbx_builtin_setvar_helper(control->channel, variable, value);
}
-static void *app_control_hold(struct stasis_app_control *control,
+static int app_control_hold(struct stasis_app_control *control,
struct ast_channel *chan, void *data)
{
ast_indicate(control->channel, AST_CONTROL_HOLD);
- return NULL;
+ return 0;
}
void stasis_app_control_hold(struct stasis_app_control *control)
@@ -469,12 +593,12 @@ void stasis_app_control_hold(struct stasis_app_control *control)
stasis_app_send_command_async(control, app_control_hold, NULL);
}
-static void *app_control_unhold(struct stasis_app_control *control,
+static int app_control_unhold(struct stasis_app_control *control,
struct ast_channel *chan, void *data)
{
ast_indicate(control->channel, AST_CONTROL_UNHOLD);
- return NULL;
+ return 0;
}
void stasis_app_control_unhold(struct stasis_app_control *control)
@@ -482,7 +606,7 @@ void stasis_app_control_unhold(struct stasis_app_control *control)
stasis_app_send_command_async(control, app_control_unhold, NULL);
}
-static void *app_control_moh_start(struct stasis_app_control *control,
+static int app_control_moh_start(struct stasis_app_control *control,
struct ast_channel *chan, void *data)
{
char *moh_class = data;
@@ -490,7 +614,7 @@ static void *app_control_moh_start(struct stasis_app_control *control,
ast_moh_start(chan, moh_class, NULL);
ast_free(moh_class);
- return NULL;
+ return 0;
}
void stasis_app_control_moh_start(struct stasis_app_control *control, const char *moh_class)
@@ -504,11 +628,11 @@ void stasis_app_control_moh_start(struct stasis_app_control *control, const char
stasis_app_send_command_async(control, app_control_moh_start, data);
}
-static void *app_control_moh_stop(struct stasis_app_control *control,
+static int app_control_moh_stop(struct stasis_app_control *control,
struct ast_channel *chan, void *data)
{
ast_moh_stop(chan);
- return NULL;
+ return 0;
}
void stasis_app_control_moh_stop(struct stasis_app_control *control)
@@ -516,7 +640,7 @@ void stasis_app_control_moh_stop(struct stasis_app_control *control)
stasis_app_send_command_async(control, app_control_moh_stop, NULL);
}
-static void *app_control_silence_start(struct stasis_app_control *control,
+static int app_control_silence_start(struct stasis_app_control *control,
struct ast_channel *chan, void *data)
{
if (control->silgen) {
@@ -538,7 +662,7 @@ static void *app_control_silence_start(struct stasis_app_control *control,
stasis_app_control_get_channel_id(control));
}
- return NULL;
+ return 0;
}
void stasis_app_control_silence_start(struct stasis_app_control *control)
@@ -546,7 +670,7 @@ void stasis_app_control_silence_start(struct stasis_app_control *control)
stasis_app_send_command_async(control, app_control_silence_start, NULL);
}
-static void *app_control_silence_stop(struct stasis_app_control *control,
+static int app_control_silence_stop(struct stasis_app_control *control,
struct ast_channel *chan, void *data)
{
if (control->silgen) {
@@ -557,7 +681,7 @@ static void *app_control_silence_stop(struct stasis_app_control *control,
control->silgen = NULL;
}
- return NULL;
+ return 0;
}
void stasis_app_control_silence_stop(struct stasis_app_control *control)
@@ -584,23 +708,31 @@ struct ast_channel_snapshot *stasis_app_control_get_snapshot(
return snapshot;
}
-void *stasis_app_send_command(struct stasis_app_control *control,
- stasis_app_command_cb command_fn, void *data)
+static int app_send_command_on_condition(struct stasis_app_control *control,
+ stasis_app_command_cb command_fn, void *data,
+ app_command_can_exec_cb can_exec_fn)
{
RAII_VAR(struct stasis_app_command *, command, NULL, ao2_cleanup);
if (control == NULL) {
- return NULL;
+ return -1;
}
- command = exec_command(control, command_fn, data);
+ command = exec_command_on_condition(
+ control, command_fn, data, can_exec_fn);
if (!command) {
- return NULL;
+ return -1;
}
return command_join(command);
}
+int stasis_app_send_command(struct stasis_app_control *control,
+ stasis_app_command_cb command_fn, void *data)
+{
+ return app_send_command_on_condition(control, command_fn, data, NULL);
+}
+
int stasis_app_send_command_async(struct stasis_app_control *control,
stasis_app_command_cb command_fn, void *data)
{
@@ -628,7 +760,7 @@ struct ast_bridge *stasis_app_get_bridge(struct stasis_app_control *control)
}
}
-static void *bridge_channel_depart(struct stasis_app_control *control,
+static int bridge_channel_depart(struct stasis_app_control *control,
struct ast_channel *chan, void *data)
{
RAII_VAR(struct ast_bridge_channel *, bridge_channel, data, ao2_cleanup);
@@ -639,7 +771,7 @@ static void *bridge_channel_depart(struct stasis_app_control *control,
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));
- return NULL;
+ return -1;
}
}
@@ -648,7 +780,7 @@ static void *bridge_channel_depart(struct stasis_app_control *control,
ast_bridge_depart(chan);
- return NULL;
+ return 0;
}
static void bridge_after_cb(struct ast_channel *chan, void *data)
@@ -691,10 +823,7 @@ static void bridge_after_cb_failed(enum ast_bridge_after_cb_reason reason,
ast_bridge_after_cb_reason_string(reason));
}
-static int OK = 0;
-static int FAIL = -1;
-
-static void *app_control_add_channel_to_bridge(
+static int app_control_add_channel_to_bridge(
struct stasis_app_control *control,
struct ast_channel *chan, void *data)
{
@@ -702,7 +831,7 @@ static void *app_control_add_channel_to_bridge(
int res;
if (!control || !bridge) {
- return NULL;
+ return -1;
}
ast_debug(3, "%s: Adding to bridge %s\n",
@@ -726,7 +855,7 @@ static void *app_control_add_channel_to_bridge(
bridge_after_cb_failed, control);
if (res != 0) {
ast_log(LOG_ERROR, "Error setting after-bridge callback\n");
- return &FAIL;
+ return -1;
}
{
@@ -752,34 +881,34 @@ static void *app_control_add_channel_to_bridge(
ast_log(LOG_ERROR, "Error adding channel to bridge\n");
ast_channel_pbx_set(chan, control->pbx);
control->pbx = NULL;
- return &FAIL;
+ return -1;
}
ast_assert(stasis_app_get_bridge(control) == NULL);
control->bridge = bridge;
}
- return &OK;
+ return 0;
}
int stasis_app_control_add_channel_to_bridge(
struct stasis_app_control *control, struct ast_bridge *bridge)
{
- int *res;
ast_debug(3, "%s: Sending channel add_to_bridge command\n",
stasis_app_control_get_channel_id(control));
- res = stasis_app_send_command(control,
- app_control_add_channel_to_bridge, bridge);
- return *res;
+
+ return app_send_command_on_condition(
+ control, app_control_add_channel_to_bridge, bridge,
+ app_control_can_add_channel_to_bridge);
}
-static void *app_control_remove_channel_from_bridge(
+static int app_control_remove_channel_from_bridge(
struct stasis_app_control *control,
struct ast_channel *chan, void *data)
{
struct ast_bridge *bridge = data;
if (!control) {
- return &FAIL;
+ return -1;
}
/* We should only depart from our own bridge */
@@ -791,22 +920,21 @@ static void *app_control_remove_channel_from_bridge(
ast_log(LOG_WARNING, "%s: Not in bridge %s; not removing\n",
stasis_app_control_get_channel_id(control),
bridge->uniqueid);
- return &FAIL;
+ return -1;
}
ast_bridge_depart(chan);
- return &OK;
+ return 0;
}
int stasis_app_control_remove_channel_from_bridge(
struct stasis_app_control *control, struct ast_bridge *bridge)
{
- int *res;
ast_debug(3, "%s: Sending channel remove_from_bridge command\n",
stasis_app_control_get_channel_id(control));
- res = stasis_app_send_command(control,
- app_control_remove_channel_from_bridge, bridge);
- return *res;
+ return app_send_command_on_condition(
+ control, app_control_remove_channel_from_bridge, bridge,
+ app_control_can_remove_channel_from_bridge);
}
const char *stasis_app_control_get_channel_id(
diff --git a/rest-api/api-docs/bridges.json b/rest-api/api-docs/bridges.json
index 2339a9671..d7f63ad14 100644
--- a/rest-api/api-docs/bridges.json
+++ b/rest-api/api-docs/bridges.json
@@ -140,7 +140,7 @@
},
{
"code": 409,
- "reason": "Bridge not in Stasis application"
+ "reason": "Bridge not in Stasis application; Channel currently recording"
},
{
"code": 422,