Beispiel #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);
	}
}
Beispiel #2
0
static void publish_message_for_channel_topics(struct stasis_message *message, struct ast_channel *chan)
{
	if (chan) {
		stasis_publish(ast_channel_topic(chan), message);
	} else {
		stasis_publish(ast_channel_topic_all(), message);
	}
}
/*! \brief Callback for \ref ast_unreal_pvt_callbacks \ref optimization_finished_cb */
static void local_optimization_finished_cb(struct ast_unreal_pvt *base, int success, unsigned int id)
{
	RAII_VAR(struct ast_json *, json_object, ast_json_null(), ast_json_unref);
	RAII_VAR(struct ast_multi_channel_blob *, payload, NULL, ao2_cleanup);
	RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
	struct local_pvt *p = (struct local_pvt *)base;

	if (!ast_local_optimization_end_type()) {
		return;
	}

	json_object = ast_json_pack("{s: i, s: i}", "success", success, "id", id);

	if (!json_object) {
		return;
	}

	payload = local_channel_optimization_blob(p, json_object);
	if (!payload) {
		return;
	}

	msg = stasis_message_create(ast_local_optimization_end_type(), payload);
	if (!msg) {
		return;
	}

	stasis_publish(ast_channel_topic(p->base.owner), msg);
}
Beispiel #4
0
int stasis_topic_wait(struct stasis_topic *topic)
{
	RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
	RAII_VAR(struct stasis_subscription *, sub, NULL, stasis_unsubscribe);
	struct caching_guarantee *guarantee;

	msg = caching_guarantee_create();
	if (!msg) {
		return -1;
	}

	sub = stasis_subscribe(topic, guarantee_handler, msg);
	if (!sub) {
		return -1;
	}

	guarantee = stasis_message_data(msg);

	ast_mutex_lock(&guarantee->lock);
	stasis_publish(topic, msg);
	while (!guarantee->done) {
		ast_cond_wait(&guarantee->cond, &guarantee->lock);
	}
	ast_mutex_unlock(&guarantee->lock);
	return 0;
}
Beispiel #5
0
static void send_subscription_unsubscribe(struct stasis_topic *topic,
	struct stasis_subscription *sub)
{
	struct stasis_subscription_change *change;
	struct stasis_message *msg;

	/* This assumes that we have already unsubscribed */
	ast_assert(!stasis_subscription_is_subscribed(sub));

	if (!stasis_subscription_change_type()) {
		return;
	}

	change = subscription_change_alloc(topic, sub->uniqueid, "Unsubscribe");
	if (!change) {
		return;
	}

	msg = stasis_message_create(stasis_subscription_change_type(), change);
	if (!msg) {
		ao2_cleanup(change);
		return;
	}

	stasis_publish(topic, msg);

	/* Now we have to dispatch to the subscription itself */
	dispatch_message(sub, msg, 0);

	ao2_cleanup(msg);
	ao2_cleanup(change);
}
Beispiel #6
0
static void endpoint_state_cb(void *data, struct stasis_subscription *sub,
	struct stasis_message *message)
{
	struct stasis_app *app = data;

	stasis_publish(app->topic, message);
}
void ast_system_publish_registry(const char *channeltype, const char *username, const char *domain, const char *status, const char *cause)
{
    RAII_VAR(struct ast_json *, registry, NULL, ast_json_unref);
    RAII_VAR(struct ast_json_payload *, payload, NULL, ao2_cleanup);
    RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);

    if (!ast_system_registry_type()) {
        return;
    }

    registry = ast_json_pack("{s: s, s: s, s: s, s: s, s: s, s: s}",
                             "type", "registry",
                             "channeltype", channeltype,
                             "username", username,
                             "domain", domain,
                             "status", status,
                             "cause", S_OR(cause, ""));

    if (!(payload = ast_json_payload_create(registry))) {
        return;
    }

    if (!(message = stasis_message_create(ast_system_registry_type(), payload))) {
        return;
    }

    stasis_publish(ast_system_topic(), message);
}
Beispiel #8
0
/*! \brief Publish single channel user event (for app_userevent compatibility) */
void ast_multi_object_blob_single_channel_publish(struct ast_channel *chan,
	struct stasis_message_type *type, struct ast_json *blob)
{
	RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
	RAII_VAR(struct ast_channel_snapshot *, channel_snapshot, NULL, ao2_cleanup);
	RAII_VAR(struct ast_multi_object_blob *, multi, NULL, ao2_cleanup);

	if (!type) {
		return;
	}

	multi = ast_multi_object_blob_create(blob);
	if (!multi) {
		return;
	}

	channel_snapshot = ast_channel_snapshot_create(chan);
	ao2_ref(channel_snapshot, +1);
	ast_multi_object_blob_add(multi, STASIS_UMOS_CHANNEL, channel_snapshot);

