/*! Forward a bridge's topics to an app */ static struct app_forwards *forwards_create_bridge(struct stasis_app *app, struct ast_bridge *bridge) { RAII_VAR(struct app_forwards *, forwards, NULL, ao2_cleanup); if (!app || !bridge) { return NULL; } forwards = forwards_create(app, bridge->uniqueid); if (!forwards) { return NULL; } forwards->forward_type = FORWARD_BRIDGE; forwards->topic_forward = stasis_forward_all(ast_bridge_topic(bridge), app->topic); if (!forwards->topic_forward) { return NULL; } forwards->topic_cached_forward = stasis_forward_all( ast_bridge_topic_cached(bridge), app->topic); if (!forwards->topic_cached_forward) { /* Half-subscribed is a bad thing */ stasis_forward_cancel(forwards->topic_forward); forwards->topic_forward = NULL; return NULL; } ao2_ref(forwards, +1); return forwards; }
/*! Forward a endpoint's topics to an app */ static struct app_forwards *forwards_create_endpoint(struct stasis_app *app, struct ast_endpoint *endpoint) { RAII_VAR(struct app_forwards *, forwards, NULL, ao2_cleanup); if (!app || !endpoint) { return NULL; } forwards = forwards_create(app, ast_endpoint_get_id(endpoint)); if (!forwards) { return NULL; } forwards->forward_type = FORWARD_ENDPOINT; forwards->topic_forward = stasis_forward_all(ast_endpoint_topic(endpoint), app->topic); if (!forwards->topic_forward) { return NULL; } forwards->topic_cached_forward = stasis_forward_all( ast_endpoint_topic_cached(endpoint), app->topic); if (!forwards->topic_cached_forward) { /* Half-subscribed is a bad thing */ stasis_forward_cancel(forwards->topic_forward); forwards->topic_forward = NULL; return NULL; } ao2_ref(forwards, +1); return forwards; }
/*! Forward a bridge's topics to an app */ static struct app_forwards *forwards_create_bridge(struct stasis_app *app, struct ast_bridge *bridge) { struct app_forwards *forwards; if (!app) { return NULL; } forwards = forwards_create(app, bridge ? bridge->uniqueid : BRIDGE_ALL); if (!forwards) { return NULL; } forwards->forward_type = FORWARD_BRIDGE; if (bridge) { forwards->topic_forward = stasis_forward_all(ast_bridge_topic(bridge), app->topic); } forwards->topic_cached_forward = stasis_forward_all( bridge ? ast_bridge_topic_cached(bridge) : ast_bridge_topic_all_cached(), app->topic); if ((!forwards->topic_forward && bridge) || !forwards->topic_cached_forward) { /* Half-subscribed is a bad thing */ forwards_unsubscribe(forwards); ao2_ref(forwards, -1); return NULL; } return forwards; }
/*! Forward a channel's topics to an app */ static struct app_forwards *forwards_create_channel(struct stasis_app *app, struct ast_channel *chan) { RAII_VAR(struct app_forwards *, forwards, NULL, ao2_cleanup); if (!app || !chan) { return NULL; } forwards = forwards_create(app, ast_channel_uniqueid(chan)); if (!forwards) { return NULL; } forwards->forward_type = FORWARD_CHANNEL; forwards->topic_forward = stasis_forward_all(ast_channel_topic(chan), app->topic); if (!forwards->topic_forward) { return NULL; } forwards->topic_cached_forward = stasis_forward_all( ast_channel_topic_cached(chan), app->topic); if (!forwards->topic_cached_forward) { /* Half-subscribed is a bad thing */ stasis_forward_cancel(forwards->topic_forward); forwards->topic_forward = NULL; return NULL; } ao2_ref(forwards, +1); return forwards; }
/*! Forward a channel's topics to an app */ static struct app_forwards *forwards_create_channel(struct stasis_app *app, struct ast_channel *chan) { struct app_forwards *forwards; if (!app) { return NULL; } forwards = forwards_create(app, chan ? ast_channel_uniqueid(chan) : CHANNEL_ALL); if (!forwards) { return NULL; } forwards->forward_type = FORWARD_CHANNEL; if (chan) { forwards->topic_forward = stasis_forward_all(ast_channel_topic(chan), app->topic); } forwards->topic_cached_forward = stasis_forward_all( chan ? ast_channel_topic_cached(chan) : ast_channel_topic_all_cached(), app->topic); if ((!forwards->topic_forward && chan) || !forwards->topic_cached_forward) { /* Half-subscribed is a bad thing */ forwards_unsubscribe(forwards); ao2_ref(forwards, -1); return NULL; } return forwards; }
struct stasis_cp_single *stasis_cp_single_create(struct stasis_cp_all *all, const char *name) { RAII_VAR(struct stasis_cp_single *, one, NULL, ao2_cleanup); one = ao2_alloc(sizeof(*one), one_dtor); if (!one) { return NULL; } one->topic = stasis_topic_create(name); if (!one->topic) { return NULL; } one->topic_cached = stasis_caching_topic_create(one->topic, all->cache); if (!one->topic_cached) { return NULL; } one->forward_topic_to_all = stasis_forward_all(one->topic, all->topic); if (!one->forward_topic_to_all) { return NULL; } one->forward_cached_to_all = stasis_forward_all( stasis_caching_get_topic(one->topic_cached), all->topic_cached); if (!one->forward_cached_to_all) { return NULL; } ao2_ref(one, +1); return one; }
/*! Forward a endpoint's topics to an app */ static struct app_forwards *forwards_create_endpoint(struct stasis_app *app, struct ast_endpoint *endpoint) { struct app_forwards *forwards; int ret = 0; if (!app) { return NULL; } forwards = forwards_create(app, endpoint ? ast_endpoint_get_id(endpoint) : ENDPOINT_ALL); if (!forwards) { return NULL; } forwards->forward_type = FORWARD_ENDPOINT; if (endpoint) { forwards->topic_forward = stasis_forward_all(ast_endpoint_topic(endpoint), app->topic); forwards->topic_cached_forward = stasis_forward_all( ast_endpoint_topic_cached(endpoint), app->topic); if (!forwards->topic_forward || !forwards->topic_cached_forward) { /* Half-subscribed is a bad thing */ forwards_unsubscribe(forwards); ao2_ref(forwards, -1); return NULL; } } else { /* Since endpoint subscriptions also subscribe to channels, in the case * of all endpoint subscriptions, we only want messages for the endpoints. * As such, we route those particular messages and then re-publish them * on the app's topic. */ ast_assert(app->endpoint_router == NULL); app->endpoint_router = stasis_message_router_create(ast_endpoint_topic_all_cached()); if (!app->endpoint_router) { forwards_unsubscribe(forwards); ao2_ref(forwards, -1); return NULL; } ret |= stasis_message_router_add(app->endpoint_router, ast_endpoint_state_type(), endpoint_state_cb, app); ret |= stasis_message_router_add(app->endpoint_router, ast_endpoint_contact_state_type(), endpoint_state_cb, app); if (ret) { ao2_ref(app->endpoint_router, -1); app->endpoint_router = NULL; ao2_ref(forwards, -1); return NULL; } } return forwards; }
struct stasis_topic *stasis_topic_pool_get_topic(struct stasis_topic_pool *pool, const char *topic_name) { RAII_VAR(struct topic_pool_entry *, topic_pool_entry, NULL, ao2_cleanup); SCOPED_AO2LOCK(topic_container_lock, pool->pool_container); topic_pool_entry = ao2_find(pool->pool_container, topic_name, OBJ_SEARCH_KEY | OBJ_NOLOCK); if (topic_pool_entry) { return topic_pool_entry->topic; } topic_pool_entry = topic_pool_entry_alloc(); if (!topic_pool_entry) { return NULL; } topic_pool_entry->topic = stasis_topic_create(topic_name); if (!topic_pool_entry->topic) { return NULL; } topic_pool_entry->forward = stasis_forward_all(topic_pool_entry->topic, pool->pool_topic); if (!topic_pool_entry->forward) { return NULL; } if (!ao2_link_flags(pool->pool_container, topic_pool_entry, OBJ_NOLOCK)) { return NULL; } return topic_pool_entry->topic; }
struct stasis_cp_all *stasis_cp_all_create(const char *name, snapshot_get_id id_fn) { char *cached_name = NULL; struct stasis_cp_all *all; static int cache_id; all = ao2_t_alloc(sizeof(*all), all_dtor, name); if (!all) { return NULL; } ast_asprintf(&cached_name, "cache_pattern:%d/%s", ast_atomic_fetchadd_int(&cache_id, +1), name); if (!cached_name) { ao2_ref(all, -1); return NULL; } all->topic = stasis_topic_create(name); all->topic_cached = stasis_topic_create(cached_name); ast_free(cached_name); all->cache = stasis_cache_create(id_fn); all->forward_all_to_cached = stasis_forward_all(all->topic, all->topic_cached); if (!all->topic || !all->topic_cached || !all->cache || !all->forward_all_to_cached) { ao2_ref(all, -1); return NULL; } return all; }
int manager_bridging_init(void) { int ret = 0; struct stasis_topic *manager_topic; struct stasis_topic *bridge_topic; if (bridge_state_router) { /* Already initialized */ return 0; } ast_register_cleanup(manager_bridging_cleanup); manager_topic = ast_manager_get_topic(); if (!manager_topic) { return -1; } bridge_topic = ast_bridge_topic_all_cached(); if (!bridge_topic) { return -1; } topic_forwarder = stasis_forward_all(bridge_topic, manager_topic); if (!topic_forwarder) { return -1; } bridge_state_router = ast_manager_get_message_router(); if (!bridge_state_router) { return -1; } ret |= stasis_message_router_add_cache_update(bridge_state_router, ast_bridge_snapshot_type(), bridge_snapshot_update, NULL); ret |= stasis_message_router_add(bridge_state_router, ast_bridge_merge_message_type(), bridge_merge_cb, NULL); ret |= stasis_message_router_add(bridge_state_router, ast_channel_entered_bridge_type(), channel_enter_cb, NULL); ret |= stasis_message_router_add(bridge_state_router, ast_channel_left_bridge_type(), channel_leave_cb, NULL); ret |= ast_manager_register_xml_core("BridgeList", 0, manager_bridges_list); ret |= ast_manager_register_xml_core("BridgeInfo", 0, manager_bridge_info); ret |= ast_manager_register_xml_core("BridgeDestroy", 0, manager_bridge_destroy); ret |= ast_manager_register_xml_core("BridgeKick", 0, manager_bridge_kick); /* If somehow we failed to add any routes, just shut down the whole * thing and fail it. */ if (ret) { manager_bridging_cleanup(); return -1; } return 0; }
/*! Forward a channel's topics to an app */ static struct app_forwards *forwards_create_channel(struct stasis_app *app, struct ast_channel *chan) { struct app_forwards *forwards; if (!app) { return NULL; } forwards = forwards_create(app, chan ? ast_channel_uniqueid(chan) : CHANNEL_ALL); if (!forwards) { return NULL; } forwards->forward_type = FORWARD_CHANNEL; forwards->topic_forward = stasis_forward_all( chan ? ast_channel_topic(chan) : ast_channel_topic_all(), app->topic); if (!forwards->topic_forward) { ao2_ref(forwards, -1); return NULL; } return forwards; }
/*! Forward a bridge's topics to an app */ static struct app_forwards *forwards_create_bridge(struct stasis_app *app, struct ast_bridge *bridge) { struct app_forwards *forwards; if (!app) { return NULL; } forwards = forwards_create(app, bridge ? bridge->uniqueid : BRIDGE_ALL); if (!forwards) { return NULL; } forwards->forward_type = FORWARD_BRIDGE; forwards->topic_forward = stasis_forward_all(ast_bridge_topic(bridge), app->topic); if (!forwards->topic_forward && bridge) { forwards_unsubscribe(forwards); ao2_ref(forwards, -1); return NULL; } return forwards; }
struct stasis_cp_all *stasis_cp_all_create(const char *name, snapshot_get_id id_fn) { RAII_VAR(char *, cached_name, NULL, ast_free); RAII_VAR(struct stasis_cp_all *, all, NULL, ao2_cleanup); all = ao2_alloc(sizeof(*all), all_dtor); if (!all) { return NULL; } ast_asprintf(&cached_name, "%s-cached", name); if (!cached_name) { return NULL; } all->topic = stasis_topic_create(name); all->topic_cached = stasis_topic_create(cached_name); all->cache = stasis_cache_create(id_fn); all->forward_all_to_cached = stasis_forward_all(all->topic, all->topic_cached); if (!all->topic || !all->topic_cached || !all->cache || !all->forward_all_to_cached) { return NULL; } ao2_ref(all, +1); return all; }
struct stasis_cp_single *stasis_cp_single_create(struct stasis_cp_all *all, const char *name) { struct stasis_cp_single *one; one = stasis_cp_sink_create(all, name); if (!one) { return NULL; } one->forward_topic_to_all = stasis_forward_all(one->topic, all->topic); one->forward_cached_to_all = stasis_forward_all( stasis_caching_get_topic(one->topic_cached), all->topic_cached); if (!one->forward_topic_to_all || !one->forward_cached_to_all) { ao2_ref(one, -1); return NULL; } return one; }
int ast_channel_forward_endpoint(struct ast_channel *chan, struct ast_endpoint *endpoint) { ast_assert(chan != NULL); ast_assert(endpoint != NULL); chan->endpoint_forward = stasis_forward_all(ast_channel_topic(chan), ast_endpoint_topic(endpoint)); if (chan->endpoint_forward == NULL) { return -1; } return 0; }
int manager_mwi_init(void) { int ret = 0; struct stasis_topic *manager_topic; struct stasis_topic *mwi_topic; struct stasis_message_router *message_router; manager_topic = ast_manager_get_topic(); if (!manager_topic) { return -1; } message_router = ast_manager_get_message_router(); if (!message_router) { return -1; } mwi_topic = ast_mwi_topic_all(); if (!mwi_topic) { return -1; } topic_forwarder = stasis_forward_all(mwi_topic, manager_topic); if (!topic_forwarder) { return -1; } ast_register_cleanup(manager_mwi_shutdown); ret |= stasis_message_router_add(message_router, ast_mwi_state_type(), mwi_update_cb, NULL); ret |= stasis_message_router_add(message_router, ast_mwi_vm_app_type(), mwi_app_event_cb, NULL); /* If somehow we failed to add any routes, just shut down the whole * thing and fail it. */ if (ret) { manager_mwi_shutdown(); return -1; } return 0; }
void ast_ari_bridges_record(struct ast_variable *headers, struct ast_ari_bridges_record_args *args, struct ast_ari_response *response) { RAII_VAR(struct ast_bridge *, bridge, find_bridge(response, args->bridge_id), ao2_cleanup); RAII_VAR(struct ast_channel *, record_channel, NULL, ast_hangup); RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup); RAII_VAR(struct stasis_app_recording *, recording, NULL, ao2_cleanup); RAII_VAR(char *, recording_url, NULL, ast_free); RAII_VAR(struct ast_json *, json, NULL, ast_json_unref); RAII_VAR(struct stasis_app_recording_options *, options, NULL, ao2_cleanup); RAII_VAR(char *, uri_encoded_name, NULL, ast_free); RAII_VAR(struct stasis_forward *, channel_forward, NULL, stasis_forward_cancel); struct stasis_topic *channel_topic; struct stasis_topic *bridge_topic; size_t uri_name_maxlen; struct bridge_channel_control_thread_data *thread_data; pthread_t threadid; ast_assert(response != NULL); if (bridge == NULL) { return; } if (!(record_channel = prepare_bridge_media_channel("Recorder"))) { ast_ari_response_error( response, 500, "Internal Server Error", "Failed to create recording channel"); return; } bridge_topic = ast_bridge_topic(bridge); channel_topic = ast_channel_topic(record_channel); /* Forward messages from the recording channel topic to the bridge topic so that anything listening for * messages on the bridge topic will receive the recording start/stop messages. Other messages that would * go to this channel will be suppressed since the channel is marked as internal. */ if (!bridge_topic || !channel_topic || !(channel_forward = stasis_forward_all(channel_topic, bridge_topic))) { ast_ari_response_error( response, 500, "Internal Error", "Could not forward record channel stasis messages to bridge topic"); return; } if (ast_unreal_channel_push_to_bridge(record_channel, bridge, AST_BRIDGE_CHANNEL_FLAG_IMMOVABLE | AST_BRIDGE_CHANNEL_FLAG_LONELY)) { ast_ari_response_error( response, 500, "Internal Error", "Failed to put recording channel into the bridge"); return; } control = stasis_app_control_create(record_channel); if (control == NULL) { ast_ari_response_alloc_failed(response); return; } options = stasis_app_recording_options_create(args->name, args->format); if (options == NULL) { ast_ari_response_alloc_failed(response); return; } ast_string_field_build(options, target, "bridge:%s", args->bridge_id); options->max_silence_seconds = args->max_silence_seconds; options->max_duration_seconds = args->max_duration_seconds; options->terminate_on = stasis_app_recording_termination_parse(args->terminate_on); options->if_exists = stasis_app_recording_if_exists_parse(args->if_exists); options->beep = args->beep; if (options->terminate_on == STASIS_APP_RECORDING_TERMINATE_INVALID) { ast_ari_response_error( response, 400, "Bad Request", "terminateOn invalid"); return; } if (options->if_exists == AST_RECORD_IF_EXISTS_ERROR) { ast_ari_response_error( response, 400, "Bad Request", "ifExists invalid"); return; } if (!ast_get_format_for_file_ext(options->format)) { ast_ari_response_error( response, 422, "Unprocessable Entity", "specified format is unknown on this system"); return; } recording = stasis_app_control_record(control, options); if (recording == NULL) { switch(errno) { case EINVAL: /* While the arguments are invalid, we should have * caught them prior to calling record. */ ast_ari_response_error( response, 500, "Internal Server Error", "Error parsing request"); break; case EEXIST: ast_ari_response_error(response, 409, "Conflict", "Recording '%s' already exists and can not be overwritten", args->name); break; case ENOMEM: ast_ari_response_alloc_failed(response); break; case EPERM: ast_ari_response_error( response, 400, "Bad Request", "Recording name invalid"); break; default: ast_log(LOG_WARNING, "Unrecognized recording error: %s\n", strerror(errno)); ast_ari_response_error( response, 500, "Internal Server Error", "Internal Server Error"); break; } return; } uri_name_maxlen = strlen(args->name) * 3; uri_encoded_name = ast_malloc(uri_name_maxlen); if (!uri_encoded_name) { ast_ari_response_alloc_failed(response); return; } ast_uri_encode(args->name, uri_encoded_name, uri_name_maxlen, ast_uri_http); if (ast_asprintf(&recording_url, "/recordings/live/%s", uri_encoded_name) == -1) { recording_url = NULL; ast_ari_response_alloc_failed(response); return; } json = stasis_app_recording_to_json(recording); if (!json) { ast_ari_response_alloc_failed(response); return; } thread_data = ast_calloc(1, sizeof(*thread_data)); if (!thread_data) { ast_ari_response_alloc_failed(response); return; } thread_data->bridge_channel = record_channel; thread_data->control = control; thread_data->forward = channel_forward; if (ast_pthread_create_detached(&threadid, NULL, bridge_channel_control_thread, thread_data)) { ast_ari_response_alloc_failed(response); ast_free(thread_data); return; } /* These are owned by the other thread now, so we don't want RAII_VAR disposing of them. */ record_channel = NULL; control = NULL; channel_forward = NULL; ast_ari_response_created(response, recording_url, ast_json_ref(json)); }
static void ari_bridges_play_new(const char *args_media, const char *args_lang, int args_offset_ms, int args_skipms, const char *args_playback_id, struct ast_ari_response *response, struct ast_bridge *bridge) { RAII_VAR(struct ast_channel *, play_channel, NULL, ast_hangup); RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup); RAII_VAR(struct ast_json *, json, NULL, ast_json_unref); RAII_VAR(struct stasis_forward *, channel_forward, NULL, stasis_forward_cancel); RAII_VAR(char *, playback_url, NULL, ast_free); struct stasis_topic *channel_topic; struct stasis_topic *bridge_topic; struct bridge_channel_control_thread_data *thread_data; pthread_t threadid; if (!(play_channel = prepare_bridge_media_channel("Announcer"))) { ast_ari_response_error( response, 500, "Internal Error", "Could not create playback channel"); return; } ast_debug(1, "Created announcer channel '%s'\n", ast_channel_name(play_channel)); bridge_topic = ast_bridge_topic(bridge); channel_topic = ast_channel_topic(play_channel); /* Forward messages from the playback channel topic to the bridge topic so that anything listening for * messages on the bridge topic will receive the playback start/stop messages. Other messages that would * go to this channel will be suppressed since the channel is marked as internal. */ if (!bridge_topic || !channel_topic || !(channel_forward = stasis_forward_all(channel_topic, bridge_topic))) { ast_ari_response_error( response, 500, "Internal Error", "Could not forward playback channel stasis messages to bridge topic"); return; } if (ast_unreal_channel_push_to_bridge(play_channel, bridge, AST_BRIDGE_CHANNEL_FLAG_IMMOVABLE | AST_BRIDGE_CHANNEL_FLAG_LONELY)) { ast_ari_response_error( response, 500, "Internal Error", "Failed to put playback channel into the bridge"); return; } control = stasis_app_control_create(play_channel); if (control == NULL) { ast_ari_response_alloc_failed(response); return; } ao2_lock(control); if (ari_bridges_play_helper(args_media, args_lang, args_offset_ms, args_skipms, args_playback_id, response, bridge, control, &json, &playback_url)) { ao2_unlock(control); return; } ao2_unlock(control); if (stasis_app_bridge_playback_channel_add(bridge, play_channel, control)) { ast_ari_response_alloc_failed(response); return; } /* Give play_channel and control reference to the thread data */ thread_data = ast_calloc(1, sizeof(*thread_data)); if (!thread_data) { ast_ari_response_alloc_failed(response); return; } thread_data->bridge_channel = play_channel; thread_data->control = control; thread_data->forward = channel_forward; if (ast_pthread_create_detached(&threadid, NULL, bridge_channel_control_thread, thread_data)) { ast_ari_response_alloc_failed(response); ast_free(thread_data); return; } /* These are owned by the other thread now, so we don't want RAII_VAR disposing of them. */ play_channel = NULL; control = NULL; channel_forward = NULL; ast_ari_response_created(response, playback_url, ast_json_ref(json)); }