static int find_route(
	struct stasis_message_router *router,
	struct stasis_message *message,
	struct stasis_message_route *route_out)
{
	struct stasis_message_route *route = NULL;
	struct stasis_message_type *type = stasis_message_type(message);
	SCOPED_AO2LOCK(lock, router);

	ast_assert(route_out != NULL);

	if (type == stasis_cache_update_type()) {
		/* Find a cache route */
		struct stasis_cache_update *update =
			stasis_message_data(message);
		route = route_table_find(&router->cache_routes, update->type);
	}

	if (route == NULL) {
		/* Find a regular route */
		route = route_table_find(&router->routes, type);
	}

	if (route == NULL && router->default_route.callback) {
		/* Maybe the default route, then? */
		route = &router->default_route;
	}

	if (!route) {
		return -1;
	}

	*route_out = *route;
	return 0;
}
Example #2
0
/*!
 * \brief Explicitly shutdown a session.
 *
 * \details An explicit shutdown is necessary, since the \ref stasis_app has a reference
 *          to this session. We also need to be sure to null out the \c ws_session field,
 *          since the websocket is about to go away.
 *
 * \internal
 *
 * \param session  Event session object (\ref event_session).
 */
static void event_session_shutdown(struct event_session *session)
{
	struct ao2_iterator i;
	char *app;
	int j;
	SCOPED_AO2LOCK(lock, session);

	/* Clean up the websocket_apps container */
	if (session->websocket_apps) {
		i = ao2_iterator_init(session->websocket_apps, 0);
		while ((app = ao2_iterator_next(&i))) {
			stasis_app_unregister(app);
			ao2_cleanup(app);
		}
		ao2_iterator_destroy(&i);
		ao2_cleanup(session->websocket_apps);
		session->websocket_apps = NULL;
	}

	/* Clean up the message_queue container */
	for (j = 0; j < AST_VECTOR_SIZE(&session->message_queue); j++) {
		struct ast_json *msg = AST_VECTOR_GET(&session->message_queue, j);
		ast_json_unref(msg);
	}
	AST_VECTOR_FREE(&session->message_queue);

	/* Remove the handle to the underlying websocket session */
	session->ws_session = NULL;
}
Example #3
0
int app_subscribe_channel(struct stasis_app *app, struct ast_channel *chan)
{
	int res;

	if (!app || !chan) {
		return -1;
	} else {
		RAII_VAR(struct app_forwards *, forwards, NULL, ao2_cleanup);
		SCOPED_AO2LOCK(lock, app->forwards);

		forwards = ao2_find(app->forwards, ast_channel_uniqueid(chan),
			OBJ_SEARCH_KEY | OBJ_NOLOCK);
		if (!forwards) {
			/* Forwards not found, create one */
			forwards = forwards_create_channel(app, chan);
			if (!forwards) {
				return -1;
			}

			res = ao2_link_flags(app->forwards, forwards,
				OBJ_NOLOCK);
			if (!res) {
				return -1;
			}
		}

		++forwards->interested;
		ast_debug(3, "Channel '%s' is %d interested in %s\n", ast_channel_uniqueid(chan), forwards->interested, app->name);
		return 0;
	}
}
Example #4
0
static void app_control_register_rule(
    const struct stasis_app_control *control,
    struct app_control_rules *list, struct stasis_app_control_rule *obj)
{
    SCOPED_AO2LOCK(lock, control->command_queue);
    AST_LIST_INSERT_TAIL(list, obj, next);
}
Example #5
0
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;
}
Example #6
0
int app_subscribe_endpoint(struct stasis_app *app, struct ast_endpoint *endpoint)
{
	if (!app || !endpoint) {
		return -1;
	} else {
		RAII_VAR(struct app_forwards *, forwards, NULL, ao2_cleanup);
		SCOPED_AO2LOCK(lock, app->forwards);

		forwards = ao2_find(app->forwards, ast_endpoint_get_id(endpoint),
			OBJ_SEARCH_KEY | OBJ_NOLOCK);

		if (!forwards) {
			/* Forwards not found, create one */
			forwards = forwards_create_endpoint(app, endpoint);
			if (!forwards) {
				return -1;
			}
			ao2_link_flags(app->forwards, forwards, OBJ_NOLOCK);

			/* Subscribe for messages */
			messaging_app_subscribe_endpoint(app->name, endpoint, &message_received_handler, app);
		}

		++forwards->interested;
		ast_debug(3, "Endpoint '%s' is %d interested in %s\n", ast_endpoint_get_id(endpoint), forwards->interested, app->name);
		return 0;
	}
}
Example #7
0
int app_subscribe_bridge(struct stasis_app *app, struct ast_bridge *bridge)
{
	if (!app || !bridge) {
		return -1;
	} else {
		RAII_VAR(struct app_forwards *, forwards, NULL, ao2_cleanup);
		SCOPED_AO2LOCK(lock, app->forwards);

		forwards = ao2_find(app->forwards, bridge->uniqueid,
			OBJ_SEARCH_KEY | OBJ_NOLOCK);

		if (!forwards) {
			/* Forwards not found, create one */
			forwards = forwards_create_bridge(app, bridge);
			if (!forwards) {
				return -1;
			}
			ao2_link_flags(app->forwards, forwards, OBJ_NOLOCK);
		}

		++forwards->interested;
		ast_debug(3, "Bridge '%s' is %d interested in %s\n", bridge->uniqueid, forwards->interested, app->name);
		return 0;
	}
}
Example #8
0
void app_update(struct stasis_app *app, stasis_app_cb handler, void *data)
{
	SCOPED_AO2LOCK(lock, app);

	if (app->handler) {
		RAII_VAR(struct ast_json *, msg, NULL, ast_json_unref);

		ast_verb(1, "Replacing Stasis app '%s'\n", app->name);

		msg = ast_json_pack("{s: s, s: s}",
			"type", "ApplicationReplaced",
			"application", app->name);
		if (msg) {
			app_send(app, msg);
		}
	} else {
		ast_verb(1, "Activating Stasis app '%s'\n", app->name);
	}

	app->handler = handler;
	ao2_cleanup(app->data);
	if (data) {
		ao2_ref(data, +1);
	}
	app->data = data;
}
Example #9
0
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;
}
Example #10
0
static struct wait_bridge_wrapper *get_wait_bridge_wrapper(const char *bridge_name)
{
    struct wait_bridge_wrapper * wrapper;
    struct ast_bridge *bridge = NULL;

