summaryrefslogtreecommitdiff
path: root/res/stasis/control.c
diff options
context:
space:
mode:
Diffstat (limited to 'res/stasis/control.c')
-rw-r--r--res/stasis/control.c260
1 files changed, 194 insertions, 66 deletions
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(