void ast_ari_channels_get(struct ast_variable *headers,
	struct ast_ari_channels_get_args *args,
	struct ast_ari_response *response)
{
	RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
	struct stasis_cache *cache;
	struct ast_channel_snapshot *snapshot;

	cache = ast_channel_cache();
	if (!cache) {
		ast_ari_response_error(
			response, 500, "Internal Server Error",
			"Message bus not initialized");
		return;
	}

	msg = stasis_cache_get(cache, ast_channel_snapshot_type(),
				   args->channel_id);
	if (!msg) {
		ast_ari_response_error(
			response, 404, "Not Found",
			"Channel not found");
		return;
	}

	snapshot = stasis_message_data(msg);
	ast_assert(snapshot != NULL);

	ast_ari_response_ok(response,
				ast_channel_snapshot_to_json(snapshot, NULL));
}
static int send_bridge_info_item_cb(void *obj, void *arg, void *data, int flags)
{
	char *uniqueid = obj;
	struct mansession *s = arg;
	char *id_text = data;
	RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
	struct ast_channel_snapshot *snapshot;
	RAII_VAR(struct ast_str *, channel_text, NULL, ast_free);
	msg = stasis_cache_get(ast_channel_cache(),
		ast_channel_snapshot_type(), uniqueid);

	if (!msg) {
		return 0;
	}

	snapshot = stasis_message_data(msg);
	if (snapshot->tech_properties & AST_CHAN_TP_INTERNAL) {
		return 0;
	}

	channel_text = ast_manager_build_channel_state_string(snapshot);
	if (!channel_text) {
		return 0;
	}

	astman_append(s,
		"Event: BridgeInfoChannel\r\n"
		"%s"
		"%s"
		"\r\n",
		ast_str_buffer(channel_text),
		id_text);
	return 0;
}
Beispiel #3
0
static const char *channel_snapshot_get_name(struct stasis_message *message)
{
	struct ast_channel_snapshot *snapshot;
	if (ast_channel_snapshot_type() != stasis_message_type(message)) {
		return NULL;
	}
	snapshot = stasis_message_data(message);
	return snapshot->name;
}
Beispiel #4
0
void ast_ari_channels_list(struct ast_variable *headers,
                           struct ast_ari_channels_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;
    struct stasis_message_sanitizer *sanitize = stasis_app_get_sanitizer();

    cache = ast_channel_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_channel_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;
    }

    for (i = ao2_iterator_init(snapshots, 0);
            (obj = ao2_iterator_next(&i)); ao2_cleanup(obj)) {
        RAII_VAR(struct stasis_message *, msg, obj, ao2_cleanup);
        struct ast_channel_snapshot *snapshot = stasis_message_data(msg);
        int r;

        if (sanitize && sanitize->channel_snapshot
                && sanitize->channel_snapshot(snapshot)) {
            continue;
        }

        r = ast_json_array_append(
                json, ast_channel_snapshot_to_json(snapshot, NULL));
        if (r != 0) {
            ast_ari_response_alloc_failed(response);
            ao2_cleanup(obj);
            ao2_iterator_destroy(&i);
            return;
        }
    }
    ao2_iterator_destroy(&i);

    ast_ari_response_ok(response, ast_json_ref(json));
}
Beispiel #5
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;
}
Beispiel #6
0
struct ast_channel_snapshot *ast_channel_snapshot_get_latest_by_name(const char *name)
{
	RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
	struct ast_channel_snapshot *snapshot;

	ast_assert(!ast_strlen_zero(name));

	message = stasis_cache_get(ast_channel_cache_by_name(),
			ast_channel_snapshot_type(),
			name);
	if (!message) {
		return NULL;
	}

	snapshot = stasis_message_data(message);
	if (!snapshot) {
		return NULL;
	}
	ao2_ref(snapshot, +1);
	return snapshot;
}
Beispiel #7
0
/*!
 * \brief Router callback for \ref stasis_cache_update messages.
 * \param data Data pointer given when added to router.
 * \param sub This subscription.
 * \param topic The topic the message was posted to. This is not necessarily the
 *              topic you subscribed to, since messages may be forwarded between
 *              topics.
 * \param message The message itself.
 */