    SCOPED_AO2LOCK(lock, wait_bridge_wrappers);

    if ((wrapper = wait_bridge_wrapper_find_by_name(bridge_name))) {
        return wrapper;
    }

    /*
     * Holding bridges can allow local channel move/swap
     * optimization to the bridge.  However, we cannot allow it for
     * this holding bridge because the call will lose the channel
     * roles and dialplan location as a result.
     */
    bridge = ast_bridge_base_new(AST_BRIDGE_CAPABILITY_HOLDING,
                                 AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM
                                 | AST_BRIDGE_FLAG_SWAP_INHIBIT_TO | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM
                                 | AST_BRIDGE_FLAG_TRANSFER_PROHIBITED, APP_NAME, bridge_name, NULL);

    if (!bridge) {
        return NULL;
    }

    /* The bridge reference is unconditionally passed. */
    return wait_bridge_wrapper_alloc(bridge_name, bridge);
}
Example #11
0
/*!
 * \brief Send a message to the given application.
 * \param app App to send the message to.
 * \param message Message to send.
 */
void app_send(struct stasis_app *app, struct ast_json *message)
{
	stasis_app_cb handler;
	char eid[20];
	RAII_VAR(void *, data, NULL, ao2_cleanup);

	if (ast_json_object_set(message, "asterisk_id", ast_json_string_create(
			ast_eid_to_str(eid, sizeof(eid), &ast_eid_default)))) {
		ast_log(AST_LOG_WARNING, "Failed to append EID to outgoing event %s\n",
			ast_json_string_get(ast_json_object_get(message, "type")));
	}

	/* Copy off mutable state with lock held */
	{
		SCOPED_AO2LOCK(lock, app);
		handler = app->handler;
		if (app->data) {
			ao2_ref(app->data, +1);
			data = app->data;
		}
		/* Name is immutable; no need to copy */
	}

	if (!handler) {
		ast_verb(3,
			"Inactive Stasis app '%s' missed message\n", app->name);
		return;
	}

	handler(data, app->name, message);
}
static void consumer_exec(void *data, struct stasis_subscription *sub, struct stasis_message *message)
{
    struct consumer *consumer = data;
    struct stasis_cache_update *cache_update = stasis_message_data(message);
    struct ast_device_state_message *device_state;

    if (!cache_update->new_snapshot) {
        return;
    }

    device_state = stasis_message_data(cache_update->new_snapshot);

    if (strcmp(device_state->device, UNIT_TEST_DEVICE_IDENTIFIER)) {
        /* not a device state we're interested in */
        return;
    }

    {
        SCOPED_AO2LOCK(lock, consumer);

        ++consumer->event_count;
        if (device_state->eid) {
            consumer->state = device_state->state;
            if (consumer->sig_on_non_aggregate_state) {
                consumer->sig_on_non_aggregate_state = 0;
                consumer->already_out = 1;
                ast_cond_signal(&consumer->out);
            }
        } else {
            consumer->aggregate_state = device_state->state;
            consumer->already_out = 1;
            ast_cond_signal(&consumer->out);
        }
    }
}
Example #13
0
static void bridge_after_cb(struct ast_channel *chan, void *data)
{
    struct stasis_app_control *control = data;
    SCOPED_AO2LOCK(lock, control);
    struct ast_bridge_channel *bridge_channel;

    ast_debug(3, "%s, %s: Channel leaving bridge\n",
              ast_channel_uniqueid(chan), control->bridge->uniqueid);

    ast_assert(chan == control->channel);

    /* Restore the channel's PBX */
    ast_channel_pbx_set(control->channel, control->pbx);
    control->pbx = NULL;

    /* No longer in the bridge */
    control->bridge = NULL;

    /* Get the bridge channel so we don't depart from the wrong bridge */
    ast_channel_lock(chan);
    bridge_channel = ast_channel_get_bridge_channel(chan);
    ast_channel_unlock(chan);

    /* Depart this channel from the bridge using the command queue if possible */
    if (stasis_app_send_command_async(control, bridge_channel_depart, bridge_channel)) {
        ao2_cleanup(bridge_channel);
    }
}
Example #14
0
void app_deactivate(struct stasis_app *app)
{
	SCOPED_AO2LOCK(lock, app);
	ast_verb(1, "Deactivating Stasis app '%s'\n", app->name);
	app->handler = NULL;
	ao2_cleanup(app->data);
	app->data = NULL;
}
Example #15
0
int ast_threadpool_push(struct ast_threadpool *pool, int (*task)(void *data), void *data)
{
	SCOPED_AO2LOCK(lock, pool);
	if (!pool->shutting_down) {
		return ast_taskprocessor_push(pool->tps, task, data);
	}
	return -1;
}
static void recording_set_state(struct stasis_app_recording *recording,
				enum stasis_app_recording_state state,
				const char *cause)
{
	SCOPED_AO2LOCK(lock, recording);
	recording->state = state;
	recording_publish(recording, cause);
}
Example #17
0
struct ast_bridge *stasis_app_get_bridge(struct stasis_app_control *control)
{
    if (!control) {
        return NULL;
    } else {
        SCOPED_AO2LOCK(lock, control);
        return control->bridge;
    }
}
Example #18
0
void app_shutdown(struct stasis_app *app)
{
	SCOPED_AO2LOCK(lock, app);

	ast_assert(app_is_finished(app));

	stasis_message_router_unsubscribe(app->router);
	app->router = NULL;
	stasis_message_router_unsubscribe(app->bridge_router);
	app->bridge_router = NULL;
}
Example #19
0
void app_set_debug(struct stasis_app *app, int debug)
{
	if (!app) {
		return;
	}

	{
		SCOPED_AO2LOCK(lock, app);
		app->debug = debug;
	}
}
Example #20
0
int stasis_subscription_is_done(struct stasis_subscription *subscription)
{
	if (subscription) {
		SCOPED_AO2LOCK(lock, subscription);

		return subscription->final_message_rxed;
	}

	/* Null subscription is about as done as you can get */
	return 1;
}
Example #21
0
void stasis_subscription_join(struct stasis_subscription *subscription)
{
	if (subscription) {
		SCOPED_AO2LOCK(lock, subscription);

		/* Wait until the processed flag has been set */
		while (!subscription->final_message_processed) {
			ast_cond_wait(&subscription->join_cond,
				ao2_object_get_lockaddr(subscription));
		}
	}
}
Example #22
0
static void internal_bridge_after_cb(struct ast_channel *chan, void *data,
	enum ast_bridge_after_cb_reason reason)
{
	struct stasis_app_control *control = data;
	SCOPED_AO2LOCK(lock, control);
	struct ast_bridge_channel *bridge_channel;

