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); } }
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); } }
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); }
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; }
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); } }
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; }
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); } }
/*! * \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; }
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); } }
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); }
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; }
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); } }
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); } }
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); } }
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); } }
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); } }
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; }
/*! \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); }
/*! \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); }
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; }
/*! * \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); } }
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); }
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); }
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); } }
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)); }
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); }