Exemplo n.º 1
0
static void caching_topic_exec(void *data, struct stasis_subscription *sub,
	struct stasis_message *message)
{
	RAII_VAR(struct stasis_caching_topic *, caching_topic_needs_unref, NULL, ao2_cleanup);
	struct stasis_caching_topic *caching_topic = data;
	const char *id = NULL;

	ast_assert(caching_topic != NULL);
	ast_assert(caching_topic->topic != NULL);
	ast_assert(caching_topic->cache != NULL);
	ast_assert(caching_topic->cache->id_fn != NULL);

	if (stasis_subscription_final_message(sub, message)) {
		caching_topic_needs_unref = caching_topic;
	}

	/* Handle cache clear event */
	if (stasis_cache_clear_type() == stasis_message_type(message)) {
		RAII_VAR(struct stasis_message *, old_snapshot, NULL, ao2_cleanup);
		RAII_VAR(struct stasis_message *, update, NULL, ao2_cleanup);
		struct stasis_message *clear_msg = stasis_message_data(message);
		const char *clear_id = caching_topic->cache->id_fn(clear_msg);
		struct stasis_message_type *clear_type = stasis_message_type(clear_msg);

		ast_assert(clear_type != NULL);

		if (clear_id) {
			old_snapshot = cache_put(caching_topic->cache, clear_type, clear_id, NULL);
			if (old_snapshot) {
				update = update_create(old_snapshot, NULL);
				stasis_publish(caching_topic->topic, update);
				return;
			}

			ast_log(LOG_ERROR,
				"Attempting to remove an item from the %s cache that isn't there: %s %s\n",
				stasis_topic_name(caching_topic->topic), stasis_message_type_name(clear_type), clear_id);
			return;
		}
	}

	id = caching_topic->cache->id_fn(message);
	if (id == NULL) {
		/* Object isn't cached; discard */
	} else {
		/* Update the cache */
		RAII_VAR(struct stasis_message *, old_snapshot, NULL, ao2_cleanup);
		RAII_VAR(struct stasis_message *, update, NULL, ao2_cleanup);

		old_snapshot = cache_put(caching_topic->cache, stasis_message_type(message), id, message);

		update = update_create(old_snapshot, message);
		if (update == NULL) {
			return;
		}

		stasis_publish(caching_topic->topic, update);
	}
}
Exemplo n.º 2
0
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);
	}
}
Exemplo n.º 3
0
static void mwi_update_cb(void *data, struct stasis_subscription *sub,
				    struct stasis_message *message)
{
	struct ast_mwi_state *mwi_state;
	RAII_VAR(struct ast_str *, channel_event_string, NULL, ast_free);

	if (ast_mwi_state_type() != stasis_message_type(message)) {
		return;
	}

	mwi_state = stasis_message_data(message);
	if (!mwi_state) {
		return;
	}

	if (mwi_state->snapshot) {
		channel_event_string = ast_manager_build_channel_state_string(mwi_state->snapshot);
	}

