static int app_control_silence_start(struct stasis_app_control *control, struct ast_channel *chan, void *data) { if (ast_channel_state(chan) != AST_STATE_UP) { ast_indicate(chan, AST_CONTROL_PROGRESS); } 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)); } return 0; }
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 -1; } /* 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); return -1; } ast_bridge_depart(chan); return 0; }
static void recording_publish(struct stasis_app_recording *recording, const char *cause) { RAII_VAR(struct ast_json *, json, NULL, ast_json_unref); RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup); ast_assert(recording != NULL); json = stasis_app_recording_to_json(recording); if (json == NULL) { return; } if (!ast_strlen_zero(cause)) { struct ast_json *failure_cause = ast_json_string_create(cause); if (!failure_cause) { return; } if (ast_json_object_set(json, "cause", failure_cause)) { return; } } message = ast_channel_blob_create_from_cache( stasis_app_control_get_channel_id(recording->control), stasis_app_recording_snapshot_type(), json); if (message == NULL) { return; } stasis_app_control_publish(recording->control, message); }
static int app_control_answer(struct stasis_app_control *control, struct ast_channel *chan, void *data) { ast_debug(3, "%s: Answering\n", stasis_app_control_get_channel_id(control)); return ast_raw_answer(chan); }
int stasis_app_control_answer(struct stasis_app_control *control) { 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 != 0) { ast_log(LOG_WARNING, "%s: Failed to answer channel\n", stasis_app_control_get_channel_id(control)); return -1; } return 0; }
static void *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; }
int stasis_app_control_remove_channel_from_bridge( 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)); return app_send_command_on_condition( control, app_control_remove_channel_from_bridge, bridge, app_control_can_remove_channel_from_bridge); }
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; }
void control_silence_stop_now(struct stasis_app_control *control) { 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; } }
int stasis_app_control_add_channel_to_bridge( 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)); return app_send_command_on_condition( control, control_add_channel_to_bridge, bridge, NULL, app_control_can_add_channel_to_bridge); }
static int app_control_silence_stop(struct stasis_app_control *control, 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; } return 0; }
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; }
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; msg = stasis_cache_get(ast_channel_cache(), ast_channel_snapshot_type(), 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; }
static void recording_publish(struct stasis_app_recording *recording) { RAII_VAR(struct ast_json *, json, NULL, ast_json_unref); RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup); RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup); ast_assert(recording != NULL); json = stasis_app_recording_to_json(recording); if (json == NULL) { return; } message = ast_channel_blob_create_from_cache( stasis_app_control_get_channel_id(recording->control), stasis_app_recording_snapshot_type(), json); if (message == NULL) { return; } stasis_app_control_publish(recording->control, message); }
int control_swap_channel_in_bridge(struct stasis_app_control *control, struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap) { int res; if (!control || !bridge) { return -1; } 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"); return -1; } { /* 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); /* 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); /* 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, swap, NULL, /* features */ AST_BRIDGE_IMPART_CHAN_DEPARTABLE); if (res != 0) { ast_log(LOG_ERROR, "Error adding channel to bridge\n"); ast_channel_pbx_set(chan, control->pbx); control->pbx = NULL; return -1; } ast_assert(stasis_app_get_bridge(control) == NULL); control->bridge = bridge; ast_channel_lock(chan); set_interval_hook(chan); ast_channel_unlock(chan); } return 0; }
struct stasis_app_recording *stasis_app_control_record( struct stasis_app_control *control, struct stasis_app_recording_options *options) { struct stasis_app_recording *recording; char *last_slash; errno = 0; if (options == NULL || ast_strlen_zero(options->name) || ast_strlen_zero(options->format) || options->max_silence_seconds < 0 || options->max_duration_seconds < 0) { errno = EINVAL; return NULL; } ast_debug(3, "%s: Sending record(%s.%s) command\n", stasis_app_control_get_channel_id(control), options->name, options->format); recording = ao2_alloc(sizeof(*recording), recording_dtor); if (!recording) { errno = ENOMEM; return NULL; } recording->duration.total = -1; recording->duration.energy_only = -1; ast_asprintf(&recording->absolute_name, "%s/%s", ast_config_AST_RECORDING_DIR, options->name); if (recording->absolute_name == NULL) { errno = ENOMEM; ao2_ref(recording, -1); return NULL; } if ((last_slash = strrchr(recording->absolute_name, '/'))) { *last_slash = '\0'; if (ast_safe_mkdir(ast_config_AST_RECORDING_DIR, recording->absolute_name, 0777) != 0) { /* errno set by ast_mkdir */ ao2_ref(recording, -1); return NULL; } *last_slash = '/'; } ao2_ref(options, +1); recording->options = options; recording->control = control; recording->state = STASIS_APP_RECORDING_STATE_QUEUED; if ((recording->options->if_exists == AST_RECORD_IF_EXISTS_FAIL) && (ast_fileexists(recording->absolute_name, NULL, NULL))) { ast_log(LOG_WARNING, "Recording file '%s' already exists and ifExists option is failure.\n", recording->absolute_name); errno = EEXIST; ao2_ref(recording, -1); return NULL; } { RAII_VAR(struct stasis_app_recording *, old_recording, NULL, ao2_cleanup); SCOPED_AO2LOCK(lock, recordings); old_recording = ao2_find(recordings, options->name, OBJ_KEY | OBJ_NOLOCK); if (old_recording) { ast_log(LOG_WARNING, "Recording %s already in progress\n", recording->options->name); errno = EEXIST; ao2_ref(recording, -1); return NULL; } ao2_link(recordings, recording); } stasis_app_control_register_add_rule(control, &rule_recording); stasis_app_send_command_async(control, record_file, ao2_bump(recording), recording_cleanup); return recording; }
struct stasis_app_recording *stasis_app_control_record( struct stasis_app_control *control, struct stasis_app_recording_options *options) { RAII_VAR(struct stasis_app_recording *, recording, NULL, ao2_cleanup); char *last_slash; errno = 0; if (options == NULL || ast_strlen_zero(options->name) || ast_strlen_zero(options->format) || options->max_silence_seconds < 0 || options->max_duration_seconds < 0) { errno = EINVAL; return NULL; } ast_debug(3, "%s: Sending record(%s.%s) command\n", stasis_app_control_get_channel_id(control), options->name, options->format); recording = ao2_alloc(sizeof(*recording), recording_dtor); if (!recording) { errno = ENOMEM; return NULL; } ast_asprintf(&recording->absolute_name, "%s/%s", ast_config_AST_RECORDING_DIR, options->name); if (recording->absolute_name == NULL) { errno = ENOMEM; return NULL; } if ((last_slash = strrchr(recording->absolute_name, '/'))) { *last_slash = '\0'; if (ast_safe_mkdir(ast_config_AST_RECORDING_DIR, recording->absolute_name, 0777) != 0) { /* errno set by ast_mkdir */ return NULL; } *last_slash = '/'; } ao2_ref(options, +1); recording->options = options; recording->control = control; recording->state = STASIS_APP_RECORDING_STATE_QUEUED; { RAII_VAR(struct stasis_app_recording *, old_recording, NULL, ao2_cleanup); SCOPED_AO2LOCK(lock, recordings); old_recording = ao2_find(recordings, options->name, OBJ_KEY | OBJ_NOLOCK); if (old_recording) { ast_log(LOG_WARNING, "Recording %s already in progress\n", recording->options->name); errno = EEXIST; return NULL; } ao2_link(recordings, recording); } /* A ref is kept in the recordings container; no need to bump */ stasis_app_send_command_async(control, record_file, recording); /* Although this should be bumped for the caller */ ao2_ref(recording, +1); return recording; }
int control_swap_channel_in_bridge(struct stasis_app_control *control, struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap) { int res; struct ast_bridge_features *features; if (!control || !bridge) { return -1; } 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"); return -1; } ao2_lock(control); /* 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); /* 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); } /* Pull bridge features from the control */ features = control->bridge_features; control->bridge_features = NULL; ast_assert(stasis_app_get_bridge(control) == NULL); /* We need to set control->bridge here since bridge_after_cb may be run * before ast_bridge_impart returns. bridge_after_cb gets a reason * code so it can tell if the bridge is actually valid or not. */ control->bridge = bridge; /* We can't be holding the control lock while impart is running * or we could create a deadlock with bridge_after_cb which also * tries to lock control. */ ao2_unlock(control); res = ast_bridge_impart(bridge, chan, swap, features, /* features */ AST_BRIDGE_IMPART_CHAN_DEPARTABLE); if (res != 0) { /* ast_bridge_impart failed before it could spawn the depart * thread. The callbacks aren't called in this case. * The impart could still fail even if ast_bridge_impart returned * ok but that's handled by bridge_after_cb. */ ast_log(LOG_ERROR, "Error adding channel to bridge\n"); ao2_lock(control); ast_channel_pbx_set(chan, control->pbx); control->pbx = NULL; control->bridge = NULL; ao2_unlock(control); } else { ast_channel_lock(chan); set_interval_hook(chan); ast_channel_unlock(chan); } return res; }
static int app_control_add_channel_to_bridge( struct stasis_app_control *control, struct ast_channel *chan, void *data) { struct ast_bridge *bridge = data; int res; if (!control || !bridge) { return -1; } 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"); return -1; } { /* 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); /* 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 */ AST_BRIDGE_IMPART_CHAN_DEPARTABLE); if (res != 0) { ast_log(LOG_ERROR, "Error adding channel to bridge\n"); ast_channel_pbx_set(chan, control->pbx); control->pbx = NULL; return -1; } ast_assert(stasis_app_get_bridge(control) == NULL); control->bridge = bridge; } return 0; }