	message = stasis_message_create(type, multi);
	if (message) {
		/* app_userevent still publishes to channel */
		stasis_publish(ast_channel_topic(chan), message);
	}
}
Beispiel #9
0
/*! \brief Publish a Corosync ping to \ref stasis */
static void publish_corosync_ping_to_stasis(struct ast_event *event)
{
	struct corosync_ping_payload *payload;
	struct stasis_message *message;

	ast_assert(ast_event_get_type(event) == AST_EVENT_PING);
	ast_assert(event != NULL);

	if (!corosync_ping_message_type()) {
		return;
	}

	payload = ao2_t_alloc(sizeof(*payload), corosync_ping_payload_dtor, "Create ping payload");
	if (!payload) {
		return;
	}
	payload->event = event;

	message = stasis_message_create(corosync_ping_message_type(), payload);
	if (!message) {
		ao2_t_ref(payload, -1, "Destroy payload on off nominal");
		return;
	}

	stasis_publish(corosync_topic(), message);

	ao2_t_ref(payload, -1, "Hand ref to stasis");
	ao2_t_ref(message, -1, "Hand ref to stasis");
}
Beispiel #10
0
void stasis_app_control_publish(
    struct stasis_app_control *control, struct stasis_message *message)
{
    if (!control || !control->channel || !message) {
        return;
    }
    stasis_publish(ast_channel_topic(control->channel), message);
}
/*!
 * \internal
 * \brief Post the \ref ast_local_bridge_type \ref stasis message
 * \since 12.0.0
 *
 * \param p local_pvt to raise the local bridge message
 *
 * \return Nothing
 */
static void publish_local_bridge_message(struct local_pvt *p)
{
	RAII_VAR(struct ast_multi_channel_blob *, multi_blob, NULL, ao2_cleanup);
	RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
	RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
	RAII_VAR(struct ast_channel_snapshot *, one_snapshot, NULL, ao2_cleanup);
	RAII_VAR(struct ast_channel_snapshot *, two_snapshot, NULL, ao2_cleanup);
	struct ast_channel *owner;
	struct ast_channel *chan;

	if (!ast_local_bridge_type()) {
		return;
	}

	ast_unreal_lock_all(&p->base, &chan, &owner);

	blob = ast_json_pack("{s: s, s: s, s: b}",
		"context", p->context,
		"exten", p->exten,
		"can_optimize", !ast_test_flag(&p->base, AST_UNREAL_NO_OPTIMIZATION));
	if (!blob) {
		goto end;
	}

	multi_blob = ast_multi_channel_blob_create(blob);
	if (!multi_blob) {
		goto end;
	}

	one_snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(owner));
	if (!one_snapshot) {
		goto end;
	}

	two_snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(chan));
	if (!two_snapshot) {
		goto end;
	}

	ast_multi_channel_blob_add_channel(multi_blob, "1", one_snapshot);
	ast_multi_channel_blob_add_channel(multi_blob, "2", two_snapshot);

	msg = stasis_message_create(ast_local_bridge_type(), multi_blob);
	if (!msg) {
		goto end;
	}

	stasis_publish(ast_channel_topic(owner), msg);

end:
	ast_channel_unlock(owner);
	ast_channel_unref(owner);

	ast_channel_unlock(chan);
	ast_channel_unref(chan);

	ao2_unlock(&p->base);
}
Beispiel #12
0
void ast_endpoint_blob_publish(struct ast_endpoint *endpoint, struct stasis_message_type *type,
	struct ast_json *blob)
{
	RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
	if (blob) {
		message = ast_endpoint_blob_create(endpoint, type, blob);
	}
	if (message) {
		stasis_publish(ast_endpoint_topic(endpoint), message);
	}
}
Beispiel #13
0
static void bridge_blind_transfer_handler(void *data, struct stasis_subscription *sub,
	struct stasis_message *message)
{
	struct stasis_app *app = data;
	struct ast_blind_transfer_message *transfer_msg = stasis_message_data(message);
	struct ast_bridge_snapshot *bridge = transfer_msg->bridge;

	if (bridge_app_subscribed(app, transfer_msg->transferer->base->uniqueid) ||
		(bridge && bridge_app_subscribed_involved(app, bridge))) {
		stasis_publish(app->topic, message);
	}
}
Beispiel #14
0
void ast_channel_publish_blob(struct ast_channel *chan, struct stasis_message_type *type, struct ast_json *blob)
{
	RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);

	if (!blob) {
		blob = ast_json_null();
	}

	message = ast_channel_blob_create(chan, type, blob);
	if (message) {
		stasis_publish(ast_channel_topic(chan), message);
	}
}
Beispiel #15
0
static void bridge_merge_handler(void *data, struct stasis_subscription *sub,
	struct stasis_message *message)
{
	struct stasis_app *app = data;
	struct ast_bridge_merge_message *merge;

	merge = stasis_message_data(message);

