/*! 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; }
/*! 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; }
static int unsubscribe(struct stasis_app *app, const char *kind, const char *id, int terminate) { RAII_VAR(struct app_forwards *, forwards, NULL, ao2_cleanup); SCOPED_AO2LOCK(lock, app->forwards); forwards = ao2_find(app->forwards, id, OBJ_SEARCH_KEY | OBJ_NOLOCK); if (!forwards) { ast_debug(3, "App '%s' not subscribed to %s '%s'\n", app->name, kind, id); return -1; } forwards->interested--; ast_debug(3, "%s '%s': is %d interested in %s\n", kind, id, forwards->interested, app->name); if (forwards->interested == 0 || terminate) { /* No one is interested any more; unsubscribe */ ast_debug(3, "%s '%s' unsubscribed from %s\n", kind, id, app->name); forwards_unsubscribe(forwards); ao2_find(app->forwards, forwards, OBJ_POINTER | OBJ_NOLOCK | OBJ_UNLINK | OBJ_NODATA); if (!strcmp(kind, "endpoint")) { messaging_app_unsubscribe_endpoint(app->name, id); } } return 0; }
/*! 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) { 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; }
static int unsubscribe(struct stasis_app *app, const char *kind, const char *id, int terminate) { struct app_forwards *forwards; if (!id) { if (!strcmp(kind, "bridge")) { id = BRIDGE_ALL; } else if (!strcmp(kind, "channel")) { id = CHANNEL_ALL; } else if (!strcmp(kind, "endpoint")) { id = ENDPOINT_ALL; } else { ast_log(LOG_WARNING, "Unknown subscription kind '%s'\n", kind); return -1; } } ao2_lock(app->forwards); forwards = ao2_find(app->forwards, id, OBJ_SEARCH_KEY | OBJ_NOLOCK); if (!forwards) { ao2_unlock(app->forwards); ast_debug(3, "App '%s' not subscribed to %s '%s'\n", app->name, kind, id); return -1; } forwards->interested--; ast_debug(3, "%s '%s': is %d interested in %s\n", kind, id, forwards->interested, app->name); if (forwards->interested == 0 || terminate) { /* No one is interested any more; unsubscribe */ ast_debug(3, "%s '%s' unsubscribed from %s\n", kind, id, app->name); forwards_unsubscribe(forwards); ao2_find(app->forwards, forwards, OBJ_POINTER | OBJ_NOLOCK | OBJ_UNLINK | OBJ_NODATA); if (!strcmp(kind, "endpoint")) { messaging_app_unsubscribe_endpoint(app->name, id); } } ao2_unlock(app->forwards); ao2_ref(forwards, -1); return 0; }