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; }
static int manager_bridge_info(struct mansession *s, const struct message *m) { const char *id = astman_get_header(m, "ActionID"); const char *bridge_uniqueid = astman_get_header(m, "BridgeUniqueid"); RAII_VAR(struct ast_str *, id_text, ast_str_create(128), ast_free); RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup); RAII_VAR(struct ast_str *, bridge_info, NULL, ast_free); struct ast_bridge_snapshot *snapshot; struct bridge_list_data list_data; if (!id_text) { astman_send_error(s, m, "Internal error"); return -1; } if (ast_strlen_zero(bridge_uniqueid)) { astman_send_error(s, m, "BridgeUniqueid must be provided"); return 0; } if (!ast_strlen_zero(id)) { ast_str_set(&id_text, 0, "ActionID: %s\r\n", id); } msg = stasis_cache_get(ast_bridge_cache(), ast_bridge_snapshot_type(), bridge_uniqueid); if (!msg) { astman_send_error(s, m, "Specified BridgeUniqueid not found"); return 0; } snapshot = stasis_message_data(msg); bridge_info = ast_manager_build_bridge_state_string(snapshot); if (!bridge_info) { astman_send_error(s, m, "Internal error"); return -1; } astman_send_listack(s, m, "Bridge channel listing will follow", "start"); list_data.id_text = ast_str_buffer(id_text); list_data.count = 0; ao2_callback_data(snapshot->channels, OBJ_NODATA, send_bridge_info_item_cb, s, &list_data); astman_send_list_complete_start(s, m, "BridgeInfoComplete", list_data.count); if (!ast_strlen_zero(ast_str_buffer(bridge_info))) { astman_append(s, "%s", ast_str_buffer(bridge_info)); } astman_send_list_complete_end(s); return 0; }
void ast_ari_bridges_list(struct ast_variable *headers, struct ast_ari_bridges_list_args *args, struct ast_ari_response *response) { RAII_VAR(struct stasis_cache *, cache, NULL, ao2_cleanup); RAII_VAR(struct ao2_container *, snapshots, NULL, ao2_cleanup); RAII_VAR(struct ast_json *, json, NULL, ast_json_unref); struct ao2_iterator i; void *obj; cache = ast_bridge_cache(); if (!cache) { ast_ari_response_error( response, 500, "Internal Server Error", "Message bus not initialized"); return; } ao2_ref(cache, +1); snapshots = stasis_cache_dump(cache, ast_bridge_snapshot_type()); if (!snapshots) { ast_ari_response_alloc_failed(response); return; } json = ast_json_array_create(); if (!json) { ast_ari_response_alloc_failed(response); return; } i = ao2_iterator_init(snapshots, 0); while ((obj = ao2_iterator_next(&i))) { RAII_VAR(struct stasis_message *, msg, obj, ao2_cleanup); struct ast_bridge_snapshot *snapshot = stasis_message_data(msg); struct ast_json *json_bridge = ast_bridge_snapshot_to_json(snapshot, stasis_app_get_sanitizer()); if (!json_bridge || ast_json_array_append(json, json_bridge)) { ao2_iterator_destroy(&i); ast_ari_response_alloc_failed(response); return; } } ao2_iterator_destroy(&i); ast_ari_response_ok(response, ast_json_ref(json)); }
static void sub_bridge_update_handler(void *data, struct stasis_subscription *sub, struct stasis_message *message) { RAII_VAR(struct ast_json *, json, NULL, ast_json_unref); struct stasis_app *app = data; struct stasis_cache_update *update; struct ast_bridge_snapshot *new_snapshot; struct ast_bridge_snapshot *old_snapshot; const struct timeval *tv; ast_assert(stasis_message_type(message) == stasis_cache_update_type()); update = stasis_message_data(message); ast_assert(update->type == ast_bridge_snapshot_type()); new_snapshot = stasis_message_data(update->new_snapshot); old_snapshot = stasis_message_data(update->old_snapshot); tv = update->new_snapshot ? stasis_message_timestamp(update->new_snapshot) : stasis_message_timestamp(message); if (!new_snapshot) { json = simple_bridge_event("BridgeDestroyed", old_snapshot, tv); } else if (!old_snapshot) { json = simple_bridge_event("BridgeCreated", new_snapshot, tv); } else if (new_snapshot && old_snapshot && strcmp(new_snapshot->video_source_id, old_snapshot->video_source_id)) { json = simple_bridge_event("BridgeVideoSourceChanged", new_snapshot, tv); if (json && !ast_strlen_zero(old_snapshot->video_source_id)) { ast_json_object_set(json, "old_video_source_id", ast_json_string_create(old_snapshot->video_source_id)); } } if (json) { app_send(app, json); } if (!new_snapshot && old_snapshot) { unsubscribe(app, "bridge", old_snapshot->uniqueid, 1); } }
static int manager_bridges_list(struct mansession *s, const struct message *m) { const char *id = astman_get_header(m, "ActionID"); const char *type_filter = astman_get_header(m, "BridgeType"); RAII_VAR(struct ast_str *, id_text, ast_str_create(128), ast_free); RAII_VAR(struct ao2_container *, bridges, NULL, ao2_cleanup); struct bridge_list_data list_data; if (!id_text) { astman_send_error(s, m, "Internal error"); return -1; } if (!ast_strlen_zero(id)) { ast_str_set(&id_text, 0, "ActionID: %s\r\n", id); } bridges = stasis_cache_dump(ast_bridge_cache(), ast_bridge_snapshot_type()); if (!bridges) { astman_send_error(s, m, "Internal error"); return -1; } astman_send_listack(s, m, "Bridge listing will follow", "start"); if (!ast_strlen_zero(type_filter)) { char *type_filter_dup = ast_strdupa(type_filter); ao2_callback(bridges, OBJ_MULTIPLE | OBJ_NODATA | OBJ_UNLINK, filter_bridge_type_cb, type_filter_dup); } list_data.id_text = ast_str_buffer(id_text); list_data.count = 0; ao2_callback_data(bridges, OBJ_NODATA, send_bridge_list_item_cb, s, &list_data); astman_send_list_complete_start(s, m, "BridgeListComplete", list_data.count); astman_send_list_complete_end(s); return 0; }
static void bridge_snapshot_update(void *data, struct stasis_subscription *sub, struct stasis_message *message) { RAII_VAR(struct ast_str *, bridge_event_string, NULL, ast_free); struct stasis_cache_update *update; struct ast_bridge_snapshot *old_snapshot; struct ast_bridge_snapshot *new_snapshot; size_t i; update = stasis_message_data(message); ast_assert(ast_bridge_snapshot_type() == update->type); old_snapshot = stasis_message_data(update->old_snapshot); new_snapshot = stasis_message_data(update->new_snapshot); for (i = 0; i < ARRAY_LEN(bridge_monitors); ++i) { RAII_VAR(struct ast_manager_event_blob *, event, NULL, ao2_cleanup); event = bridge_monitors[i](old_snapshot, new_snapshot); if (!event) { continue; } /* If we haven't already, build the channel event string */ if (!bridge_event_string) { bridge_event_string = ast_manager_build_bridge_state_string( new_snapshot ? new_snapshot : old_snapshot); if (!bridge_event_string) { return; } } manager_event(event->event_flags, event->manager_event, "%s%s", ast_str_buffer(bridge_event_string), event->extra_fields); } }
struct stasis_app *app_create(const char *name, stasis_app_cb handler, void *data, enum stasis_app_subscription_model subscription_model) { RAII_VAR(struct stasis_app *, app, NULL, ao2_cleanup); size_t size; int res = 0; size_t context_size = strlen("stasis-") + strlen(name) + 1; char context_name[context_size]; ast_assert(name != NULL); ast_assert(handler != NULL); ast_verb(1, "Creating Stasis app '%s'\n", name); size = sizeof(*app) + strlen(name) + 1; app = ao2_alloc_options(size, app_dtor, AO2_ALLOC_OPT_LOCK_MUTEX); if (!app) { return NULL; } app->subscription_model = subscription_model; app->forwards = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_MUTEX, AO2_CONTAINER_ALLOC_OPT_DUPS_OBJ_REJECT, forwards_sort, NULL); if (!app->forwards) { return NULL; } app->topic = stasis_topic_create(name); if (!app->topic) { return NULL; } app->bridge_router = stasis_message_router_create(ast_bridge_topic_all()); if (!app->bridge_router) { return NULL; } res |= stasis_message_router_add(app->bridge_router, ast_bridge_merge_message_type(), bridge_merge_handler, app); res |= stasis_message_router_add(app->bridge_router, ast_blind_transfer_type(), bridge_blind_transfer_handler, app); res |= stasis_message_router_add(app->bridge_router, ast_attended_transfer_type(), bridge_attended_transfer_handler, app); res |= stasis_message_router_add(app->bridge_router, stasis_subscription_change_type(), bridge_subscription_change_handler, app); if (res != 0) { return NULL; } /* Bridge router holds a reference */ ao2_ref(app, +1); app->router = stasis_message_router_create(app->topic); if (!app->router) { return NULL; } res |= stasis_message_router_add(app->router, ast_bridge_snapshot_type(), sub_bridge_update_handler, app); res |= stasis_message_router_add(app->router, ast_channel_snapshot_type(), sub_channel_update_handler, app); res |= stasis_message_router_add_cache_update(app->router, ast_endpoint_snapshot_type(), sub_endpoint_update_handler, app); res |= stasis_message_router_add(app->router, stasis_subscription_change_type(), sub_subscription_change_handler, app); stasis_message_router_set_formatters_default(app->router, sub_default_handler, app, STASIS_SUBSCRIPTION_FORMATTER_JSON); if (res != 0) { return NULL; } /* Router holds a reference */ ao2_ref(app, +1); strncpy(app->name, name, size - sizeof(*app)); app->handler = handler; app->data = ao2_bump(data); /* Create a context, a match-all extension, and a 'h' extension for this application. Note that * this should only be done if a context does not already exist. */ strcpy(context_name, "stasis-"); strcat(context_name, name); if (!ast_context_find(context_name)) { if (!ast_context_find_or_create(NULL, NULL, context_name, "res_stasis")) { ast_log(LOG_WARNING, "Could not create context '%s' for Stasis application '%s'\n", context_name, name); } else { ast_add_extension(context_name, 0, "_.", 1, NULL, NULL, "Stasis", ast_strdup(name), ast_free_ptr, "res_stasis"); ast_add_extension(context_name, 0, "h", 1, NULL, NULL, "NoOp", NULL, NULL, "res_stasis"); } } else { ast_log(LOG_WARNING, "Not creating context '%s' for Stasis application '%s' because it already exists\n", context_name, name); } ao2_ref(app, +1); return app; }
struct stasis_app *app_create(const char *name, stasis_app_cb handler, void *data) { RAII_VAR(struct stasis_app *, app, NULL, ao2_cleanup); size_t size; int res = 0; ast_assert(name != NULL); ast_assert(handler != NULL); ast_verb(1, "Creating Stasis app '%s'\n", name); size = sizeof(*app) + strlen(name) + 1; app = ao2_alloc_options(size, app_dtor, AO2_ALLOC_OPT_LOCK_MUTEX); if (!app) { return NULL; } app->forwards = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_MUTEX, AO2_CONTAINER_ALLOC_OPT_DUPS_OBJ_REJECT, forwards_sort, NULL); if (!app->forwards) { return NULL; } app->topic = stasis_topic_create(name); if (!app->topic) { return NULL; } app->bridge_router = stasis_message_router_create(ast_bridge_topic_all()); if (!app->bridge_router) { return NULL; } res |= stasis_message_router_add(app->bridge_router, ast_bridge_merge_message_type(), bridge_merge_handler, app); res |= stasis_message_router_add(app->bridge_router, ast_blind_transfer_type(), bridge_blind_transfer_handler, app); res |= stasis_message_router_add(app->bridge_router, ast_attended_transfer_type(), bridge_attended_transfer_handler, app); res |= stasis_message_router_set_default(app->bridge_router, bridge_default_handler, app); if (res != 0) { return NULL; } /* Bridge router holds a reference */ ao2_ref(app, +1); app->router = stasis_message_router_create(app->topic); if (!app->router) { return NULL; } res |= stasis_message_router_add_cache_update(app->router, ast_bridge_snapshot_type(), sub_bridge_update_handler, app); res |= stasis_message_router_add_cache_update(app->router, ast_channel_snapshot_type(), sub_channel_update_handler, app); res |= stasis_message_router_add_cache_update(app->router, ast_endpoint_snapshot_type(), sub_endpoint_update_handler, app); res |= stasis_message_router_set_default(app->router, sub_default_handler, app); if (res != 0) { return NULL; } /* Router holds a reference */ ao2_ref(app, +1); strncpy(app->name, name, size - sizeof(*app)); app->handler = handler; ao2_ref(data, +1); app->data = data; ao2_ref(app, +1); return app; }