	/* Find out if we're subscribed to either bridge */
	if (bridge_app_subscribed(app, merge->from->uniqueid) ||
		bridge_app_subscribed(app, merge->to->uniqueid)) {
		/* Forward the message to the app */
		stasis_publish(app->topic, message);
	}
}
Beispiel #16
0
void publish_parked_call(struct parked_user *pu, enum ast_parked_call_event_type event_type)
{
	RAII_VAR(struct ast_parked_call_payload *, payload, NULL, ao2_cleanup);
	RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);

	payload = parked_call_payload_from_parked_user(pu, event_type);
	if (!payload) {
		return;
	}

	msg = stasis_message_create(ast_parked_call_type(), payload);
	if (!msg) {
		return;
	}

	stasis_publish(ast_parking_topic(), msg);
}
Beispiel #17
0
void publish_parked_call_failure(struct ast_channel *parkee)
{
	RAII_VAR(struct ast_parked_call_payload *, payload, NULL, ao2_cleanup);
	RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);

	payload = parked_call_payload_from_failure(parkee);
	if (!payload) {
		return;
	}

	msg = stasis_message_create(ast_parked_call_type(), payload);
	if (!msg) {
		return;
	}

	stasis_publish(ast_parking_topic(), msg);
}
Beispiel #18
0
static void bridge_attended_transfer_handler(void *data, struct stasis_subscription *sub,
	struct stasis_message *message)
{
	struct stasis_app *app = data;
	struct ast_attended_transfer_message *transfer_msg = stasis_message_data(message);
	int subscribed = 0;

	subscribed = bridge_app_subscribed(app, transfer_msg->to_transferee.channel_snapshot->base->uniqueid);
	if (!subscribed) {
		subscribed = bridge_app_subscribed(app, transfer_msg->to_transfer_target.channel_snapshot->base->uniqueid);
	}
	if (!subscribed && transfer_msg->to_transferee.bridge_snapshot) {
		subscribed = bridge_app_subscribed_involved(app, transfer_msg->to_transferee.bridge_snapshot);
	}
	if (!subscribed && transfer_msg->to_transfer_target.bridge_snapshot) {
		subscribed = bridge_app_subscribed_involved(app, transfer_msg->to_transfer_target.bridge_snapshot);
	}

	if (!subscribed) {
		switch (transfer_msg->dest_type) {
		case AST_ATTENDED_TRANSFER_DEST_BRIDGE_MERGE:
			subscribed = bridge_app_subscribed(app, transfer_msg->dest.bridge);
			break;
		case AST_ATTENDED_TRANSFER_DEST_LINK:
			subscribed = bridge_app_subscribed(app, transfer_msg->dest.links[0]->base->uniqueid);
			if (!subscribed) {
				subscribed = bridge_app_subscribed(app, transfer_msg->dest.links[1]->base->uniqueid);
			}
			break;
		break;
		case AST_ATTENDED_TRANSFER_DEST_THREEWAY:
			subscribed = bridge_app_subscribed_involved(app, transfer_msg->dest.threeway.bridge_snapshot);
			if (!subscribed) {
				subscribed = bridge_app_subscribed(app, transfer_msg->dest.threeway.channel_snapshot->base->uniqueid);
			}
			break;
		default:
			break;
		}
	}

	if (subscribed) {
		stasis_publish(app->topic, message);
	}
}
Beispiel #19
0
/*! \brief Publish cluster discovery to \ref stasis */
static void publish_cluster_discovery_to_stasis_full(struct corosync_node *node, int joined)
{
	struct ast_json *json;
	struct ast_json_payload *payload;
	struct stasis_message *message;
	char eid[18];
	const char *addr;

	ast_eid_to_str(eid, sizeof(eid), &node->eid);
	addr = ast_sockaddr_stringify_addr(&node->addr);

	ast_log(AST_LOG_NOTICE, "Node %u (%s) at %s %s the cluster\n",
		node->id,
		eid,
		addr,
		joined ? "joined" : "left");

	json = ast_json_pack("{s: s, s: i, s: s, s: i}",
		"address", addr,
		"node_id", node->id,
		"eid", eid,
		"joined", joined);
	if (!json) {
		return;
	}

	payload = ast_json_payload_create(json);
	if (!payload) {
		ast_json_unref(json);
		return;
	}

	message = stasis_message_create(ast_cluster_discovery_type(), payload);
	if (!message) {
		ast_json_unref(json);
		ao2_ref(payload, -1);
		return;
	}

	stasis_publish(ast_system_topic(), message);
	ast_json_unref(json);
	ao2_ref(payload, -1);
	ao2_ref(message, -1);
}
Beispiel #20
0
static int send_call_pickup_stasis_message(struct ast_channel *picking_up, struct ast_channel_snapshot *chan, struct ast_channel_snapshot *target)
{
	RAII_VAR(struct ast_multi_channel_blob *, pickup_payload, NULL, ao2_cleanup);
	RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);

	if (!(pickup_payload = ast_multi_channel_blob_create(ast_json_null()))) {
		return -1;
	}

	ast_multi_channel_blob_add_channel(pickup_payload, "channel", chan);
	ast_multi_channel_blob_add_channel(pickup_payload, "target", target);

	if (!(msg = stasis_message_create(ast_call_pickup_type(), pickup_payload))) {
		return -1;
	}

	stasis_publish(ast_channel_topic(picking_up), msg);
	return 0;
}
/*! \brief Callback for \ref ast_unreal_pvt_callbacks \ref optimization_started_cb */
static void local_optimization_started_cb(struct ast_unreal_pvt *base, struct ast_channel *source,
		enum ast_unreal_channel_indicator dest, unsigned int id)
{
	RAII_VAR(struct ast_json *, json_object, ast_json_null(), ast_json_unref);
	RAII_VAR(struct ast_multi_channel_blob *, payload, NULL, ao2_cleanup);
	RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
	struct local_pvt *p = (struct local_pvt *)base;