static void updates(void *data, struct stasis_subscription *sub,
	struct stasis_message *message)
{
	/* Since this came from a message router, we know the type of the
	 * message. We can cast the data without checking its type.
	 */
	struct stasis_cache_update *update = stasis_message_data(message);

	/* We're only interested in channel snapshots, so check the type
	 * of the underlying message.
	 */
	if (ast_channel_snapshot_type() != update->type) {
		return;
	}

	/* There are three types of cache updates.
	 * !old && new -> Initial cache entry
	 * old && new -> Updated cache entry
	 * old && !new -> Cache entry removed.
	 */

	if (!update->old_snapshot && update->new_snapshot) {
		/* Initial cache entry; count a channel creation */
		ast_statsd_log("channels.count", AST_STATSD_COUNTER, 1);
	} else if (update->old_snapshot && !update->new_snapshot) {
		/* Cache entry removed. Compute the age of the channel and post
		 * that, as well as decrementing the channel count.
		 */
		struct ast_channel_snapshot *last;
		int64_t age;

		last = stasis_message_data(update->old_snapshot);
		age = ast_tvdiff_ms(*stasis_message_timestamp(message),
			last->creationtime);
		ast_statsd_log("channels.calltime", AST_STATSD_TIMER, age);

		/* And decrement the channel count */
		ast_statsd_log("channels.count", AST_STATSD_COUNTER, -1);
	}
}
Beispiel #8
0
static void sub_channel_update_handler(void *data,
	struct stasis_subscription *sub,
	struct stasis_message *message)
{
	struct stasis_app *app = data;
	struct stasis_cache_update *update;
	struct ast_channel_snapshot *new_snapshot;
	struct ast_channel_snapshot *old_snapshot;
	const struct timeval *tv;
	int i;

	ast_assert(stasis_message_type(message) == stasis_cache_update_type());

	update = stasis_message_data(message);

	ast_assert(update->type == ast_channel_snapshot_type());

	new_snapshot = stasis_message_data(update->new_snapshot);
	old_snapshot = stasis_message_data(update->old_snapshot);

	/* Pull timestamp from the new snapshot, or from the update message
	 * when there isn't one. */
	tv = update->new_snapshot ?
		stasis_message_timestamp(update->new_snapshot) :
		stasis_message_timestamp(message);

	for (i = 0; i < ARRAY_LEN(channel_monitors); ++i) {
		RAII_VAR(struct ast_json *, msg, NULL, ast_json_unref);

		msg = channel_monitors[i](old_snapshot, new_snapshot, tv);
		if (msg) {
			app_send(app, msg);
		}
	}

	if (!new_snapshot && old_snapshot) {
		unsubscribe(app, "channel", old_snapshot->uniqueid, 1);
	}
}
Beispiel #9
0
void ast_channel_publish_snapshot(struct ast_channel *chan)
{
	RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
	RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);

	if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_SNAPSHOT_STAGE)) {
		return;
	}

	snapshot = ast_channel_snapshot_create(chan);
	if (!snapshot) {
		return;
	}

	message = stasis_message_create(ast_channel_snapshot_type(), snapshot);
	if (!message) {
		return;
	}

	ast_assert(ast_channel_topic(chan) != NULL);
	stasis_publish(ast_channel_topic(chan), message);
}
Beispiel #10
0
void ast_publish_channel_state(struct ast_channel *chan)
{
	RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
	RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);

	ast_assert(chan != NULL);
	if (!chan) {
		return;
	}

	snapshot = ast_channel_snapshot_create(chan);
	if (!snapshot) {
		return;
	}

	message = stasis_message_create(ast_channel_snapshot_type(), snapshot);
	if (!message) {
		return;
	}

	ast_assert(ast_channel_topic(chan) != NULL);
	stasis_publish(ast_channel_topic(chan), message);
}
Beispiel #11
0
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;
}
Beispiel #12
0
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;
}