	/*** DOCUMENTATION
		<managerEventInstance>
			<synopsis>Raised when the state of messages in a voicemail mailbox
			has changed or when a channel has finished interacting with a
			mailbox.</synopsis>
			<syntax>
				<channel_snapshot/>
				<parameter name="Mailbox">
					<para>The mailbox with the new message, specified as <literal>mailbox</literal>@<literal>context</literal></para>
				</parameter>
				<parameter name="Waiting">
					<para>Whether or not the mailbox has messages waiting for it.</para>
				</parameter>
				<parameter name="New">
					<para>The number of new messages.</para>
				</parameter>
				<parameter name="Old">
					<para>The number of old messages.</para>
				</parameter>
			</syntax>
			<description>
				<note><para>The Channel related parameters are only present if a
				channel was involved in the manipulation of a mailbox. If no
				channel is involved, the parameters are not included with the
				event.</para>
				</note>
			</description>
		</managerEventInstance>
	***/
	manager_event(EVENT_FLAG_CALL, "MessageWaiting",
			"%s"
			"Mailbox: %s\r\n"
			"Waiting: %d\r\n"
			"New: %d\r\n"
			"Old: %d\r\n",
			AS_OR(channel_event_string, ""),
			mwi_state->uniqueid,
			ast_app_has_voicemail(mwi_state->uniqueid, NULL),
			mwi_state->new_msgs,
			mwi_state->old_msgs);
}
Exemplo n.º 4
0
static void appcdr_callback(void *data, struct stasis_subscription *sub, struct stasis_message *message)
{
	struct app_cdr_message_payload *payload;

	if (stasis_message_type(message) != appcdr_message_type()) {
		return;
	}

	payload = stasis_message_data(message);
	if (!payload) {
		return;
	}

	if (payload->disable) {
		if (ast_cdr_set_property(payload->channel_name, AST_CDR_FLAG_DISABLE_ALL)) {
			ast_log(AST_LOG_WARNING, "Failed to disable CDRs on channel %s\n",
				payload->channel_name);
		}
	}

	if (payload->reenable) {
		if (ast_cdr_clear_property(payload->channel_name, AST_CDR_FLAG_DISABLE_ALL)) {
			ast_log(AST_LOG_WARNING, "Failed to enable CDRs on channel %s\n",
				payload->channel_name);
		}
	}

	if (payload->reset) {
		if (ast_cdr_reset(payload->channel_name, payload->keep_variables)) {
			ast_log(AST_LOG_WARNING, "Failed to reset CDRs on channel %s\n",
				payload->channel_name);
		}
	}
}
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;
}
Exemplo n.º 6
0
static void parking_event_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message)
{
	if (stasis_message_type(message) == ast_parked_call_type()) {
		struct ast_parked_call_payload *parked_call_message = stasis_message_data(message);
		parked_call_message_response(parked_call_message);
	}
}
Exemplo n.º 7
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;
}
Exemplo n.º 8
0
static void rtp_topic_handler(void *data, struct stasis_subscription *sub, struct stasis_message *message)
{
	struct stasis_message_type *message_type = stasis_message_type(message);

	if ((message_type == ast_rtp_rtcp_sent_type()) ||
		(message_type == ast_rtp_rtcp_received_type())) {
		rtcp_message_handler(message);
	}
}
Exemplo n.º 9
0
/*!
 * \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;
}
Exemplo n.º 10
0
static void guarantee_handler(void *data, struct stasis_subscription *sub,
	struct stasis_message *message)
{
	/* Wait for our particular message */
	if (data == message) {
		struct caching_guarantee *guarantee;
		ast_assert(cache_guarantee_type() == stasis_message_type(message));
		guarantee = stasis_message_data(message);

		ast_mutex_lock(&guarantee->lock);
		guarantee->done = 1;
		ast_cond_signal(&guarantee->cond);
		ast_mutex_unlock(&guarantee->lock);
	}
}
Exemplo n.º 11
0
static void security_stasis_cb(void *data, struct stasis_subscription *sub,
	struct stasis_message *message)
{
	struct ast_json_payload *payload = stasis_message_data(message);

	if (stasis_message_type(message) != ast_security_event_type()) {
		return;
	}

	if (!payload) {
		return;
	}

	security_event_stasis_cb(payload->json);
}
Exemplo n.º 12
0
static struct stasis_message *update_create(struct stasis_message *old_snapshot, struct stasis_message *new_snapshot)
{
	RAII_VAR(struct stasis_cache_update *, update, NULL, ao2_cleanup);
	RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);

	ast_assert(old_snapshot != NULL || new_snapshot != NULL);

	update = ao2_alloc_options(sizeof(*update), stasis_cache_update_dtor,
		AO2_ALLOC_OPT_LOCK_NOLOCK);
	if (!update) {
		return NULL;
	}

	if (old_snapshot) {
		ao2_ref(old_snapshot, +1);
		update->old_snapshot = old_snapshot;
		if (!new_snapshot) {
			ao2_ref(stasis_message_type(old_snapshot), +1);
			update->type = stasis_message_type(old_snapshot);
		}
	}
	if (new_snapshot) {
		ao2_ref(new_snapshot, +1);
		update->new_snapshot = new_snapshot;
		ao2_ref(stasis_message_type(new_snapshot), +1);
		update->type = stasis_message_type(new_snapshot);
	}

	msg = stasis_message_create(stasis_cache_update_type(), update);
	if (!msg) {
		return NULL;
	}

	ao2_ref(msg, +1);
	return msg;
}
Exemplo n.º 13
0
static void parker_update_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message)
{
	if (stasis_subscription_final_message(sub, message)) {
		struct parked_subscription_data *ps_data = data;
		ao2_cleanup(ps_data->transfer_data);
		ps_data->transfer_data = NULL;
		ast_free(data);
		return;
	}

	if (stasis_message_type(message) == ast_parked_call_type()) {
		struct ast_parked_call_payload *parked_call_message = stasis_message_data(message);
		parker_parked_call_message_response(parked_call_message, data, sub);
	}
}
Exemplo n.º 14
0
static void mwi_stasis_cb(void *userdata, struct stasis_subscription *sub,
		struct stasis_message *msg)
{
	struct mwi_subscription *mwi_sub = userdata;