	if (!ast_local_optimization_begin_type()) {
		return;
	}

	json_object = ast_json_pack("{s: i, s: i}",
			"dest", dest, "id", id);

	if (!json_object) {
		return;
	}

	payload = local_channel_optimization_blob(p, json_object);
	if (!payload) {
		return;
	}

	if (source) {
		RAII_VAR(struct ast_channel_snapshot *, source_snapshot, NULL, ao2_cleanup);
		source_snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(source));
		if (!source_snapshot) {
			return;
		}

		ast_multi_channel_blob_add_channel(payload, "source", source_snapshot);
	}

	msg = stasis_message_create(ast_local_optimization_begin_type(), payload);
	if (!msg) {
		return;
	}

	stasis_publish(ast_channel_topic(p->base.owner), msg);
}
Beispiel #22
0
/*! \internal
 * \brief Publish the chanspy message over Stasis-Core
 * \param spyer The channel doing the spying
 * \param spyee Who is being spied upon
 * \start start If non-zero, the spying is starting. Otherwise, the spyer is
 * finishing
 */
static void publish_chanspy_message(struct ast_channel *spyer,
									struct ast_channel *spyee,
									int start)
{
	RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
	RAII_VAR(struct ast_multi_channel_blob *, payload, NULL, ao2_cleanup);
	RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);

	if (!spyer) {
		ast_log(AST_LOG_WARNING, "Attempt to publish ChanSpy message for NULL spyer channel\n");
		return;
	}
	blob = ast_json_null();
	if (!blob) {
		return;
	}

	payload = ast_multi_channel_blob_create(blob);
	if (!payload) {
		return;
	}

	if (pack_channel_into_message(spyer, "spyer_channel", payload)) {
		return;
	}

	if (spyee) {
		if (pack_channel_into_message(spyee, "spyee_channel", payload)) {
			return;
		}
	}

	message = stasis_message_create(
			start ? ast_channel_chanspy_start_type(): ast_channel_chanspy_stop_type(),
					payload);
	if (!message) {
		return;
	}
	stasis_publish(ast_channel_topic(spyer), message);
}
Beispiel #23
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 #24
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 #25
0
/*! \internal
 * \brief Publish the chanspy message over Stasis-Core
 * \param snoop The snoop structure
 * \start start If non-zero, the spying is starting. Otherwise, the spyer is
 * finishing
 */
static void publish_chanspy_message(struct stasis_app_snoop *snoop, int start)
{
	RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
	RAII_VAR(struct ast_multi_channel_blob *, payload, NULL, ao2_cleanup);
	RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
	RAII_VAR(struct ast_channel_snapshot *, snoop_snapshot, NULL, ao2_cleanup);
	RAII_VAR(struct ast_channel_snapshot *, spyee_snapshot, NULL, ao2_cleanup);
	struct stasis_message_type *type = start ? ast_channel_chanspy_start_type(): ast_channel_chanspy_stop_type();

	blob = ast_json_null();
	if (!blob || !type) {
		return;
	}

	payload = ast_multi_channel_blob_create(blob);
	if (!payload) {
		return;
	}

	snoop_snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(snoop->chan));
	if (!snoop_snapshot) {
		return;
	}
	ast_multi_channel_blob_add_channel(payload, "spyer_channel", snoop_snapshot);

	spyee_snapshot = ast_channel_snapshot_get_latest(snoop->uniqueid);
	if (spyee_snapshot) {
		ast_multi_channel_blob_add_channel(payload, "spyee_channel", spyee_snapshot);
	}

	message = stasis_message_create(type, payload);
	if (!message) {
		return;
	}

	stasis_publish(ast_channel_topic(snoop->chan), message);
}
static void endpoint_state_cb(void *data, struct stasis_subscription *sub,
	struct stasis_message *message)
{
	stasis_publish(ast_manager_get_topic(), message);
}
static void consumer_wait_for(struct consumer *consumer)
{
    int res;
    struct timeval start = ast_tvnow();
    struct timespec end = {
        .tv_sec = start.tv_sec + 10,
        .tv_nsec = start.tv_usec * 1000
    };

    SCOPED_AO2LOCK(lock, consumer);

    while (!consumer->already_out) {
        res = ast_cond_timedwait(&consumer->out, ao2_object_get_lockaddr(consumer), &end);
        if (!res || res == ETIMEDOUT) {
            break;
        }
    }
}