	ast_debug(3, "%s, %s: %s\n",
		ast_channel_uniqueid(chan), control->bridge ? control->bridge->uniqueid : "unknown",
			ast_bridge_after_cb_reason_string(reason));

	if (reason == AST_BRIDGE_AFTER_CB_REASON_IMPART_FAILED) {
		/* The impart actually failed so control->bridge isn't valid. */
		control->bridge = NULL;
	}

	ast_assert(chan == control->channel);

	/* Restore the channel's PBX */
	ast_channel_pbx_set(control->channel, control->pbx);
	control->pbx = NULL;

	if (control->bridge) {
		app_unsubscribe_bridge(control->app, control->bridge);

		/* No longer in the bridge */
		control->bridge = NULL;

		/* Get the bridge channel so we don't depart from the wrong bridge */
		ast_channel_lock(chan);
		bridge_channel = ast_channel_get_bridge_channel(chan);
		ast_channel_unlock(chan);

		/* Depart this channel from the bridge using the command queue if possible */
		stasis_app_send_command_async(control, bridge_channel_depart, bridge_channel, __ao2_cleanup);
	}

	if (stasis_app_channel_is_stasis_end_published(chan)) {
		/* The channel has had a StasisEnd published on it, but until now had remained in
		 * the bridging system. This means that the channel moved from a Stasis bridge to a
		 * non-Stasis bridge and is now exiting the bridging system. Because of this, the
		 * channel needs to exit the Stasis application and go to wherever the non-Stasis
		 * bridge has directed it to go. If the non-Stasis bridge has not set up an after
		 * bridge destination, then the channel should be hung up.
		 */
		int hangup_flag;

		hangup_flag = ast_bridge_setup_after_goto(chan) ? AST_SOFTHANGUP_DEV : AST_SOFTHANGUP_ASYNCGOTO;
		ast_channel_lock(chan);
		ast_softhangup_nolock(chan, hangup_flag);
		ast_channel_unlock(chan);
	}
}
Example #23
0
/*!
 * \brief Invoke the subscription's callback.
 * \param sub Subscription to invoke.
 * \param topic Topic message was published to.
 * \param message Message to send.
 */