	if (stasis_subscription_final_message(sub, msg)) {
		if (ast_sip_push_task(NULL, serialized_cleanup, ao2_bump(mwi_sub))) {
			ao2_ref(mwi_sub, -1);
		}
		return;
	}

	if (ast_mwi_state_type() == stasis_message_type(msg)) {
		send_notify(mwi_sub, NULL, 0);
	}
}
Exemplo n.º 15
0
static void mwi_stasis_cb(void *userdata, struct stasis_subscription *sub,
		struct stasis_message *msg)
{
	struct mwi_subscription *mwi_sub = userdata;

	if (stasis_subscription_final_message(sub, msg)) {
		ao2_ref(mwi_sub, +1);
		ast_sip_push_task(NULL, serialized_cleanup, mwi_sub);
		return;
	}

	if (ast_mwi_state_type() == stasis_message_type(msg)) {
		struct ast_taskprocessor *serializer = mwi_sub->is_solicited ? ast_sip_subscription_get_serializer(mwi_sub->sip_sub) : NULL;
		ao2_ref(mwi_sub, +1);
		ast_sip_push_task(serializer, serialized_notify, mwi_sub);
	}
}
Exemplo n.º 16
0
static void forkcdr_callback(void *data, struct stasis_subscription *sub, struct stasis_message *message)
{
	struct fork_cdr_message_payload *payload;

	if (stasis_message_type(message) != forkcdr_message_type()) {
		return;
	}

	payload = stasis_message_data(message);
	if (!payload) {
		return;
	}

	if (ast_cdr_fork(payload->channel_name, payload->flags)) {
		ast_log(AST_LOG_WARNING, "Failed to fork CDR for channel %s\n",
			payload->channel_name);
	}
}
Exemplo n.º 17
0
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);
	}
}
Exemplo n.º 18
0
int stasis_subscription_final_message(struct stasis_subscription *sub, struct stasis_message *msg)
{
	struct stasis_subscription_change *change;

	if (stasis_message_type(msg) != stasis_subscription_change_type()) {
		return 0;
	}

	change = stasis_message_data(msg);
	if (strcmp("Unsubscribe", change->description)) {
		return 0;
	}

	if (strcmp(stasis_subscription_uniqueid(sub), change->uniqueid)) {
		return 0;
	}

	return 1;
}
Exemplo n.º 19
0
/*! \brief Event callback which fires initial unsolicited MWI NOTIFY messages when we're fully booted */
static void mwi_startup_event_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message)
{
	struct ast_json_payload *payload;
	const char *type;

	if (stasis_message_type(message) != ast_manager_get_generic_type()) {
		return;
	}

	payload = stasis_message_data(message);
	type = ast_json_string_get(ast_json_object_get(payload->json, "type"));

	if (strcmp(type, "FullyBooted")) {
		return;
	}

	ast_sip_push_task(NULL, send_initial_notify_all, NULL);

	stasis_unsubscribe(sub);
}
Exemplo n.º 20
0
/*! \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);
}
Exemplo n.º 21
0
static struct stasis_message *cache_put(struct stasis_cache *cache,
	struct stasis_message_type *type, const char *id,
	struct stasis_message *new_snapshot)
{
	RAII_VAR(struct cache_entry *, new_entry, NULL, ao2_cleanup);
	RAII_VAR(struct cache_entry *, cached_entry, NULL, ao2_cleanup);
	struct stasis_message *old_snapshot = NULL;

	ast_assert(cache->entries != NULL);
	ast_assert(new_snapshot == NULL ||
		type == stasis_message_type(new_snapshot));

	new_entry = cache_entry_create(type, id, new_snapshot);

	if (new_snapshot == NULL) {
		/* Remove entry from cache */
		cached_entry = ao2_find(cache->entries, new_entry, OBJ_POINTER | OBJ_UNLINK);
		if (cached_entry) {
			old_snapshot = cached_entry->snapshot;
			cached_entry->snapshot = NULL;
		}
	} else {
		/* Insert/update cache */
		SCOPED_AO2LOCK(lock, cache->entries);

		cached_entry = ao2_find(cache->entries, new_entry, OBJ_POINTER | OBJ_NOLOCK);
		if (cached_entry) {
			/* Update cache. Because objects are moving, no need to update refcounts. */
			old_snapshot = cached_entry->snapshot;
			cached_entry->snapshot = new_entry->snapshot;
			new_entry->snapshot = NULL;
		} else {
			/* Insert into the cache */
			ao2_link_flags(cache->entries, new_entry, OBJ_NOLOCK);
		}

	}

	return old_snapshot;
}
Exemplo n.º 22
0
/*!
 * \brief Subscription callback for all channel messages.
 * \param data Data pointer given when creating the subscription.
 * \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 statsmaker(void *data, struct stasis_subscription *sub,
	struct stasis_message *message)
{
	RAII_VAR(struct ast_str *, metric, NULL, ast_free);

	if (stasis_subscription_final_message(sub, message)) {
		/* Normally, data points to an object that must be cleaned up.
		 * The final message is an unsubscribe notification that's
		 * guaranteed to be the last message this subscription receives.
		 * This would be a safe place to kick off any needed cleanup.
		 */
		return;
	}

	/* For no good reason, count message types */
	metric = ast_str_create(80);
	if (metric) {
		ast_str_set(&metric, 0, "stasis.message.%s",
			stasis_message_type_name(stasis_message_type(message)));
		ast_statsd_log(ast_str_buffer(metric), AST_STATSD_METER, 1);
	}
}
Exemplo n.º 23
0
static void sub_default_handler(void *data, struct stasis_subscription *sub,
	struct stasis_message *message)
{
	struct stasis_app *app = data;
	struct ast_json *json;