static int remove_device_states_cb(void *obj, void *arg, int flags)
{
    struct stasis_message *msg = obj;
    struct ast_device_state_message *device_state = stasis_message_data(msg);

    if (strcmp(UNIT_TEST_DEVICE_IDENTIFIER, device_state->device)) {
        /* Not a unit test device */
        return 0;
    }

    msg = stasis_cache_clear_create(msg);
    if (msg) {
        /* topic guaranteed to have been created by this point */
        stasis_publish(ast_device_state_topic(device_state->device), msg);
    }
    ao2_cleanup(msg);
    return 0;
}

static void cache_cleanup(int unused)
{
    struct ao2_container *cache_dump;

    /* remove all device states created during this test */
    cache_dump = stasis_cache_dump_all(ast_device_state_cache(), NULL);
    if (!cache_dump) {
        return;
    }
    ao2_callback(cache_dump, 0, remove_device_states_cb, NULL);
    ao2_cleanup(cache_dump);
}

AST_TEST_DEFINE(device_state_aggregation_test)
{
    RAII_VAR(struct consumer *, consumer, NULL, ao2_cleanup);
    RAII_VAR(struct stasis_message_router *, device_msg_router, NULL, stasis_message_router_unsubscribe);
    RAII_VAR(struct ast_eid *, foreign_eid, NULL, ast_free);
    RAII_VAR(int, cleanup_cache, 0, cache_cleanup);
    RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
    int res;
    struct ast_device_state_message *device_state;

    switch (cmd) {
    case TEST_INIT:
        info->name = "device_state_aggregation_test";
        info->category = "/main/devicestate/";
        info->summary = "Tests message routing and aggregation through the Stasis device state system.";
        info->description =
            "Verifies that the device state system passes "
            "messages appropriately, that the aggregator is "
            "working properly, that the aggregate results match "
            "the expected combined devstate, and that the cached "
            "aggregate devstate is correct.";
        return AST_TEST_NOT_RUN;
    case TEST_EXECUTE:
        break;
    }

    foreign_eid = ast_malloc(sizeof(*foreign_eid));
    ast_test_validate(test, NULL != foreign_eid);
    memset(foreign_eid, 0xFF, sizeof(*foreign_eid));

    consumer = consumer_create();
    ast_test_validate(test, NULL != consumer);

    device_msg_router = stasis_message_router_create(ast_device_state_topic_cached());
    ast_test_validate(test, NULL != device_msg_router);

    ao2_ref(consumer, +1);
    res = stasis_message_router_add(device_msg_router, stasis_cache_update_type(), consumer_exec, consumer);
    ast_test_validate(test, !res);

    res = stasis_message_router_add(device_msg_router, stasis_subscription_change_type(), consumer_finalize, consumer);
    ast_test_validate(test, !res);

    /* push local state */
    ast_publish_device_state(UNIT_TEST_DEVICE_IDENTIFIER, AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE);

    /* Check cache aggregate state immediately */
    ao2_cleanup(msg);
    msg = stasis_cache_get_by_eid(ast_device_state_cache(), ast_device_state_message_type(), UNIT_TEST_DEVICE_IDENTIFIER, NULL);
    device_state = stasis_message_data(msg);
    ast_test_validate(test, AST_DEVICE_NOT_INUSE == device_state->state);

    consumer_wait_for(consumer);
    ast_test_validate(test, AST_DEVICE_NOT_INUSE == consumer->state);
    ast_test_validate(test, AST_DEVICE_NOT_INUSE == consumer->aggregate_state);
    ast_test_validate(test, 2 == consumer->event_count);
    consumer_reset(consumer);

    /* push remote state */
    /* this will not produce a new aggregate state message since the aggregate state does not change */
    consumer->sig_on_non_aggregate_state = 1;
    ast_publish_device_state_full(UNIT_TEST_DEVICE_IDENTIFIER, AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, foreign_eid);

    /* Check cache aggregate state immediately */
    ao2_cleanup(msg);
    msg = stasis_cache_get_by_eid(ast_device_state_cache(), ast_device_state_message_type(), UNIT_TEST_DEVICE_IDENTIFIER, NULL);
    device_state = stasis_message_data(msg);
    ast_test_validate(test, AST_DEVICE_NOT_INUSE == device_state->state);

    /* Check for expected events. */
    consumer_wait_for(consumer);
    ast_test_validate(test, AST_DEVICE_NOT_INUSE == consumer->state);
    ast_test_validate(test, AST_DEVICE_TOTAL == consumer->aggregate_state);
    ast_test_validate(test, 1 == consumer->event_count);
    consumer_reset(consumer);

    /* push remote state different from local state */
    ast_publish_device_state_full(UNIT_TEST_DEVICE_IDENTIFIER, AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE, foreign_eid);

    /* Check cache aggregate state immediately */
    ao2_cleanup(msg);
    msg = stasis_cache_get_by_eid(ast_device_state_cache(), ast_device_state_message_type(), UNIT_TEST_DEVICE_IDENTIFIER, NULL);
    device_state = stasis_message_data(msg);
    ast_test_validate(test, AST_DEVICE_INUSE == device_state->state);

    /* Check for expected events. */
    consumer_wait_for(consumer);
    ast_test_validate(test, AST_DEVICE_INUSE == consumer->state);
    ast_test_validate(test, AST_DEVICE_INUSE == consumer->aggregate_state);
    ast_test_validate(test, 2 == consumer->event_count);
    consumer_reset(consumer);

    /* push local state that will cause aggregated state different from local non-aggregate state */
    ast_publish_device_state(UNIT_TEST_DEVICE_IDENTIFIER, AST_DEVICE_RINGING, AST_DEVSTATE_CACHABLE);

    /* Check cache aggregate state immediately */
    ao2_cleanup(msg);
    msg = stasis_cache_get_by_eid(ast_device_state_cache(), ast_device_state_message_type(), UNIT_TEST_DEVICE_IDENTIFIER, NULL);
    device_state = stasis_message_data(msg);
    ast_test_validate(test, AST_DEVICE_RINGINUSE == device_state->state);

    /* Check for expected events. */
    consumer_wait_for(consumer);
    ast_test_validate(test, AST_DEVICE_RINGING == consumer->state);
    ast_test_validate(test, AST_DEVICE_RINGINUSE == consumer->aggregate_state);
    ast_test_validate(test, 2 == consumer->event_count);
    consumer_reset(consumer);

    return AST_TEST_PASS;
}