static void subscription_invoke(struct stasis_subscription *sub,
				  struct stasis_message *message)
{
	/* Notify that the final message has been received */
	if (stasis_subscription_final_message(sub, message)) {
		SCOPED_AO2LOCK(lock, sub);

		sub->final_message_rxed = 1;
		ast_cond_signal(&sub->join_cond);
	}

	/* Since sub is mostly immutable, no need to lock sub */
	sub->callback(sub->data, sub, message);

	/* Notify that the final message has been processed */
	if (stasis_subscription_final_message(sub, message)) {
		SCOPED_AO2LOCK(lock, sub);

		sub->final_message_processed = 1;
		ast_cond_signal(&sub->join_cond);
	}
}
Example #24
0
static int topic_remove_subscription(struct stasis_topic *topic, struct stasis_subscription *sub)
{
	size_t idx;
	SCOPED_AO2LOCK(lock_topic, topic);

	for (idx = 0; idx < AST_VECTOR_SIZE(&topic->upstream_topics); ++idx) {
		topic_remove_subscription(
			AST_VECTOR_GET(&topic->upstream_topics, idx), sub);
	}

	return AST_VECTOR_REMOVE_ELEM_UNORDERED(&topic->subscribers, sub,
		AST_VECTOR_ELEM_CLEANUP_NOOP);
}
Example #25
0
/*!
 * \brief Taskprocessor listener emptied callback
 *
 * The threadpool queues a task to let the threadpool listener know that
 * the threadpool no longer contains any tasks.
 * \param listener The taskprocessor listener. The threadpool is the listener's private data.
 */