	/* The dial type can be converted to JSON so it will always be passed
	 * here.
	 */
	if (stasis_message_type(message) == ast_channel_dial_type()) {
		call_forwarded_handler(app, message);
	}

	/* By default, send any message that has a JSON representation */
	json = stasis_message_to_json(message, stasis_app_get_sanitizer());
	if (!json) {
		return;
	}

	app_send(app, json);
	ast_json_unref(json);
}
Exemplo n.º 24
0
static void sub_default_handler(void *data, struct stasis_subscription *sub,
	struct stasis_message *message)
{
	struct stasis_app *app = data;
	RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);

	if (stasis_subscription_final_message(sub, message)) {
		ao2_cleanup(app);
	}

	if (stasis_message_type(message) == ast_channel_dial_type()) {
		call_forwarded_handler(app, message);
	}

	/* By default, send any message that has a JSON representation */
	json = stasis_message_to_json(message, stasis_app_get_sanitizer());
	if (!json) {
		return;
	}

	app_send(app, json);
}
Exemplo n.º 25
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);
	}
}
Exemplo n.º 26
0
static struct ast_manager_event_blob *local_message_to_ami(struct stasis_message *message)
{
	struct ast_multi_channel_blob *obj = stasis_message_data(message);
	struct ast_json *blob = ast_multi_channel_blob_get_json(obj);
	struct ast_channel_snapshot *local_snapshot_one;
	struct ast_channel_snapshot *local_snapshot_two;
	RAII_VAR(struct ast_str *, local_channel_one, NULL, ast_free);
	RAII_VAR(struct ast_str *, local_channel_two, NULL, ast_free);
	RAII_VAR(struct ast_str *, event_buffer, NULL, ast_free);
	const char *event;

	local_snapshot_one = ast_multi_channel_blob_get_channel(obj, "1");
	local_snapshot_two = ast_multi_channel_blob_get_channel(obj, "2");
	if (!local_snapshot_one || !local_snapshot_two) {
		return NULL;
	}

	event_buffer = ast_str_create(1024);
	local_channel_one = ast_manager_build_channel_state_string_prefix(local_snapshot_one, "LocalOne");
	local_channel_two = ast_manager_build_channel_state_string_prefix(local_snapshot_two, "LocalTwo");
	if (!event_buffer || !local_channel_one || !local_channel_two) {
		return NULL;
	}