static int unload_module(void)
{
    AST_TEST_UNREGISTER(device2extenstate_test);
    AST_TEST_UNREGISTER(device_state_aggregation_test);
    return 0;
}
Beispiel #28
0
void ast_channel_publish_dial_forward(struct ast_channel *caller, struct ast_channel *peer,
	struct ast_channel *forwarded, const char *dialstring, const char *dialstatus,
	const char *forward)
{
	RAII_VAR(struct ast_multi_channel_blob *, payload, NULL, ao2_cleanup);
	RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
	RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
	RAII_VAR(struct ast_channel_snapshot *, caller_snapshot, NULL, ao2_cleanup);
	RAII_VAR(struct ast_channel_snapshot *, peer_snapshot, NULL, ao2_cleanup);
	RAII_VAR(struct ast_channel_snapshot *, forwarded_snapshot, NULL, ao2_cleanup);

	ast_assert(peer != NULL);
	blob = ast_json_pack("{s: s, s: s, s: s}",
			     "dialstatus", S_OR(dialstatus, ""),
			     "forward", S_OR(forward, ""),
			     "dialstring", S_OR(dialstring, ""));
	if (!blob) {
		return;
	}
	payload = ast_multi_channel_blob_create(blob);
	if (!payload) {
		return;
	}

	if (caller) {
		ast_channel_lock(caller);
		caller_snapshot = ast_channel_snapshot_create(caller);
		ast_channel_unlock(caller);
		if (!caller_snapshot) {
			return;
		}
		ast_multi_channel_blob_add_channel(payload, "caller", caller_snapshot);
	}

	ast_channel_lock(peer);
	peer_snapshot = ast_channel_snapshot_create(peer);
	ast_channel_unlock(peer);
	if (!peer_snapshot) {
		return;
	}
	ast_multi_channel_blob_add_channel(payload, "peer", peer_snapshot);

	if (forwarded) {
		ast_channel_lock(forwarded);
		forwarded_snapshot = ast_channel_snapshot_create(forwarded);
		ast_channel_unlock(forwarded);
		if (!forwarded_snapshot) {
			return;
		}
		ast_multi_channel_blob_add_channel(payload, "forwarded", forwarded_snapshot);
	}

	msg = stasis_message_create(ast_channel_dial_type(), payload);
	if (!msg) {
		return;
	}

	if (forwarded) {
		struct stasis_subscription *subscription = stasis_subscribe(ast_channel_topic(peer), dummy_event_cb, NULL);

		stasis_publish(ast_channel_topic(peer), msg);
		stasis_unsubscribe_and_join(subscription);
	} else {
		publish_message_for_channel_topics(msg, caller);
	}
}
Beispiel #29
0
/*! \brief Start monitoring a channel
 * \param chan ast_channel struct to record
 * \param format_spec file format to use for recording
 * \param fname_base filename base to record to
 * \param need_lock whether to lock the channel mutex
 * \param stream_action whether to record the input and/or output streams.  X_REC_IN | X_REC_OUT is most often used
 * Creates the file to record, if no format is specified it assumes WAV
 * It also sets channel variable __MONITORED=yes
 * \retval 0 on success
 * \retval -1 on failure
 */
