diff --git a/include/asterisk/stasis_app.h b/include/asterisk/stasis_app.h index 0c22a6c309764c7adce9b9ab5b6a1191ca1de629..56e039b43eea4b107f7843f616d1608745968807 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. @@ -581,6 +635,16 @@ struct ast_channel *stasis_app_bridge_moh_channel( 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. * diff --git a/include/asterisk/stasis_app_impl.h b/include/asterisk/stasis_app_impl.h index d4b467756427dd9da6a8dbe68860bb0c5814705e..a8c8c05866c1e42e27f9eab83470c48849d73722 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 e09bea6b56acfd74f0f254792fa48789e5a42f4b..c074718160703fad4889525d92d51a83cba8d9c5 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 f72a728053a96ed462f56152cc119ca92c4db28c..a68fbf6ac0194dd592d93bcce2e9b7e4a2665b67 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 257afb5c6ba0809119439e786899dc46ce816a08..bc7d2fba314c661721f9234068350da8af59258a 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 ce29de12ce38ce3d842e4500fc8830f2a1de632a..f6cd5404e2dbcc90a4ad315bf1f7844796dee6d8 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 ecf0cfa49f51f39bfa8db6053b5e51b9a909ff1e..f0fa1e5f6fdb71c7e1817ec535f85ab1e9bf6ef2 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 f1f7f8f3b4903d15f1910d4a95979e3284e540a4..a9e53af12581102c0fd3f76924a99245eb2d5366 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 21f4df0c0ab4e40828400356e3870d065c7bfe99..a99d40d0a6e071c66444d9f18de85cdc5fce228c 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 14c9f57f5bb5c49e04ec1b6c6649424a1d1e95f7..6b092918edd28d965d653a4529bd3c164775ecfb 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 */ @@ -58,6 +60,14 @@ struct stasis_app_control { * Holding place for channel's PBX while imparted to a bridge. */ 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. */ @@ -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 2339a96711d364c82ed31ee08630990be7efa218..d7f63ad144bad912f930b211af518e6530d088ef 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,