	if (stasis_message_type(message) == ast_local_optimization_begin_type()) {
		struct ast_channel_snapshot *source_snapshot;
		RAII_VAR(struct ast_str *, source_str, NULL, ast_free);
		const char *dest_uniqueid;

		source_snapshot = ast_multi_channel_blob_get_channel(obj, "source");
		if (source_snapshot) {
			source_str = ast_manager_build_channel_state_string_prefix(source_snapshot, "Source");
			if (!source_str) {
				return NULL;
			}
		}

		dest_uniqueid = ast_json_object_get(blob, "dest") == AST_UNREAL_OWNER ?
				local_snapshot_one->uniqueid : local_snapshot_two->uniqueid;

		event = "LocalOptimizationBegin";
		if (source_str) {
			ast_str_append(&event_buffer, 0, "%s", ast_str_buffer(source_str));
		}
		ast_str_append(&event_buffer, 0, "DestUniqueId: %s\r\n", dest_uniqueid);
		ast_str_append(&event_buffer, 0, "Id: %u\r\n", (unsigned int) ast_json_integer_get(ast_json_object_get(blob, "id")));
	} else if (stasis_message_type(message) == ast_local_optimization_end_type()) {
		event = "LocalOptimizationEnd";
		ast_str_append(&event_buffer, 0, "Success: %s\r\n", ast_json_integer_get(ast_json_object_get(blob, "success")) ? "Yes" : "No");
		ast_str_append(&event_buffer, 0, "Id: %u\r\n", (unsigned int) ast_json_integer_get(ast_json_object_get(blob, "id")));
	} else if (stasis_message_type(message) == ast_local_bridge_type()) {
		event = "LocalBridge";
		ast_str_append(&event_buffer, 0, "Context: %s\r\n", ast_json_string_get(ast_json_object_get(blob, "context")));
		ast_str_append(&event_buffer, 0, "Exten: %s\r\n", ast_json_string_get(ast_json_object_get(blob, "exten")));
		ast_str_append(&event_buffer, 0, "LocalOptimization: %s\r\n", ast_json_is_true(ast_json_object_get(blob, "can_optimize")) ? "Yes" : "No");
	} else {
		return NULL;
	}

	return ast_manager_event_blob_create(EVENT_FLAG_CALL, event,
		"%s"
		"%s"
		"%s",
		ast_str_buffer(local_channel_one),
		ast_str_buffer(local_channel_two),
		ast_str_buffer(event_buffer));
}
Exemplo n.º 27
0
static struct timespec make_deadline(int timeout_millis)
{
	struct timeval start = ast_tvnow();
	struct timeval delta = {
		.tv_sec = timeout_millis / 1000,
		.tv_usec = (timeout_millis % 1000) * 1000,
	};
	struct timeval deadline_tv = ast_tvadd(start, delta);
	struct timespec deadline = {
		.tv_sec = deadline_tv.tv_sec,
		.tv_nsec = 1000 * deadline_tv.tv_usec,
	};

	return deadline;
}

struct stasis_message_sink *stasis_message_sink_create(void)
{
	RAII_VAR(struct stasis_message_sink *, sink, NULL, ao2_cleanup);

	sink = ao2_alloc(sizeof(*sink), stasis_message_sink_dtor);
	if (!sink) {
		return NULL;
	}
	ast_mutex_init(&sink->lock);
	ast_cond_init(&sink->cond, NULL);
	sink->max_messages = 4;
	sink->messages =
		ast_malloc(sizeof(*sink->messages) * sink->max_messages);
	if (!sink->messages) {
		return NULL;
	}
	ao2_ref(sink, +1);
	return sink;
}

/*!
 * \brief Implementation of the stasis_message_sink_cb() callback.
 *
 * Why the roundabout way of exposing this via stasis_message_sink_cb()? Well,
 * it has to do with how we load modules.
 *
 * Modules have their own metadata compiled into them in the module info block
 * at the end of the file.  This includes dependency information in the
 * \c nonoptreq field.
 *
 * Asterisk loads the module, inspects the field, then loads any needed
 * dependencies. This works because Asterisk passes \c RTLD_LAZY to the initial
 * dlopen(), which defers binding function references until they are called.
 *
 * But when you take the address of a function, that function needs to be
 * available at load time. So if some module used the address of
 * message_sink_cb() directly, and \c res_stasis_test.so wasn't loaded yet, then
 * that module would fail to load.
 *
 * The stasis_message_sink_cb() function gives us a layer of indirection so that
 * the initial lazy binding will still work as expected.
 */