static void threadpool_tps_emptied(struct ast_taskprocessor_listener *listener)
{
	struct ast_threadpool *pool = ast_taskprocessor_listener_get_user_data(listener);
	SCOPED_AO2LOCK(lock, pool);

	if (pool->shutting_down) {
		return;
	}

	if (pool->listener && pool->listener->callbacks->emptied) {
		ast_taskprocessor_push(pool->control_tps, queued_emptied, pool);
	}
}
Example #26
0
static void threadpool_idle_thread_dead(struct ast_threadpool *pool,
		struct worker_thread *worker)
{
	struct thread_worker_pair *pair;
	SCOPED_AO2LOCK(lock, pool);
	if (pool->shutting_down) {
		return;
	}
	pair = thread_worker_pair_alloc(pool, worker);
	if (!pair) {
		return;
	}
	ast_taskprocessor_push(pool->control_tps, queued_idle_thread_dead, pair);
}
Example #27
0
static void app_control_unregister_rule(
    const struct stasis_app_control *control,
    struct app_control_rules *list, struct stasis_app_control_rule *obj)
{
    struct stasis_app_control_rule *rule;
    SCOPED_AO2LOCK(lock, control->command_queue);
    AST_RWLIST_TRAVERSE_SAFE_BEGIN(list, rule, next) {
        if (rule == obj) {
            AST_RWLIST_REMOVE_CURRENT(next);
            break;
        }
    }
    AST_RWLIST_TRAVERSE_SAFE_END;
}
Example #28
0
void ast_threadpool_set_size(struct ast_threadpool *pool, unsigned int size)
{
	struct set_size_data *ssd;
	SCOPED_AO2LOCK(lock, pool);
	if (pool->shutting_down) {
		return;
	}

	ssd = set_size_data_alloc(pool, size);
	if (!ssd) {
		return;
	}

	ast_taskprocessor_push(pool->control_tps, queued_set_size, ssd);
}
Example #29
0
int stasis_subscription_is_subscribed(const struct stasis_subscription *sub)
{
	if (sub) {
		size_t i;
		struct stasis_topic *topic = sub->topic;
		SCOPED_AO2LOCK(lock_topic, topic);

		for (i = 0; i < AST_VECTOR_SIZE(&topic->subscribers); ++i) {
			if (AST_VECTOR_GET(&topic->subscribers, i) == sub) {
				return 1;
			}
		}
	}

	return 0;
}
Example #30
0
/*!
 * \brief Explicitly shutdown a session.
 *
 * An explicit shutdown is necessary, since stasis-app has a reference to this
 * session. We also need to be sure to null out the \c ws_session field, since
 * the websocket is about to go away.
 *
 * \param session Session info struct.
 */
static void session_shutdown(struct event_session *session)
{
        struct ao2_iterator i;
	char *app;
	SCOPED_AO2LOCK(lock, session);

	i = ao2_iterator_init(session->websocket_apps, 0);
	while ((app = ao2_iterator_next(&i))) {
		stasis_app_unregister(app);
		ao2_cleanup(app);
	}
	ao2_iterator_destroy(&i);
	ao2_cleanup(session->websocket_apps);

	session->websocket_apps = NULL;
	session->ws_session = NULL;
}