123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404 |
- /*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 2017, Digium Inc.
- *
- * Richard Mudgett <rmudgett@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
- /*!
- * \file
- * \brief Manages the global transport event notification callbacks.
- *
- * \author Richard Mudgett <rmudgett@digium.com>
- * See Also:
- *
- * \arg \ref AstCREDITS
- */
- #include "asterisk.h"
- #include "asterisk/res_pjsip.h"
- #include "include/res_pjsip_private.h"
- #include "asterisk/linkedlists.h"
- #include "asterisk/vector.h"
- /* ------------------------------------------------------------------- */
- /*! \brief Number of buckets for monitored active transports */
- #define ACTIVE_TRANSPORTS_BUCKETS 127
- /*! Who to notify when transport shuts down. */
- struct transport_monitor_notifier {
- /*! Who to call when transport shuts down. */
- ast_transport_monitor_shutdown_cb cb;
- /*! ao2 data object to pass to callback. */
- void *data;
- };
- /*! \brief Structure for transport to be monitored */
- struct transport_monitor {
- /*! \brief The underlying PJSIP transport */
- pjsip_transport *transport;
- /*! Who is interested in when this transport shuts down. */
- AST_VECTOR(, struct transport_monitor_notifier) monitors;
- };
- /*! \brief Global container of active reliable transports */
- static AO2_GLOBAL_OBJ_STATIC(active_transports);
- /*! \brief Existing transport events callback that we need to invoke */
- static pjsip_tp_state_callback tpmgr_state_callback;
- /*! List of registered transport state callbacks. */
- static AST_RWLIST_HEAD(, ast_sip_tpmgr_state_callback) transport_state_list;
- /*! \brief Hashing function for struct transport_monitor */
- AO2_STRING_FIELD_HASH_FN(transport_monitor, transport->obj_name);
- /*! \brief Comparison function for struct transport_monitor */
- AO2_STRING_FIELD_CMP_FN(transport_monitor, transport->obj_name);
- static const char *transport_state2str(pjsip_transport_state state)
- {
- const char *name;
- switch (state) {
- case PJSIP_TP_STATE_CONNECTED:
- name = "CONNECTED";
- break;
- case PJSIP_TP_STATE_DISCONNECTED:
- name = "DISCONNECTED";
- break;
- case PJSIP_TP_STATE_SHUTDOWN:
- name = "SHUTDOWN";
- break;
- case PJSIP_TP_STATE_DESTROY:
- name = "DESTROY";
- break;
- default:
- /*
- * We have to have a default case because the enum is
- * defined by a third-party library.
- */
- ast_assert(0);
- name = "<unknown>";
- break;
- }
- return name;
- }
- static void transport_monitor_dtor(void *vdoomed)
- {
- struct transport_monitor *monitored = vdoomed;
- int idx;
- for (idx = AST_VECTOR_SIZE(&monitored->monitors); idx--;) {
- struct transport_monitor_notifier *notifier;
- notifier = AST_VECTOR_GET_ADDR(&monitored->monitors, idx);
- ao2_cleanup(notifier->data);
- }
- AST_VECTOR_FREE(&monitored->monitors);
- }
- /*!
- * \internal
- * \brief Do registered callbacks for the transport.
- * \since 13.21.0
- *
- * \param transports Active transports container
- * \param transport Which transport to do callbacks for.
- *
- * \return Nothing
- */
- static void transport_state_do_reg_callbacks(struct ao2_container *transports, pjsip_transport *transport)
- {
- struct transport_monitor *monitored;
- monitored = ao2_find(transports, transport->obj_name, OBJ_SEARCH_KEY | OBJ_UNLINK);
- if (monitored) {
- int idx;
- for (idx = AST_VECTOR_SIZE(&monitored->monitors); idx--;) {
- struct transport_monitor_notifier *notifier;
- notifier = AST_VECTOR_GET_ADDR(&monitored->monitors, idx);
- ast_debug(3, "running callback %p(%p) for transport %s\n",
- notifier->cb, notifier->data, transport->obj_name);
- notifier->cb(notifier->data);
- }
- ao2_ref(monitored, -1);
- }
- }
- /*! \brief Callback invoked when transport state changes occur */
- static void transport_state_callback(pjsip_transport *transport,
- pjsip_transport_state state, const pjsip_transport_state_info *info)
- {
- struct ao2_container *transports;
- /* We only care about monitoring reliable transports */
- if (PJSIP_TRANSPORT_IS_RELIABLE(transport)
- && (transports = ao2_global_obj_ref(active_transports))) {
- struct transport_monitor *monitored;
- ast_debug(3, "Reliable transport '%s' state:%s\n",
- transport->obj_name, transport_state2str(state));
- switch (state) {
- case PJSIP_TP_STATE_CONNECTED:
- monitored = ao2_alloc_options(sizeof(*monitored),
- transport_monitor_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK);
- if (!monitored) {
- break;
- }
- monitored->transport = transport;
- if (AST_VECTOR_INIT(&monitored->monitors, 5)) {
- ao2_ref(monitored, -1);
- break;
- }
- ao2_link(transports, monitored);
- ao2_ref(monitored, -1);
- break;
- case PJSIP_TP_STATE_DISCONNECTED:
- if (!transport->is_shutdown) {
- pjsip_transport_shutdown(transport);
- }
- transport_state_do_reg_callbacks(transports, transport);
- break;
- case PJSIP_TP_STATE_SHUTDOWN:
- /*
- * Set shutdown flag early so we can force a new transport to be
- * created if a monitor callback needs to reestablish a link.
- * PJPROJECT sets the flag after this routine returns even though
- * it has already called the transport's shutdown routine.
- */
- transport->is_shutdown = PJ_TRUE;
- transport_state_do_reg_callbacks(transports, transport);
- break;
- case PJSIP_TP_STATE_DESTROY:
- transport_state_do_reg_callbacks(transports, transport);
- break;
- default:
- /*
- * We have to have a default case because the enum is
- * defined by a third-party library.
- */
- ast_assert(0);
- break;
- }
- ao2_ref(transports, -1);
- }
- /* Loop over other transport state callbacks registered with us. */
- if (!AST_LIST_EMPTY(&transport_state_list)) {
- struct ast_sip_tpmgr_state_callback *tpmgr_notifier;
- AST_RWLIST_RDLOCK(&transport_state_list);
- AST_LIST_TRAVERSE(&transport_state_list, tpmgr_notifier, node) {
- tpmgr_notifier->cb(transport, state, info);
- }
- AST_RWLIST_UNLOCK(&transport_state_list);
- }
- /* Forward to the old state callback if present */
- if (tpmgr_state_callback) {
- tpmgr_state_callback(transport, state, info);
- }
- }
- struct callback_data {
- ast_transport_monitor_shutdown_cb cb;
- void *data;
- ast_transport_monitor_data_matcher matches;
- };
- static int transport_monitor_unregister_cb(void *obj, void *arg, int flags)
- {
- struct transport_monitor *monitored = obj;
- struct callback_data *cb_data = arg;
- int idx;
- for (idx = AST_VECTOR_SIZE(&monitored->monitors); idx--;) {
- struct transport_monitor_notifier *notifier;
- notifier = AST_VECTOR_GET_ADDR(&monitored->monitors, idx);
- if (notifier->cb == cb_data->cb && (!cb_data->data
- || cb_data->matches(cb_data->data, notifier->data))) {
- ao2_cleanup(notifier->data);
- AST_VECTOR_REMOVE_UNORDERED(&monitored->monitors, idx);
- ast_debug(3, "Unregistered monitor %p(%p) from transport %s\n",
- notifier->cb, notifier->data, monitored->transport->obj_name);
- }
- }
- return 0;
- }
- static int ptr_matcher(void *a, void *b)
- {
- return a == b;
- }
- void ast_sip_transport_monitor_unregister_all(ast_transport_monitor_shutdown_cb cb,
- void *data, ast_transport_monitor_data_matcher matches)
- {
- struct ao2_container *transports;
- struct callback_data cb_data = {
- .cb = cb,
- .data = data,
- .matches = matches ?: ptr_matcher,
- };
- ast_assert(cb != NULL);
- transports = ao2_global_obj_ref(active_transports);
- if (!transports) {
- return;
- }
- ao2_callback(transports, OBJ_MULTIPLE | OBJ_NODATA, transport_monitor_unregister_cb, &cb_data);
- ao2_ref(transports, -1);
- }
- void ast_sip_transport_monitor_unregister(pjsip_transport *transport,
- ast_transport_monitor_shutdown_cb cb, void *data, ast_transport_monitor_data_matcher matches)
- {
- struct ao2_container *transports;
- struct transport_monitor *monitored;
- ast_assert(transport != NULL && cb != NULL);
- transports = ao2_global_obj_ref(active_transports);
- if (!transports) {
- return;
- }
- ao2_lock(transports);
- monitored = ao2_find(transports, transport->obj_name, OBJ_SEARCH_KEY | OBJ_NOLOCK);
- if (monitored) {
- struct callback_data cb_data = {
- .cb = cb,
- .data = data,
- .matches = matches ?: ptr_matcher,
- };
- transport_monitor_unregister_cb(monitored, &cb_data, 0);
- ao2_ref(monitored, -1);
- }
- ao2_unlock(transports);
- ao2_ref(transports, -1);
- }
- enum ast_transport_monitor_reg ast_sip_transport_monitor_register(pjsip_transport *transport,
- ast_transport_monitor_shutdown_cb cb, void *ao2_data)
- {
- struct ao2_container *transports;
- struct transport_monitor *monitored;
- enum ast_transport_monitor_reg res = AST_TRANSPORT_MONITOR_REG_NOT_FOUND;
- ast_assert(transport != NULL && cb != NULL);
- transports = ao2_global_obj_ref(active_transports);
- if (!transports) {
- return res;
- }
- ao2_lock(transports);
- monitored = ao2_find(transports, transport->obj_name, OBJ_SEARCH_KEY | OBJ_NOLOCK);
- if (monitored) {
- struct transport_monitor_notifier new_monitor;
- /* Add new monitor to vector */
- new_monitor.cb = cb;
- new_monitor.data = ao2_bump(ao2_data);
- if (AST_VECTOR_APPEND(&monitored->monitors, new_monitor)) {
- ao2_cleanup(ao2_data);
- res = AST_TRANSPORT_MONITOR_REG_FAILED;
- ast_debug(3, "Register monitor %p(%p) to transport %s FAILED\n",
- cb, ao2_data, transport->obj_name);
- } else {
- res = AST_TRANSPORT_MONITOR_REG_SUCCESS;
- ast_debug(3, "Registered monitor %p(%p) to transport %s\n",
- cb, ao2_data, transport->obj_name);
- }
- ao2_ref(monitored, -1);
- }
- ao2_unlock(transports);
- ao2_ref(transports, -1);
- return res;
- }
- void ast_sip_transport_state_unregister(struct ast_sip_tpmgr_state_callback *element)
- {
- AST_RWLIST_WRLOCK(&transport_state_list);
- AST_LIST_REMOVE(&transport_state_list, element, node);
- AST_RWLIST_UNLOCK(&transport_state_list);
- }
- void ast_sip_transport_state_register(struct ast_sip_tpmgr_state_callback *element)
- {
- struct ast_sip_tpmgr_state_callback *tpmgr_notifier;
- AST_RWLIST_WRLOCK(&transport_state_list);
- AST_LIST_TRAVERSE(&transport_state_list, tpmgr_notifier, node) {
- if (element == tpmgr_notifier) {
- /* Already registered. */
- AST_RWLIST_UNLOCK(&transport_state_list);
- return;
- }
- }
- AST_LIST_INSERT_HEAD(&transport_state_list, element, node);
- AST_RWLIST_UNLOCK(&transport_state_list);
- }
- void ast_sip_destroy_transport_events(void)
- {
- pjsip_tpmgr *tpmgr;
- tpmgr = pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint());
- if (tpmgr) {
- pjsip_tpmgr_set_state_cb(tpmgr, tpmgr_state_callback);
- }
- ao2_global_obj_release(active_transports);
- }
- int ast_sip_initialize_transport_events(void)
- {
- pjsip_tpmgr *tpmgr;
- struct ao2_container *transports;
- tpmgr = pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint());
- if (!tpmgr) {
- return -1;
- }
- transports = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0,
- ACTIVE_TRANSPORTS_BUCKETS, transport_monitor_hash_fn, NULL,
- transport_monitor_cmp_fn);
- if (!transports) {
- return -1;
- }
- ao2_global_obj_replace_unref(active_transports, transports);
- ao2_ref(transports, -1);
- tpmgr_state_callback = pjsip_tpmgr_get_state_cb(tpmgr);
- pjsip_tpmgr_set_state_cb(tpmgr, &transport_state_callback);
- return 0;
- }
|