static void message_sink_cb(void *data, struct stasis_subscription *sub,
	struct stasis_message *message)
{
	struct stasis_message_sink *sink = data;

	SCOPED_MUTEX(lock, &sink->lock);

	if (stasis_subscription_final_message(sub, message)) {
		sink->is_done = 1;
		ast_cond_signal(&sink->cond);
		return;
	}

	if (stasis_subscription_change_type() == stasis_message_type(message)) {
		/* Ignore subscription changes */
		return;
	}

	if (sink->num_messages == sink->max_messages) {
		size_t new_max_messages = sink->max_messages * 2;
		struct stasis_message **new_messages = ast_realloc(
			sink->messages,
			sizeof(*new_messages) * new_max_messages);
		if (!new_messages) {
			return;
		}
		sink->max_messages = new_max_messages;
		sink->messages = new_messages;
	}

	ao2_ref(message, +1);
	sink->messages[sink->num_messages++] = message;
	ast_cond_signal(&sink->cond);
}

stasis_subscription_cb stasis_message_sink_cb(void)
{
	return message_sink_cb;
}


int stasis_message_sink_wait_for_count(struct stasis_message_sink *sink,
	int num_messages, int timeout_millis)
{
	struct timespec deadline = make_deadline(timeout_millis);

	SCOPED_MUTEX(lock, &sink->lock);
	while (sink->num_messages < num_messages) {
		int r = ast_cond_timedwait(&sink->cond, &sink->lock, &deadline);

		if (r == ETIMEDOUT) {
			break;
		}
		if (r != 0) {
			ast_log(LOG_ERROR, "Unexpected condition error: %s\n",
				strerror(r));
			break;
		}
	}
	return sink->num_messages;
}

int stasis_message_sink_should_stay(struct stasis_message_sink *sink,
	int num_messages, int timeout_millis)
{
	struct timespec deadline = make_deadline(timeout_millis);

	SCOPED_MUTEX(lock, &sink->lock);
	while (sink->num_messages == num_messages) {
		int r = ast_cond_timedwait(&sink->cond, &sink->lock, &deadline);

		if (r == ETIMEDOUT) {
			break;
		}
		if (r != 0) {
			ast_log(LOG_ERROR, "Unexpected condition error: %s\n",
				strerror(r));
			break;
		}
	}
	return sink->num_messages;
}

int stasis_message_sink_wait_for(struct stasis_message_sink *sink, int start,
	stasis_wait_cb cmp_cb, const void *data, int timeout_millis)
{
	struct timespec deadline = make_deadline(timeout_millis);

	SCOPED_MUTEX(lock, &sink->lock);

	/* wait for the start */
	while (sink->num_messages < start + 1) {
		int r = ast_cond_timedwait(&sink->cond, &sink->lock, &deadline);

		if (r == ETIMEDOUT) {
			/* Timed out waiting for the start */
			return -1;
		}
		if (r != 0) {
			ast_log(LOG_ERROR, "Unexpected condition error: %s\n",
				strerror(r));
			return -2;
		}
	}


	while (!cmp_cb(sink->messages[start], data)) {
		++start;

		while (sink->num_messages < start + 1) {
			int r = ast_cond_timedwait(&sink->cond,
				&sink->lock, &deadline);

			if (r == ETIMEDOUT) {
				return -1;
			}
			if (r != 0) {
				ast_log(LOG_ERROR,
					"Unexpected condition error: %s\n",
					strerror(r));
				return -2;
			}
		}
	}

	return start;
}

struct stasis_message *stasis_test_message_create(void)
{
	RAII_VAR(void *, data, NULL, ao2_cleanup);

	if (!stasis_test_message_type()) {
		return NULL;
	}

	/* We just need the unique pointer; don't care what's in it */
	data = ao2_alloc(1, NULL);
	if (!data) {
		return NULL;
	}

	return stasis_message_create(stasis_test_message_type(), data);
}