int ast_format_cache_set(struct ast_format *format) { SCOPED_AO2WRLOCK(lock, formats); struct ast_format *old_format; ast_assert(format != NULL); if (ast_strlen_zero(ast_format_get_name(format))) { return -1; } old_format = ao2_find(formats, ast_format_get_name(format), OBJ_SEARCH_KEY | OBJ_NOLOCK); if (old_format) { ao2_unlink_flags(formats, old_format, OBJ_NOLOCK); } ao2_link_flags(formats, format, OBJ_NOLOCK); set_cached_format(ast_format_get_name(format), format); ast_verb(2, "%s cached format with name '%s'\n", old_format ? "Updated" : "Created", ast_format_get_name(format)); ao2_cleanup(old_format); return 0; }
/*! * \brief Callback handler for Stasis application messages. */ static void app_handler(void *data, const char *app_name, struct ast_json *message) { struct event_session *session = data; int res; const char *msg_type = S_OR( ast_json_string_get(ast_json_object_get(message, "type")), ""); const char *msg_application = S_OR( ast_json_string_get(ast_json_object_get(message, "application")), ""); if (!session) { return; } /* Determine if we've been replaced */ if (strcmp(msg_type, "ApplicationReplaced") == 0 && strcmp(msg_application, app_name) == 0) { ao2_find(session->websocket_apps, msg_application, OBJ_UNLINK | OBJ_NODATA); } res = ast_json_object_set(message, "application", ast_json_string_create(app_name)); if(res != 0) { return; } ao2_lock(session); if (session->ws_session) { ast_ari_websocket_session_write(session->ws_session, message); } ao2_unlock(session); }
void ast_sip_dialog_set_endpoint(pjsip_dialog *dlg, struct ast_sip_endpoint *endpoint) { struct distributor_dialog_data *dist; ao2_wrlock(dialog_associations); dist = ao2_find(dialog_associations, dlg, OBJ_SEARCH_KEY | OBJ_NOLOCK); if (!dist) { if (endpoint) { dist = ao2_alloc(sizeof(*dist), NULL); if (dist) { dist->dlg = dlg; dist->endpoint = endpoint; ao2_link_flags(dialog_associations, dist, OBJ_NOLOCK); ao2_ref(dist, -1); } } } else { ao2_lock(dist); dist->endpoint = endpoint; if (!dist->serializer && !dist->endpoint) { ao2_unlink_flags(dialog_associations, dist, OBJ_NOLOCK); } ao2_unlock(dist); ao2_ref(dist, -1); } ao2_unlock(dialog_associations); }
int __ast_format_interface_register(const char *codec, const struct ast_format_interface *interface, struct ast_module *mod) { SCOPED_AO2WRLOCK(lock, interfaces); struct format_interface *format_interface; if (!interface->format_clone || !interface->format_destroy) { ast_log(LOG_ERROR, "Format interface for codec '%s' does not implement required callbacks\n", codec); return -1; } format_interface = ao2_find(interfaces, codec, OBJ_SEARCH_KEY | OBJ_NOLOCK); if (format_interface) { ast_log(LOG_ERROR, "A format interface is already present for codec '%s'\n", codec); ao2_ref(format_interface, -1); return -1; } format_interface = ao2_alloc_options(sizeof(*format_interface) + strlen(codec) + 1, NULL, AO2_ALLOC_OPT_LOCK_NOLOCK); if (!format_interface) { return -1; } format_interface->interface = interface; strcpy(format_interface->codec, codec); /* Safe */ /* Once registered a format interface cannot be unregistered. */ ast_module_shutdown_ref(mod); ao2_link_flags(interfaces, format_interface, OBJ_NOLOCK); ao2_ref(format_interface, -1); ast_verb(2, "Registered format interface for codec '%s'\n", codec); return 0; }
static int manager_optimize_away(struct mansession *s, const struct message *m) { const char *channel; struct local_pvt *p; struct local_pvt *found; struct ast_channel *chan; channel = astman_get_header(m, "Channel"); if (ast_strlen_zero(channel)) { astman_send_error(s, m, "'Channel' not specified."); return 0; } chan = ast_channel_get_by_name(channel); if (!chan) { astman_send_error(s, m, "Channel does not exist."); return 0; } p = ast_channel_tech_pvt(chan); ast_channel_unref(chan); found = p ? ao2_find(locals, p, 0) : NULL; if (found) { ao2_lock(found); ast_clear_flag(&found->base, AST_UNREAL_NO_OPTIMIZATION); ao2_unlock(found); ao2_ref(found, -1); astman_send_ack(s, m, "Queued channel to be optimized away"); } else { astman_send_error(s, m, "Unable to find channel"); } return 0; }
/*! Delete entries from the hash */ static void *hash_test_shrink(void *d) { const struct hash_test *data = d; int i; for (i = 1; i < data->preload; ++i) { char *obj = ht_new(-i); char *from_ao2; if (obj == NULL) { return "Allocation failed"; } from_ao2 = ao2_find(data->to_be_thrashed, obj, OBJ_UNLINK | OBJ_POINTER); ao2_ref(obj, -1); if (from_ao2) { ao2_ref(from_ao2, -1); } else { return "Could not find object to delete"; } if (is_timed_out(data)) { return "Shrink timed out"; } } return NULL; }
void ast_ari_websocket_events_event_websocket_established( struct ast_ari_websocket_session *ws_session, struct ast_variable *headers, struct ast_ari_events_event_websocket_args *args) { RAII_VAR(struct event_session *, session, NULL, event_session_cleanup); struct ast_json *msg; const char *session_id; ast_debug(3, "/events WebSocket established\n"); ast_assert(ws_session != NULL); session_id = ast_ari_websocket_session_id(ws_session); /* Find the event_session and update its websocket */ session = ao2_find(event_session_registry, session_id, OBJ_SEARCH_KEY); if (session) { ao2_unlink(event_session_registry, session); event_session_update_websocket(session, ws_session); } else { ast_log(LOG_WARNING, "Failed to locate an event session for the provided websocket session\n"); } /* We don't process any input, but we'll consume it waiting for EOF */ while ((msg = ast_ari_websocket_session_read(ws_session))) { ast_json_unref(msg); } }
int app_subscribe_bridge(struct stasis_app *app, struct ast_bridge *bridge) { if (!app || !bridge) { return -1; } else { RAII_VAR(struct app_forwards *, forwards, NULL, ao2_cleanup); SCOPED_AO2LOCK(lock, app->forwards); forwards = ao2_find(app->forwards, bridge->uniqueid, OBJ_SEARCH_KEY | OBJ_NOLOCK); if (!forwards) { /* Forwards not found, create one */ forwards = forwards_create_bridge(app, bridge); if (!forwards) { return -1; } ao2_link_flags(app->forwards, forwards, OBJ_NOLOCK); } ++forwards->interested; ast_debug(3, "Bridge '%s' is %d interested in %s\n", bridge->uniqueid, forwards->interested, app->name); return 0; } }
int app_subscribe_endpoint(struct stasis_app *app, struct ast_endpoint *endpoint) { if (!app || !endpoint) { return -1; } else { RAII_VAR(struct app_forwards *, forwards, NULL, ao2_cleanup); SCOPED_AO2LOCK(lock, app->forwards); forwards = ao2_find(app->forwards, ast_endpoint_get_id(endpoint), OBJ_SEARCH_KEY | OBJ_NOLOCK); if (!forwards) { /* Forwards not found, create one */ forwards = forwards_create_endpoint(app, endpoint); if (!forwards) { return -1; } ao2_link_flags(app->forwards, forwards, OBJ_NOLOCK); /* Subscribe for messages */ messaging_app_subscribe_endpoint(app->name, endpoint, &message_received_handler, app); } ++forwards->interested; ast_debug(3, "Endpoint '%s' is %d interested in %s\n", ast_endpoint_get_id(endpoint), forwards->interested, app->name); return 0; } }
/*! \brief Frame hook callback for writing */ static struct ast_frame *t38_framehook_write(struct ast_sip_session *session, struct ast_frame *f) { if (f->frametype == AST_FRAME_CONTROL && f->subclass.integer == AST_CONTROL_T38_PARAMETERS && session->endpoint->media.t38.enabled) { struct t38_parameters_task_data *data = t38_parameters_task_data_alloc(session, f); if (!data) { return f; } if (ast_sip_push_task(session->serializer, t38_interpret_parameters, data)) { ao2_ref(data, -1); } f = &ast_null_frame; } else if (f->frametype == AST_FRAME_MODEM) { RAII_VAR(struct ast_sip_session_media *, session_media, NULL, ao2_cleanup); if ((session_media = ao2_find(session->media, "image", OBJ_KEY)) && session_media->udptl) { ast_udptl_write(session_media->udptl, f); } } return f; }
int app_subscribe_channel(struct stasis_app *app, struct ast_channel *chan) { int res; if (!app || !chan) { return -1; } else { RAII_VAR(struct app_forwards *, forwards, NULL, ao2_cleanup); SCOPED_AO2LOCK(lock, app->forwards); forwards = ao2_find(app->forwards, ast_channel_uniqueid(chan), OBJ_SEARCH_KEY | OBJ_NOLOCK); if (!forwards) { /* Forwards not found, create one */ forwards = forwards_create_channel(app, chan); if (!forwards) { return -1; } res = ao2_link_flags(app->forwards, forwards, OBJ_NOLOCK); if (!res) { return -1; } } ++forwards->interested; ast_debug(3, "Channel '%s' is %d interested in %s\n", ast_channel_uniqueid(chan), forwards->interested, app->name); return 0; } }
struct ast_named_lock *__ast_named_lock_get(const char *filename, int lineno, const char *func, enum ast_named_lock_type lock_type, const char *keyspace, const char *key) { struct ast_named_lock *lock = NULL; int concat_key_buff_len = strlen(keyspace) + strlen(key) + 2; char *concat_key = ast_alloca(concat_key_buff_len); sprintf(concat_key, "%s-%s", keyspace, key); /* Safe */ ao2_lock(named_locks); lock = ao2_find(named_locks, concat_key, OBJ_SEARCH_KEY | OBJ_NOLOCK); if (lock) { ao2_unlock(named_locks); ast_assert((ao2_options_get(lock) & AO2_ALLOC_OPT_LOCK_MASK) == lock_type); return lock; } lock = ao2_alloc_options(sizeof(*lock) + concat_key_buff_len, NULL, lock_type); if (lock) { strcpy(lock->key, concat_key); /* Safe */ ao2_link_flags(named_locks, lock, OBJ_NOLOCK); } ao2_unlock(named_locks); return lock; }
static int timerfd_timer_set_rate(int handle, unsigned int rate) { struct timerfd_timer *our_timer, find_helper = { .handle = handle, }; int res = 0; if (handle == -1) { ast_log(LOG_ERROR, "Attempting to set rate on timerfd handle -1"); return -1; } if (!(our_timer = ao2_find(timerfd_timers, &find_helper, OBJ_POINTER))) { ast_log(LOG_ERROR, "Couldn't find timer with handle %d\n", handle); return -1; } ao2_lock(our_timer); our_timer->saved_timer.it_value.tv_sec = 0; our_timer->saved_timer.it_value.tv_nsec = rate ? (long) (1000000000 / rate) : 0L; our_timer->saved_timer.it_interval.tv_sec = our_timer->saved_timer.it_value.tv_sec; our_timer->saved_timer.it_interval.tv_nsec = our_timer->saved_timer.it_value.tv_nsec; if (!our_timer->is_continuous) { res = timerfd_settime(handle, 0, &our_timer->saved_timer, NULL); } ao2_unlock(our_timer); ao2_ref(our_timer, -1); return res; }
static int t38_incoming_bye_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata) { struct ast_datastore *datastore; struct ast_sip_session_media *session_media; if (!session->channel) { return 0; } datastore = ast_sip_session_get_datastore(session, "t38"); if (!datastore) { return 0; } session_media = ao2_find(session->media, "image", OBJ_KEY); if (!session_media) { ao2_ref(datastore, -1); return 0; } t38_change_state(session, session_media, datastore->data, T38_REJECTED); ao2_ref(datastore, -1); ao2_ref(session_media, -1); return 0; }
int ast_local_setup_masquerade(struct ast_channel *ast, struct ast_channel *masq) { struct local_pvt *p; struct local_pvt *found; int res = -1; /* Sanity checks. */ if (!ast || !masq) { return -1; } ast_channel_lock(ast); p = ast_channel_tech_pvt(ast); ast_channel_unlock(ast); found = p ? ao2_find(locals, p, 0) : NULL; if (found) { ao2_lock(found); if (found->type == LOCAL_CALL_ACTION_DIALPLAN && found->base.owner && found->base.chan && !ast_test_flag(&found->base, AST_UNREAL_CARETAKER_THREAD)) { ast_channel_ref(masq); found->type = LOCAL_CALL_ACTION_MASQUERADE; found->action.masq = masq; res = 0; } ao2_unlock(found); ao2_ref(found, -1); } return res; }
struct ast_channel *ast_local_get_peer(struct ast_channel *ast) { struct local_pvt *p = ast_channel_tech_pvt(ast); struct local_pvt *found; struct ast_channel *peer; if (!p) { return NULL; } found = p ? ao2_find(locals, p, 0) : NULL; if (!found) { /* ast is either not a local channel or it has alredy been hungup */ return NULL; } ao2_lock(found); if (ast == p->base.owner) { peer = p->base.chan; } else if (ast == p->base.chan) { peer = p->base.owner; } else { peer = NULL; } if (peer) { ast_channel_ref(peer); } ao2_unlock(found); ao2_ref(found, -1); return peer; }
/*! \brief Function called when a contact is added */ static void mwi_contact_added(const void *object) { const struct ast_sip_contact *contact = object; struct ao2_iterator *mwi_subs; struct mwi_subscription *mwi_sub; const char *endpoint_id = ast_sorcery_object_get_id(contact->endpoint); if (ast_strlen_zero(contact->endpoint->subscription.mwi.mailboxes)) { return; } ao2_lock(unsolicited_mwi); mwi_subs = ao2_find(unsolicited_mwi, endpoint_id, OBJ_SEARCH_KEY | OBJ_MULTIPLE | OBJ_NOLOCK | OBJ_UNLINK); if (mwi_subs) { for (; (mwi_sub = ao2_iterator_next(mwi_subs)); ao2_cleanup(mwi_sub)) { unsubscribe(mwi_sub, NULL, 0); } ao2_iterator_destroy(mwi_subs); } create_mwi_subscriptions_for_endpoint(contact->endpoint, NULL, 0); ao2_unlock(unsolicited_mwi); mwi_contact_updated(object); }
struct stasis_topic *stasis_topic_pool_get_topic(struct stasis_topic_pool *pool, const char *topic_name) { RAII_VAR(struct topic_pool_entry *, topic_pool_entry, NULL, ao2_cleanup); SCOPED_AO2LOCK(topic_container_lock, pool->pool_container); topic_pool_entry = ao2_find(pool->pool_container, topic_name, OBJ_SEARCH_KEY | OBJ_NOLOCK); if (topic_pool_entry) { return topic_pool_entry->topic; } topic_pool_entry = topic_pool_entry_alloc(); if (!topic_pool_entry) { return NULL; } topic_pool_entry->topic = stasis_topic_create(topic_name); if (!topic_pool_entry->topic) { return NULL; } topic_pool_entry->forward = stasis_forward_all(topic_pool_entry->topic, pool->pool_topic); if (!topic_pool_entry->forward) { return NULL; } if (!ao2_link_flags(pool->pool_container, topic_pool_entry, OBJ_NOLOCK)) { return NULL; } return topic_pool_entry->topic; }
struct ast_format *__ast_format_cache_get(const char *name) { if (ast_strlen_zero(name)) { return NULL; } return ao2_find(formats, name, OBJ_SEARCH_KEY); }
/*! \brief \ref aco_type item_find function */ static void *user_find(struct ao2_container *tmp_container, const char *cat) { if (!cat) { return NULL; } return ao2_find(tmp_container, cat, OBJ_SEARCH_KEY); }
static int timerfd_timer_ack(int handle, unsigned int quantity) { uint64_t expirations; int read_result = 0; int res = 0; struct timerfd_timer *our_timer, find_helper = { .handle = handle, }; if (handle == -1) { ast_log(LOG_ERROR, "Attempting to ack timerfd handle -1"); return -1; } if (!(our_timer = ao2_find(timerfd_timers, &find_helper, OBJ_POINTER))) { ast_log(LOG_ERROR, "Couldn't find a timer with handle %d\n", handle); return -1; } ao2_lock(our_timer); do { struct itimerspec timer_status; if (timerfd_gettime(handle, &timer_status)) { ast_log(LOG_ERROR, "Call to timerfd_gettime() using handle %d error: %s\n", handle, strerror(errno)); expirations = 0; res = -1; break; } if (timer_status.it_value.tv_sec == 0 && timer_status.it_value.tv_nsec == 0) { ast_debug(1, "Avoiding read on disarmed timerfd %d\n", handle); expirations = 0; break; } read_result = read(handle, &expirations, sizeof(expirations)); if (read_result == -1) { if (errno == EINTR || errno == EAGAIN) { continue; } else { ast_log(LOG_ERROR, "Read error: %s\n", strerror(errno)); res = -1; break; } } } while (read_result != sizeof(expirations)); ao2_unlock(our_timer); ao2_ref(our_timer, -1); if (expirations != quantity) { ast_debug(2, "Expected to acknowledge %u ticks but got %llu instead\n", quantity, (unsigned long long) expirations); } return res; }
int app_subscribe_channel(struct stasis_app *app, struct ast_channel *chan) { struct app_forwards *forwards; SCOPED_AO2LOCK(lock, app->forwards); int res; if (!app) { return -1; } /* If subscribed to all, don't subscribe again */ forwards = ao2_find(app->forwards, CHANNEL_ALL, OBJ_SEARCH_KEY | OBJ_NOLOCK); if (forwards) { ao2_ref(forwards, -1); return 0; } forwards = ao2_find(app->forwards, chan ? ast_channel_uniqueid(chan) : CHANNEL_ALL, OBJ_SEARCH_KEY | OBJ_NOLOCK); if (!forwards) { /* Forwards not found, create one */ forwards = forwards_create_channel(app, chan); if (!forwards) { return -1; } res = ao2_link_flags(app->forwards, forwards, OBJ_NOLOCK); if (!res) { ao2_ref(forwards, -1); return -1; } } ++forwards->interested; ast_debug(3, "Channel '%s' is %d interested in %s\n", chan ? ast_channel_uniqueid(chan) : "ALL", forwards->interested, app->name); ao2_ref(forwards, -1); return 0; }
static int unsubscribe(struct stasis_app *app, const char *kind, const char *id, int terminate) { struct app_forwards *forwards; if (!id) { if (!strcmp(kind, "bridge")) { id = BRIDGE_ALL; } else if (!strcmp(kind, "channel")) { id = CHANNEL_ALL; } else if (!strcmp(kind, "endpoint")) { id = ENDPOINT_ALL; } else { ast_log(LOG_WARNING, "Unknown subscription kind '%s'\n", kind); return -1; } } ao2_lock(app->forwards); forwards = ao2_find(app->forwards, id, OBJ_SEARCH_KEY | OBJ_NOLOCK); if (!forwards) { ao2_unlock(app->forwards); ast_debug(3, "App '%s' not subscribed to %s '%s'\n", app->name, kind, id); return -1; } forwards->interested--; ast_debug(3, "%s '%s': is %d interested in %s\n", kind, id, forwards->interested, app->name); if (forwards->interested == 0 || terminate) { /* No one is interested any more; unsubscribe */ ast_debug(3, "%s '%s' unsubscribed from %s\n", kind, id, app->name); forwards_unsubscribe(forwards); ao2_find(app->forwards, forwards, OBJ_POINTER | OBJ_NOLOCK | OBJ_UNLINK | OBJ_NODATA); if (!strcmp(kind, "endpoint")) { messaging_app_unsubscribe_endpoint(app->name, id); } } ao2_unlock(app->forwards); ao2_ref(forwards, -1); return 0; }
static pj_bool_t authenticate(pjsip_rx_data *rdata) { RAII_VAR(struct ast_sip_endpoint *, endpoint, ast_pjsip_rdata_get_endpoint(rdata), ao2_cleanup); int is_ack = rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD; ast_assert(endpoint != NULL); if (is_ack) { return PJ_FALSE; } if (ast_sip_requires_authentication(endpoint, rdata)) { pjsip_tx_data *tdata; struct unidentified_request *unid; pjsip_endpt_create_response(ast_sip_get_pjsip_endpoint(), rdata, 401, NULL, &tdata); switch (ast_sip_check_authentication(endpoint, rdata, tdata)) { case AST_SIP_AUTHENTICATION_CHALLENGE: /* Send the 401 we created for them */ ast_sip_report_auth_challenge_sent(endpoint, rdata, tdata); if (pjsip_endpt_send_response2(ast_sip_get_pjsip_endpoint(), rdata, tdata, NULL, NULL) != PJ_SUCCESS) { pjsip_tx_data_dec_ref(tdata); } return PJ_TRUE; case AST_SIP_AUTHENTICATION_SUCCESS: /* See note in endpoint_lookup about not holding an unnecessary write lock */ unid = ao2_find(unidentified_requests, rdata->pkt_info.src_name, OBJ_SEARCH_KEY); if (unid) { ao2_unlink(unidentified_requests, unid); ao2_ref(unid, -1); } ast_sip_report_auth_success(endpoint, rdata); break; case AST_SIP_AUTHENTICATION_FAILED: log_failed_request(rdata, "Failed to authenticate", 0, 0); ast_sip_report_auth_failed_challenge_response(endpoint, rdata); if (pjsip_endpt_send_response2(ast_sip_get_pjsip_endpoint(), rdata, tdata, NULL, NULL) != PJ_SUCCESS) { pjsip_tx_data_dec_ref(tdata); } return PJ_TRUE; case AST_SIP_AUTHENTICATION_ERROR: log_failed_request(rdata, "Error to authenticate", 0, 0); ast_sip_report_auth_failed_challenge_response(endpoint, rdata); pjsip_tx_data_dec_ref(tdata); pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL); return PJ_TRUE; } pjsip_tx_data_dec_ref(tdata); } else if (endpoint == artificial_endpoint) { /* Uh. Oh. The artificial endpoint couldn't challenge so block the request. */ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL); return PJ_TRUE; } return PJ_FALSE; }
int app_is_subscribed_channel_id(struct stasis_app *app, const char *channel_id) { RAII_VAR(struct app_forwards *, forwards, NULL, ao2_cleanup); if (ast_strlen_zero(channel_id)) { channel_id = CHANNEL_ALL; } forwards = ao2_find(app->forwards, channel_id, OBJ_SEARCH_KEY); return forwards != NULL; }
struct ast_codec *ast_codec_get(const char *name, enum ast_media_type type, unsigned int sample_rate) { struct ast_codec codec = { .name = name, .type = type, .sample_rate = sample_rate, }; return ao2_find(codecs, &codec, OBJ_SEARCH_OBJECT); }
int app_is_subscribed_endpoint_id(struct stasis_app *app, const char *endpoint_id) { RAII_VAR(struct app_forwards *, forwards, NULL, ao2_cleanup); if (ast_strlen_zero(endpoint_id)) { endpoint_id = ENDPOINT_ALL; } forwards = ao2_find(app->forwards, endpoint_id, OBJ_SEARCH_KEY); return forwards != NULL; }
/*! \brief Helper function for determining if the application is subscribed to a given entity */ static int bridge_app_subscribed(struct stasis_app *app, const char *uniqueid) { struct app_forwards *forwards = NULL; forwards = ao2_find(app->forwards, uniqueid, OBJ_SEARCH_KEY); if (!forwards) { return 0; } ao2_ref(forwards, -1); return 1; }
struct stasis_app_recording *stasis_app_recording_find_by_name(const char *name) { RAII_VAR(struct stasis_app_recording *, recording, NULL, ao2_cleanup); recording = ao2_find(recordings, name, OBJ_KEY); if (recording == NULL) { return NULL; } ao2_ref(recording, +1); return recording; }
int app_subscribe_endpoint(struct stasis_app *app, struct ast_endpoint *endpoint) { struct app_forwards *forwards; SCOPED_AO2LOCK(lock, app->forwards); if (!app) { return -1; } /* If subscribed to all, don't subscribe again */ forwards = ao2_find(app->forwards, ENDPOINT_ALL, OBJ_SEARCH_KEY | OBJ_NOLOCK); if (forwards) { ao2_ref(forwards, -1); return 0; } forwards = ao2_find(app->forwards, endpoint ? ast_endpoint_get_id(endpoint) : ENDPOINT_ALL, OBJ_SEARCH_KEY | OBJ_NOLOCK); if (!forwards) { /* Forwards not found, create one */ forwards = forwards_create_endpoint(app, endpoint); if (!forwards) { return -1; } ao2_link_flags(app->forwards, forwards, OBJ_NOLOCK); /* Subscribe for messages */ messaging_app_subscribe_endpoint(app->name, endpoint, &message_received_handler, app); } ++forwards->interested; ast_debug(3, "Endpoint '%s' is %d interested in %s\n", endpoint ? ast_endpoint_get_id(endpoint) : "ALL", forwards->interested, app->name); ao2_ref(forwards, -1); return 0; }