static struct stasis_message *create_channel_blob_message(struct ast_channel_snapshot *snapshot, struct stasis_message_type *type, struct ast_json *blob) { struct stasis_message *msg; struct ast_channel_blob *obj; obj = ao2_alloc(sizeof(*obj), channel_blob_dtor); if (!obj) { return NULL; } if (snapshot) { obj->snapshot = snapshot; ao2_ref(obj->snapshot, +1); } if (!blob) { blob = ast_json_null(); } obj->blob = ast_json_ref(blob); msg = stasis_message_create(type, obj); ao2_cleanup(obj); return msg; }
struct stasis_message *ast_endpoint_blob_create(struct ast_endpoint *endpoint, struct stasis_message_type *type, struct ast_json *blob) { RAII_VAR(struct ast_endpoint_blob *, obj, NULL, ao2_cleanup); RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup); if (!type) { return NULL; } if (!blob) { blob = ast_json_null(); } if (!(obj = ao2_alloc(sizeof(*obj), endpoint_blob_dtor))) { return NULL; } if (endpoint) { if (!(obj->snapshot = ast_endpoint_snapshot_create(endpoint))) { return NULL; } } obj->blob = ast_json_ref(blob); if (!(msg = stasis_message_create(type, obj))) { return NULL; } ao2_ref(msg, +1); return msg; }
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); }
/*! \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); } }
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); }
/*! \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); }
/*! \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"); }
/*! * \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); }
struct stasis_message *stasis_cache_clear_create(struct stasis_message *id_message) { RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup); msg = stasis_message_create(stasis_cache_clear_type(), id_message); if (!msg) { return NULL; } ao2_ref(msg, +1); return msg; }
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); }
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); }
/*! \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); }
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; }
static struct stasis_message *caching_guarantee_create(void) { RAII_VAR(struct caching_guarantee *, guarantee, NULL, ao2_cleanup); RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup); if (!(guarantee = ao2_alloc(sizeof(*guarantee), caching_guarantee_dtor))) { return NULL; } ast_mutex_init(&guarantee->lock); ast_cond_init(&guarantee->cond, NULL); if (!(msg = stasis_message_create(cache_guarantee_type(), guarantee))) { return NULL; } ao2_ref(msg, +1); return msg; }
/*! \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); }
static int publish_app_cdr_message(struct ast_channel *chan, struct app_cdr_message_payload *payload) { RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup); RAII_VAR(struct stasis_message_router *, router, ast_cdr_message_router(), ao2_cleanup); if (!router) { ast_log(AST_LOG_WARNING, "Failed to manipulate CDR for channel %s: no message router\n", ast_channel_name(chan)); return -1; } message = stasis_message_create(appcdr_message_type(), payload); if (!message) { ast_log(AST_LOG_WARNING, "Failed to manipulate CDR for channel %s: unable to create message\n", payload->channel_name); return -1; } stasis_message_router_publish_sync(router, message); return 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); }
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); }
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); }
/*! \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 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; }
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); } }
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); }