int AST_OPTIONAL_API_NAME(ast_monitor_start)(struct ast_channel *chan, const char *format_spec,
					     const char *fname_base, int need_lock, int stream_action,
					     const char *beep_id)
{
	int res = 0;
	RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);

	LOCK_IF_NEEDED(chan, need_lock);

	if (!(ast_channel_monitor(chan))) {
		struct ast_channel_monitor *monitor;
		char *channel_name, *p;

		/* Create monitoring directory if needed */
		ast_mkdir(ast_config_AST_MONITOR_DIR, 0777);

		if (!(monitor = ast_calloc(1, sizeof(*monitor)))) {
			UNLOCK_IF_NEEDED(chan, need_lock);
			return -1;
		}

		if (!ast_strlen_zero(beep_id)) {
			ast_copy_string(monitor->beep_id, beep_id, sizeof(monitor->beep_id));
		}

		/* Determine file names */
		if (!ast_strlen_zero(fname_base)) {
			int directory = strchr(fname_base, '/') ? 1 : 0;
			const char *absolute = *fname_base == '/' ? "" : ast_config_AST_MONITOR_DIR;
			const char *absolute_suffix = *fname_base == '/' ? "" : "/";

			snprintf(monitor->read_filename, FILENAME_MAX, "%s%s%s-in",
						absolute, absolute_suffix, fname_base);
			snprintf(monitor->write_filename, FILENAME_MAX, "%s%s%s-out",
						absolute, absolute_suffix, fname_base);
			snprintf(monitor->filename_base, FILENAME_MAX, "%s%s%s",
					 	absolute, absolute_suffix, fname_base);

			/* try creating the directory just in case it doesn't exist */
			if (directory) {
				char *name = ast_strdupa(monitor->filename_base);
				ast_mkdir(dirname(name), 0777);
			}
		} else {
			ast_mutex_lock(&monitorlock);
			snprintf(monitor->read_filename, FILENAME_MAX, "%s/audio-in-%lu",
						ast_config_AST_MONITOR_DIR, seq);
			snprintf(monitor->write_filename, FILENAME_MAX, "%s/audio-out-%lu",
						ast_config_AST_MONITOR_DIR, seq);
			seq++;
			ast_mutex_unlock(&monitorlock);

			/* Replace all '/' chars from the channel name with '-' chars. */
			channel_name = ast_strdupa(ast_channel_name(chan));
			for (p = channel_name; (p = strchr(p, '/')); ) {
				*p = '-';
			}

			snprintf(monitor->filename_base, FILENAME_MAX, "%s/%d-%s",
					 ast_config_AST_MONITOR_DIR, (int)time(NULL), channel_name);
			monitor->filename_changed = 1;
		}

		monitor->stop = ast_monitor_stop;

		/* Determine file format */
		if (!ast_strlen_zero(format_spec)) {
			monitor->format = ast_strdup(format_spec);
		} else {
			monitor->format = ast_strdup("wav");
		}
		
		/* open files */
		if (stream_action & X_REC_IN) {
			if (ast_fileexists(monitor->read_filename, NULL, NULL) > 0)
				ast_filedelete(monitor->read_filename, NULL);
			if (!(monitor->read_stream = ast_writefile(monitor->read_filename,
							monitor->format, NULL,
							O_CREAT|O_TRUNC|O_WRONLY, 0, AST_FILE_MODE))) {
				ast_log(LOG_WARNING, "Could not create file %s\n",
							monitor->read_filename);
				ast_free(monitor);
				UNLOCK_IF_NEEDED(chan, need_lock);
				return -1;
			}
		} else
			monitor->read_stream = NULL;

		if (stream_action & X_REC_OUT) {
			if (ast_fileexists(monitor->write_filename, NULL, NULL) > 0) {
				ast_filedelete(monitor->write_filename, NULL);
			}
			if (!(monitor->write_stream = ast_writefile(monitor->write_filename,
							monitor->format, NULL,
							O_CREAT|O_TRUNC|O_WRONLY, 0, AST_FILE_MODE))) {
				ast_log(LOG_WARNING, "Could not create file %s\n",
							monitor->write_filename);
				if (monitor->read_stream) {
					ast_closestream(monitor->read_stream);
				}
				ast_free(monitor);
				UNLOCK_IF_NEEDED(chan, need_lock);
				return -1;
			}
		} else
			monitor->write_stream = NULL;

		ast_channel_insmpl_set(chan, 0);
		ast_channel_outsmpl_set(chan, 0);
		ast_channel_monitor_set(chan, monitor);
		ast_monitor_set_state(chan, AST_MONITOR_RUNNING);
		/* so we know this call has been monitored in case we need to bill for it or something */
		pbx_builtin_setvar_helper(chan, "__MONITORED","true");

		message = ast_channel_blob_create_from_cache(ast_channel_uniqueid(chan),
				ast_channel_monitor_start_type(),
				NULL);
		if (message) {
			stasis_publish(ast_channel_topic(chan), message);
		}
	} else {
		ast_debug(1,"Cannot start monitoring %s, already monitored\n", ast_channel_name(chan));
		res = -1;
	}

	UNLOCK_IF_NEEDED(chan, need_lock);

	return res;
}
Beispiel #30
0
/*! 
 * \brief Stop monitoring channel 
 * \param chan 
 * \param need_lock
 * Stop the recording, close any open streams, mix in/out channels if required
 * \return Always 0
*/
int AST_OPTIONAL_API_NAME(ast_monitor_stop)(struct ast_channel *chan, int need_lock)
{
	int delfiles = 0;
	RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);

	LOCK_IF_NEEDED(chan, need_lock);

	if (ast_channel_monitor(chan)) {
		char filename[ FILENAME_MAX ];

		if (ast_channel_monitor(chan)->read_stream) {
			ast_closestream(ast_channel_monitor(chan)->read_stream);
		}
		if (ast_channel_monitor(chan)->write_stream) {
			ast_closestream(ast_channel_monitor(chan)->write_stream);
		}

		if (ast_channel_monitor(chan)->filename_changed && !ast_strlen_zero(ast_channel_monitor(chan)->filename_base)) {
			if (ast_fileexists(ast_channel_monitor(chan)->read_filename,NULL,NULL) > 0) {
				snprintf(filename, FILENAME_MAX, "%s-in", ast_channel_monitor(chan)->filename_base);
				if (ast_fileexists(filename, NULL, NULL) > 0) {
					ast_filedelete(filename, NULL);
				}
				ast_filerename(ast_channel_monitor(chan)->read_filename, filename, ast_channel_monitor(chan)->format);
			} else {
				ast_log(LOG_WARNING, "File %s not found\n", ast_channel_monitor(chan)->read_filename);
			}

			if (ast_fileexists(ast_channel_monitor(chan)->write_filename,NULL,NULL) > 0) {
				snprintf(filename, FILENAME_MAX, "%s-out", ast_channel_monitor(chan)->filename_base);
				if (ast_fileexists(filename, NULL, NULL) > 0) {
					ast_filedelete(filename, NULL);
				}
				ast_filerename(ast_channel_monitor(chan)->write_filename, filename, ast_channel_monitor(chan)->format);
			} else {
				ast_log(LOG_WARNING, "File %s not found\n", ast_channel_monitor(chan)->write_filename);
			}
		}

		if (ast_channel_monitor(chan)->joinfiles && !ast_strlen_zero(ast_channel_monitor(chan)->filename_base)) {
			char tmp[1024];
			char tmp2[1024];
			const char *format = !strcasecmp(ast_channel_monitor(chan)->format,"wav49") ? "WAV" : ast_channel_monitor(chan)->format;
			char *fname_base = ast_channel_monitor(chan)->filename_base;
			const char *execute, *execute_args;
			/* at this point, fname_base really is the full path */

			/* Set the execute application */
			execute = pbx_builtin_getvar_helper(chan, "MONITOR_EXEC");
			if (ast_strlen_zero(execute)) {
#ifdef HAVE_SOXMIX
				execute = "nice -n 19 soxmix";
#else
				execute = "nice -n 19 sox -m";
#endif
				format = get_soxmix_format(format);
				delfiles = 1;
			} 
			execute_args = pbx_builtin_getvar_helper(chan, "MONITOR_EXEC_ARGS");
			if (ast_strlen_zero(execute_args)) {
				execute_args = "";
			}
			
			snprintf(tmp, sizeof(tmp), "%s \"%s-in.%s\" \"%s-out.%s\" \"%s.%s\" %s &",
				execute, fname_base, format, fname_base, format, fname_base, format,execute_args);
			if (delfiles) {
				snprintf(tmp2,sizeof(tmp2), "( %s& rm -f \"%s-\"* ) &",tmp, fname_base); /* remove legs when done mixing */
				ast_copy_string(tmp, tmp2, sizeof(tmp));
			}
			ast_debug(1,"monitor executing %s\n",tmp);
			if (ast_safe_system(tmp) == -1)
				ast_log(LOG_WARNING, "Execute of %s failed.\n",tmp);
		}

		if (!ast_strlen_zero(ast_channel_monitor(chan)->beep_id)) {
			ast_beep_stop(chan, ast_channel_monitor(chan)->beep_id);
		}

		ast_free(ast_channel_monitor(chan)->format);
		ast_free(ast_channel_monitor(chan));
		ast_channel_monitor_set(chan, NULL);

		message = ast_channel_blob_create_from_cache(ast_channel_uniqueid(chan),
				ast_channel_monitor_stop_type(),
				NULL);
		if (message) {
			stasis_publish(ast_channel_topic(chan), message);
		}
		pbx_builtin_setvar_helper(chan, "MONITORED", NULL);
	}
	pbx_builtin_setvar_helper(chan, "AUTO_MONITOR", NULL);

	UNLOCK_IF_NEEDED(chan, need_lock);

	return 0;
}