struct ast_endpoint_snapshot *ast_endpoint_latest_snapshot(const char *tech, const char *name) { RAII_VAR(char *, id, NULL, ast_free); RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup); struct ast_endpoint_snapshot *snapshot; if (ast_strlen_zero(name)) { ast_asprintf(&id, "%s", tech); } else { ast_asprintf(&id, "%s/%s", tech, name); } if (!id) { return NULL; } ast_tech_to_upper(id); msg = stasis_cache_get(ast_endpoint_cache(), ast_endpoint_snapshot_type(), id); if (!msg) { return NULL; } snapshot = stasis_message_data(msg); ast_assert(snapshot != NULL); ao2_ref(snapshot, +1); return snapshot; }
static void sub_endpoint_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_endpoint_snapshot *new_snapshot; struct ast_endpoint_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_endpoint_snapshot_type()); new_snapshot = stasis_message_data(update->new_snapshot); old_snapshot = stasis_message_data(update->old_snapshot); if (new_snapshot) { tv = stasis_message_timestamp(update->new_snapshot); json = simple_endpoint_event("EndpointStateChange", new_snapshot, tv); if (!json) { return; } app_send(app, json); } if (!new_snapshot && old_snapshot) { unsubscribe(app, "endpoint", old_snapshot->id, 1); } }
/*! * \brief Callback extract a unique identity from a snapshot message. * * This identity is unique to the underlying object of the snapshot, such as the * UniqueId field of a channel. * * \param message Message to extract id from. * \return String representing the snapshot's id. * \return \c NULL if the message_type of the message isn't a handled snapshot. * \since 12 */ static const char *endpoint_snapshot_get_id(struct stasis_message *message) { struct ast_endpoint_snapshot *snapshot; if (ast_endpoint_snapshot_type() != stasis_message_type(message)) { return NULL; } snapshot = stasis_message_data(message); return snapshot->id; }
void ast_ari_endpoints_list(struct ast_variable *headers, struct ast_ari_endpoints_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_endpoint_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_endpoint_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_endpoint_snapshot *snapshot = stasis_message_data(msg); struct ast_json *json_endpoint = ast_endpoint_snapshot_to_json(snapshot, stasis_app_get_sanitizer()); int r; if (!json_endpoint) { return; } r = ast_json_array_append( json, json_endpoint); if (r != 0) { ast_ari_response_alloc_failed(response); return; } } ao2_iterator_destroy(&i); ast_ari_response_ok(response, ast_json_ref(json)); }
static int unload_module(void) { struct ao2_container *endpoints; endpoints = stasis_cache_dump(ast_endpoint_cache(), ast_endpoint_snapshot_type()); if (endpoints) { ao2_callback(endpoints, OBJ_MULTIPLE | OBJ_NODATA | OBJ_NOLOCK, dump_cache_unload, NULL); ao2_ref(endpoints, -1); } stasis_message_router_unsubscribe_and_join(router); router = NULL; return 0; }
static void cache_update_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message) { struct stasis_cache_update *update = stasis_message_data(message); struct ast_endpoint_snapshot *old_snapshot; struct ast_endpoint_snapshot *new_snapshot; if (ast_endpoint_snapshot_type() != update->type) { return; } old_snapshot = stasis_message_data(update->old_snapshot); new_snapshot = stasis_message_data(update->new_snapshot); handle_endpoint_update(old_snapshot, new_snapshot); }
static int load_module(void) { struct ao2_container *endpoints; router = stasis_message_router_create(ast_endpoint_topic_all_cached()); if (!router) { return AST_MODULE_LOAD_FAILURE; } stasis_message_router_add(router, stasis_cache_update_type(), cache_update_cb, NULL); endpoints = stasis_cache_dump(ast_endpoint_cache(), ast_endpoint_snapshot_type()); if (endpoints) { ao2_callback(endpoints, OBJ_MULTIPLE | OBJ_NODATA | OBJ_NOLOCK, dump_cache_load, NULL); ao2_ref(endpoints, -1); } return AST_MODULE_LOAD_SUCCESS; }
/*! \brief Message matcher looking for cache update messages */ static int cache_update(struct stasis_message *msg, const void *data) { struct stasis_cache_update *update; struct ast_endpoint_snapshot *snapshot; const char *name = data; if (stasis_cache_update_type() != stasis_message_type(msg)) { return 0; } update = stasis_message_data(msg); if (ast_endpoint_snapshot_type() != update->type) { return 0; } snapshot = stasis_message_data(update->old_snapshot); if (!snapshot) { snapshot = stasis_message_data(update->new_snapshot); } return 0 == strcmp(name, snapshot->resource); }
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; }