<p>George Joseph <strong>merged</strong> this change.</p><p><a href="https://gerrit.asterisk.org/8084">View Change</a></p><div style="white-space:pre-wrap">Approvals:
Richard Mudgett: Looks good to me, but someone else must approve
Joshua Colp: Looks good to me, but someone else must approve
George Joseph: Looks good to me, approved; Approved for Submit
</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">res_pjsip_pubsub: Prune subs with reliable transports at startup<br><br>In an earlier release, inbound registrations on a reliable transport<br>were pruned on Asterisk restart since the TCP connection would have<br>been torn down and become unusable when Asterisk stopped. This same<br>process is now also applied to inbound subscriptions.<br><br>Also fixed issues in res_pjsip_registrar where it wasn't handling the<br>monitoring correctly when multiple registrations came in over the same<br>transport.<br><br>To accomplish this, the pjsip_transport_event feature needed to<br>be refactored to allow multiple monitors (multiple subcriptions or<br>registrations from the same endpoint) to exist on the same transport.<br>Since this changed the API, any external modules that may have used the<br>transport monitor feature (highly unlikey) will need to be changed.<br><br>ASTERISK-27612<br>Reported by: Ross Beer<br><br>Change-Id: Iee87cf4eb9b7b2b93d5739a72af52d6ca8fbbe36<br>---<br>M CHANGES<br>A contrib/ast-db-manage/config/versions/d3e4284f8707_add_prune_on_boot_to_ps_subscription_.py<br>M include/asterisk/res_pjsip.h<br>M res/res_pjsip.c<br>M res/res_pjsip/include/res_pjsip_private.h<br>M res/res_pjsip/pjsip_transport_events.c<br>M res/res_pjsip_outbound_registration.c<br>M res/res_pjsip_pubsub.c<br>M res/res_pjsip_registrar.c<br>9 files changed, 311 insertions(+), 80 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">diff --git a/CHANGES b/CHANGES<br>index 50ed2ca..8caec82 100644<br>--- a/CHANGES<br>+++ b/CHANGES<br>@@ -81,6 +81,10 @@<br> identifier method split into the "ip" and "header" endpoint identifier<br> methods.<br> <br>+ * The pjsip_transport_event feature introduced in 15.1.0 has been refactored.<br>+ Any external modules that may have used that feature (highly unlikey) will<br>+ need to be changed as the API has been altered slightly.<br>+<br> res_pjsip_endpoint_identifier_ip<br> ------------------<br> * The endpoint identifier "ip" method previously recognized endpoints either<br>@@ -95,6 +99,17 @@<br> you can now predict which endpoint is matched when a request comes in that<br> matches both.<br> <br>+res_pjsip_pubsub<br>+------------------<br>+ * In an earlier release, inbound registrations on a reliable transport<br>+ were pruned on Asterisk restart since the TCP connection would have<br>+ been torn down and become unusable when Asterisk stopped. This same<br>+ process is now also applied to inbound subscriptions. Since this<br>+ required the addition of a new column to the ps_subscription_persistence<br>+ realtime table, users who store their subscriptions in a database will<br>+ need to run the "alembic upgrade head" process to add the column to<br>+ the schema.<br>+<br> ------------------------------------------------------------------------------<br> --- Functionality changes from Asterisk 15.1.0 to Asterisk 15.2.0 ------------<br> ------------------------------------------------------------------------------<br>diff --git a/contrib/ast-db-manage/config/versions/d3e4284f8707_add_prune_on_boot_to_ps_subscription_.py b/contrib/ast-db-manage/config/versions/d3e4284f8707_add_prune_on_boot_to_ps_subscription_.py<br>new file mode 100644<br>index 0000000..aa780be<br>--- /dev/null<br>+++ b/contrib/ast-db-manage/config/versions/d3e4284f8707_add_prune_on_boot_to_ps_subscription_.py<br>@@ -0,0 +1,33 @@<br>+"""add prune_on_boot to ps_subscription_persistence<br>+<br>+Revision ID: d3e4284f8707<br>+Revises: 52798ad97bdf<br>+Create Date: 2018-01-28 17:45:36.218123<br>+<br>+"""<br>+<br>+# revision identifiers, used by Alembic.<br>+revision = 'd3e4284f8707'<br>+down_revision = '52798ad97bdf'<br>+<br>+from alembic import op<br>+import sqlalchemy as sa<br>+from sqlalchemy.dialects.postgresql import ENUM<br>+<br>+YESNO_NAME = 'yesno_values'<br>+YESNO_VALUES = ['yes', 'no']<br>+<br>+<br>+def upgrade():<br>+ ############################# Enums ##############################<br>+<br>+ # yesno_values have already been created, so use postgres enum object<br>+ # type to get around "already created" issue - works okay with mysql<br>+ yesno_values = ENUM(*YESNO_VALUES, name=YESNO_NAME, create_type=False)<br>+<br>+ op.add_column('ps_subscription_persistence', sa.Column('prune_on_boot', yesno_values))<br>+<br>+def downgrade():<br>+ if op.get_context().bind.dialect.name == 'mssql':<br>+ op.drop_constraint('ck_ps_subscription_persistence_prune_on_boot_yesno_values','ps_subscription_persistence')<br>+ op.drop_column('ps_subscription_persistence', 'prune_on_boot')<br>diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h<br>index a3bd782..c017e62 100644<br>--- a/include/asterisk/res_pjsip.h<br>+++ b/include/asterisk/res_pjsip.h<br>@@ -2985,6 +2985,18 @@<br> */<br> typedef void (*ast_transport_monitor_shutdown_cb)(void *data);<br> <br>+/*!<br>+ * \brief Transport shutdown monitor data matcher<br>+ * \since 13.20.0<br>+ *<br>+ * \param a User data to compare.<br>+ * \param b User data to compare.<br>+ *<br>+ * \retval 1 The data objects match<br>+ * \retval 0 The data objects don't match<br>+ */<br>+typedef int (*ast_transport_monitor_data_matcher)(void *a, void *b);<br>+<br> enum ast_transport_monitor_reg {<br> /*! \brief Successfully registered the transport monitor */<br> AST_TRANSPORT_MONITOR_REG_SUCCESS,<br>@@ -3001,11 +3013,17 @@<br> <br> /*!<br> * \brief Register a reliable transport shutdown monitor callback.<br>- * \since 13.18.0<br>+ * \since 13.20.0<br> *<br> * \param transport Transport to monitor for shutdown.<br> * \param cb Who to call when transport is shutdown.<br> * \param ao2_data Data to pass with the callback.<br>+ *<br>+ * \note The data object passed will have its reference count automatically<br>+ * incremented by this call and automatically decremented after the callback<br>+ * runs or when the callback is unregistered.<br>+ *<br>+ * There is no checking for duplicate registrations.<br> *<br> * \return enum ast_transport_monitor_reg<br> */<br>@@ -3013,25 +3031,41 @@<br> ast_transport_monitor_shutdown_cb cb, void *ao2_data);<br> <br> /*!<br>- * \brief Unregister a reliable transport shutdown monitor callback.<br>- * \since 13.18.0<br>+ * \brief Unregister a reliable transport shutdown monitor<br>+ * \since 13.20.0<br> *<br> * \param transport Transport to monitor for shutdown.<br>- * \param cb Who to call when transport is shutdown.<br>+ * \param cb The callback that was used for the original register.<br>+ * \param data Data to pass to the matcher. May be NULL and does NOT need to be an ao2 object.<br>+ * If NULL, all monitors with the provided callbck are unregistered.<br>+ * \param matches Matcher function that returns true if data matches the previously<br>+ * registered data object. If NULL, a simple pointer comparison is done.<br>+ *<br>+ * \note The data object passed into the original register will have its reference count<br>+ * automatically decremeneted.<br> *<br> * \return Nothing<br> */<br>-void ast_sip_transport_monitor_unregister(pjsip_transport *transport, ast_transport_monitor_shutdown_cb cb);<br>+void ast_sip_transport_monitor_unregister(pjsip_transport *transport,<br>+ ast_transport_monitor_shutdown_cb cb, void *data, ast_transport_monitor_data_matcher matches);<br> <br> /*!<br>- * \brief Unregister monitor callback from all reliable transports.<br>- * \since 13.18.0<br>+ * \brief Unregister a transport shutdown monitor from all reliable transports<br>+ * \since 13.20.0<br> *<br>- * \param cb Who to call when a transport is shutdown.<br>+ * \param cb The callback that was used for the original register.<br>+ * \param data Data to pass to the matcher. May be NULL and does NOT need to be an ao2 object.<br>+ * If NULL, all monitors with the provided callbck are unregistered.<br>+ * \param matches Matcher function that returns true if ao2_data matches the previously<br>+ * registered data object. If NULL, a simple pointer comparison is done.<br>+ *<br>+ * \note The data object passed into the original register will have its reference count<br>+ * automatically decremeneted.<br> *<br> * \return Nothing<br> */<br>-void ast_sip_transport_monitor_unregister_all(ast_transport_monitor_shutdown_cb cb);<br>+void ast_sip_transport_monitor_unregister_all(ast_transport_monitor_shutdown_cb cb,<br>+ void *data, ast_transport_monitor_data_matcher matches);<br> <br> /*! Transport state notification registration element. */<br> struct ast_sip_tpmgr_state_callback {<br>diff --git a/res/res_pjsip.c b/res/res_pjsip.c<br>index 6a7d918..bf859fe 100644<br>--- a/res/res_pjsip.c<br>+++ b/res/res_pjsip.c<br>@@ -3118,6 +3118,45 @@<br> return ast_pjsip_endpoint;<br> }<br> <br>+int ast_sip_will_uri_survive_restart(pjsip_sip_uri *uri, struct ast_sip_endpoint *endpoint,<br>+ pjsip_rx_data *rdata)<br>+{<br>+ pj_str_t host_name;<br>+ int result = 1;<br>+<br>+ /* Determine if the contact cannot survive a restart/boot. */<br>+ if (uri->port == rdata->pkt_info.src_port<br>+ && !pj_strcmp(&uri->host,<br>+ pj_cstr(&host_name, rdata->pkt_info.src_name))<br>+ /* We have already checked if the URI scheme is sip: or sips: */<br>+ && PJSIP_TRANSPORT_IS_RELIABLE(rdata->tp_info.transport)) {<br>+ pj_str_t type_name;<br>+<br>+ /* Determine the transport parameter value */<br>+ if (!strcasecmp("WSS", rdata->tp_info.transport->type_name)) {<br>+ /* WSS is special, as it needs to be ws. */<br>+ pj_cstr(&type_name, "ws");<br>+ } else {<br>+ pj_cstr(&type_name, rdata->tp_info.transport->type_name);<br>+ }<br>+<br>+ if (!pj_stricmp(&uri->transport_param, &type_name)<br>+ && (endpoint->nat.rewrite_contact<br>+ /* Websockets are always rewritten */<br>+ || !pj_stricmp(&uri->transport_param,<br>+ pj_cstr(&type_name, "ws")))) {<br>+ /*<br>+ * The contact was rewritten to the reliable transport's<br>+ * source address. Disconnecting the transport for any<br>+ * reason invalidates the contact.<br>+ */<br>+ result = 0;<br>+ }<br>+ }<br>+<br>+ return result;<br>+}<br>+<br> int ast_sip_get_transport_name(const struct ast_sip_endpoint *endpoint,<br> pjsip_sip_uri *sip_uri, char *buf, size_t buf_len)<br> {<br>diff --git a/res/res_pjsip/include/res_pjsip_private.h b/res/res_pjsip/include/res_pjsip_private.h<br>index 7fafd80..7d434aa 100644<br>--- a/res/res_pjsip/include/res_pjsip_private.h<br>+++ b/res/res_pjsip/include/res_pjsip_private.h<br>@@ -347,4 +347,18 @@<br> */<br> int ast_sip_destroy_scheduler(void);<br> <br>+/*!<br>+ * \internal<br>+ * \brief Determines if a uri will still be valid after an asterisk restart<br>+ * \since 13.20.0<br>+ *<br>+ * \param uri uri to test<br>+ * \param endpoint The associated endpoint<br>+ * \param rdata The rdata to get transport information from<br>+ *<br>+ * \retval 1 Yes, 0 No<br>+ */<br>+int ast_sip_will_uri_survive_restart(pjsip_sip_uri *uri, struct ast_sip_endpoint *endpoint,<br>+ pjsip_rx_data *rdata);<br>+<br> #endif /* RES_PJSIP_PRIVATE_H_ */<br>diff --git a/res/res_pjsip/pjsip_transport_events.c b/res/res_pjsip/pjsip_transport_events.c<br>index 0f57303..c701b84 100644<br>--- a/res/res_pjsip/pjsip_transport_events.c<br>+++ b/res/res_pjsip/pjsip_transport_events.c<br>@@ -135,7 +135,7 @@<br> break;<br> }<br> monitored->transport = transport;<br>- if (AST_VECTOR_INIT(&monitored->monitors, 2)) {<br>+ if (AST_VECTOR_INIT(&monitored->monitors, 5)) {<br> ao2_ref(monitored, -1);<br> break;<br> }<br>@@ -166,6 +166,8 @@<br> struct transport_monitor_notifier *notifier;<br> <br> notifier = AST_VECTOR_GET_ADDR(&monitored->monitors, idx);<br>+ ast_debug(3, "running callback %p(%p) for transport %s\n",<br>+ notifier->cb, notifier->data, transport->obj_name);<br> notifier->cb(notifier->data);<br> }<br> ao2_ref(monitored, -1);<br>@@ -195,42 +197,65 @@<br> }<br> }<br> <br>-static int transport_monitor_unregister_all(void *obj, void *arg, int flags)<br>+struct callback_data {<br>+ ast_transport_monitor_shutdown_cb cb;<br>+ void *data;<br>+ ast_transport_monitor_data_matcher matches;<br>+};<br>+<br>+static int transport_monitor_unregister_cb(void *obj, void *arg, int flags)<br> {<br> struct transport_monitor *monitored = obj;<br>- ast_transport_monitor_shutdown_cb cb = arg;<br>+ struct callback_data *cb_data = arg;<br> int idx;<br> <br> for (idx = AST_VECTOR_SIZE(&monitored->monitors); idx--;) {<br> struct transport_monitor_notifier *notifier;<br> <br> notifier = AST_VECTOR_GET_ADDR(&monitored->monitors, idx);<br>- if (notifier->cb == cb) {<br>+ if (notifier->cb == cb_data->cb && (!cb_data->data<br>+ || cb_data->matches(cb_data->data, notifier->data))) {<br> ao2_cleanup(notifier->data);<br> AST_VECTOR_REMOVE_UNORDERED(&monitored->monitors, idx);<br>- break;<br>+ ast_debug(3, "Unregistered monitor %p(%p) from transport %s\n",<br>+ notifier->cb, notifier->data, monitored->transport->obj_name);<br> }<br> }<br> return 0;<br> }<br> <br>-void ast_sip_transport_monitor_unregister_all(ast_transport_monitor_shutdown_cb cb)<br>+static int ptr_matcher(void *a, void *b)<br>+{<br>+ return a == b;<br>+}<br>+<br>+void ast_sip_transport_monitor_unregister_all(ast_transport_monitor_shutdown_cb cb,<br>+ void *data, ast_transport_monitor_data_matcher matches)<br> {<br> struct ao2_container *transports;<br>+ struct callback_data cb_data = {<br>+ .cb = cb,<br>+ .data = data,<br>+ .matches = matches ?: ptr_matcher,<br>+ };<br>+<br>+ ast_assert(cb != NULL);<br> <br> transports = ao2_global_obj_ref(active_transports);<br> if (!transports) {<br> return;<br> }<br>- ao2_callback(transports, OBJ_MULTIPLE | OBJ_NODATA, transport_monitor_unregister_all,<br>- cb);<br>+ ao2_callback(transports, OBJ_MULTIPLE | OBJ_NODATA, transport_monitor_unregister_cb, &cb_data);<br> ao2_ref(transports, -1);<br> }<br> <br>-void ast_sip_transport_monitor_unregister(pjsip_transport *transport, ast_transport_monitor_shutdown_cb cb)<br>+void ast_sip_transport_monitor_unregister(pjsip_transport *transport,<br>+ ast_transport_monitor_shutdown_cb cb, void *data, ast_transport_monitor_data_matcher matches)<br> {<br> struct ao2_container *transports;<br> struct transport_monitor *monitored;<br>+<br>+ ast_assert(transport != NULL && cb != NULL);<br> <br> transports = ao2_global_obj_ref(active_transports);<br> if (!transports) {<br>@@ -240,18 +265,13 @@<br> ao2_lock(transports);<br> monitored = ao2_find(transports, transport->obj_name, OBJ_SEARCH_KEY | OBJ_NOLOCK);<br> if (monitored) {<br>- int idx;<br>+ struct callback_data cb_data = {<br>+ .cb = cb,<br>+ .data = data,<br>+ .matches = matches ?: ptr_matcher,<br>+ };<br> <br>- for (idx = AST_VECTOR_SIZE(&monitored->monitors); idx--;) {<br>- struct transport_monitor_notifier *notifier;<br>-<br>- notifier = AST_VECTOR_GET_ADDR(&monitored->monitors, idx);<br>- if (notifier->cb == cb) {<br>- ao2_cleanup(notifier->data);<br>- AST_VECTOR_REMOVE_UNORDERED(&monitored->monitors, idx);<br>- break;<br>- }<br>- }<br>+ transport_monitor_unregister_cb(monitored, &cb_data, 0);<br> ao2_ref(monitored, -1);<br> }<br> ao2_unlock(transports);<br>@@ -265,6 +285,8 @@<br> struct transport_monitor *monitored;<br> enum ast_transport_monitor_reg res = AST_TRANSPORT_MONITOR_REG_NOT_FOUND;<br> <br>+ ast_assert(transport != NULL && cb != NULL);<br>+<br> transports = ao2_global_obj_ref(active_transports);<br> if (!transports) {<br> return res;<br>@@ -273,21 +295,7 @@<br> ao2_lock(transports);<br> monitored = ao2_find(transports, transport->obj_name, OBJ_SEARCH_KEY | OBJ_NOLOCK);<br> if (monitored) {<br>- int idx;<br> struct transport_monitor_notifier new_monitor;<br>-<br>- /* Check if the callback monitor already exists */<br>- for (idx = AST_VECTOR_SIZE(&monitored->monitors); idx--;) {<br>- struct transport_monitor_notifier *notifier;<br>-<br>- notifier = AST_VECTOR_GET_ADDR(&monitored->monitors, idx);<br>- if (notifier->cb == cb) {<br>- /* The monitor is already in the vector replace with new ao2_data. */<br>- ao2_replace(notifier->data, ao2_data);<br>- res = AST_TRANSPORT_MONITOR_REG_REPLACED;<br>- goto register_done;<br>- }<br>- }<br> <br> /* Add new monitor to vector */<br> new_monitor.cb = cb;<br>@@ -295,9 +303,14 @@<br> if (AST_VECTOR_APPEND(&monitored->monitors, new_monitor)) {<br> ao2_cleanup(ao2_data);<br> res = AST_TRANSPORT_MONITOR_REG_FAILED;<br>+ ast_debug(3, "Register monitor %p(%p) to transport %s FAILED\n",<br>+ cb, ao2_data, transport->obj_name);<br>+ } else {<br>+ res = AST_TRANSPORT_MONITOR_REG_SUCCESS;<br>+ ast_debug(3, "Registered monitor %p(%p) to transport %s\n",<br>+ cb, ao2_data, transport->obj_name);<br> }<br> <br>-register_done:<br> ao2_ref(monitored, -1);<br> }<br> ao2_unlock(transports);<br>diff --git a/res/res_pjsip_outbound_registration.c b/res/res_pjsip_outbound_registration.c<br>index 4baf23c..d0f7546 100644<br>--- a/res/res_pjsip_outbound_registration.c<br>+++ b/res/res_pjsip_outbound_registration.c<br>@@ -850,6 +850,14 @@<br> }<br> }<br> <br>+static int monitor_matcher(void *a, void *b)<br>+{<br>+ char *ma = a;<br>+ char *mb = b;<br>+<br>+ return strcmp(ma, mb) == 0;<br>+}<br>+<br> static void registration_transport_monitor_setup(pjsip_transport *transport, const char *registration_name)<br> {<br> char *monitor;<br>@@ -950,7 +958,8 @@<br> ast_debug(1, "Outbound unregistration to '%s' with client '%s' successful\n", server_uri, client_uri);<br> update_client_state_status(response->client_state, SIP_REGISTRATION_UNREGISTERED);<br> ast_sip_transport_monitor_unregister(response->rdata->tp_info.transport,<br>- registration_transport_shutdown_cb);<br>+ registration_transport_shutdown_cb, response->client_state->registration_name,<br>+ monitor_matcher);<br> }<br> } else if (response->client_state->destroy) {<br> /* We need to deal with the pending destruction instead. */<br>@@ -2149,7 +2158,7 @@<br> <br> ao2_global_obj_release(current_states);<br> <br>- ast_sip_transport_monitor_unregister_all(registration_transport_shutdown_cb);<br>+ ast_sip_transport_monitor_unregister_all(registration_transport_shutdown_cb, NULL, NULL);<br> <br> /* Wait for registration serializers to get destroyed. */<br> ast_debug(2, "Waiting for registration transactions to complete for unload.\n");<br>diff --git a/res/res_pjsip_pubsub.c b/res/res_pjsip_pubsub.c<br>index 369e06d..c78f20c 100644<br>--- a/res/res_pjsip_pubsub.c<br>+++ b/res/res_pjsip_pubsub.c<br>@@ -127,6 +127,11 @@<br> <configOption name="contact_uri"><br> <synopsis>The Contact URI of the dialog for the subscription</synopsis><br> </configOption><br>+ <configOption name="prune_on_boot"><br>+ <synopsis>If set, indicates that the contact used a reliable transport<br>+ and therefore the subscription must be deleted after an asterisk restart.<br>+ </synopsis><br>+ </configOption><br> </configObject><br> <configObject name="resource_list"><br> <synopsis>Resource list configuration parameters.</synopsis><br>@@ -382,6 +387,8 @@<br> struct timeval expires;<br> /*! Contact URI */<br> char contact_uri[PJSIP_MAX_URL_SIZE];<br>+ /*! Prune subscription on restart */<br>+ int prune_on_boot;<br> };<br> <br> /*!<br>@@ -446,6 +453,10 @@<br> * capable of restarting the timer.<br> */<br> struct ast_sip_sched_task *expiration_task;<br>+ /*! The transport the subscription was received on.<br>+ * Only used for reliable transports.<br>+ */<br>+ pjsip_transport *transport;<br> };<br> <br> /*!<br>@@ -549,6 +560,17 @@<br> return ast_sorcery_generic_alloc(sizeof(struct ast_sip_publication_resource), publication_resource_destroy);<br> }<br> <br>+static void sub_tree_transport_cb(void *data) {<br>+ struct sip_subscription_tree *sub_tree = data;<br>+<br>+ ast_debug(3, "Transport destroyed. Removing subscription '%s->%s' prune on restart: %d\n",<br>+ sub_tree->persistence->endpoint, sub_tree->root->resource,<br>+ sub_tree->persistence->prune_on_boot);<br>+<br>+ sub_tree->state = SIP_SUB_TREE_TERMINATE_IN_PROGRESS;<br>+ pjsip_evsub_terminate(sub_tree->evsub, PJ_TRUE);<br>+}<br>+<br> /*! \brief Destructor for subscription persistence */<br> static void subscription_persistence_destroy(void *obj)<br> {<br>@@ -599,8 +621,9 @@<br> return;<br> }<br> <br>- ast_debug(3, "Updating persistence for '%s->%s'\n", sub_tree->persistence->endpoint,<br>- sub_tree->root->resource);<br>+ ast_debug(3, "Updating persistence for '%s->%s' prune on restart: %s\n",<br>+ sub_tree->persistence->endpoint, sub_tree->root->resource,<br>+ sub_tree->persistence->prune_on_boot ? "yes" : "no");<br> <br> dlg = sub_tree->dlg;<br> sub_tree->persistence->cseq = dlg->local.cseq;<br>@@ -614,6 +637,28 @@<br> sub_tree->persistence->expires = ast_tvadd(ast_tvnow(), ast_samp2tv(expires, 1));<br> <br> if (contact_hdr) {<br>+ if (contact_hdr) {<br>+ if (type == SUBSCRIPTION_PERSISTENCE_CREATED) {<br>+ sub_tree->persistence->prune_on_boot =<br>+ !ast_sip_will_uri_survive_restart(<br>+ (pjsip_sip_uri *)pjsip_uri_get_uri(contact_hdr->uri),<br>+ sub_tree->endpoint, rdata);<br>+<br>+ if (sub_tree->persistence->prune_on_boot) {<br>+ ast_debug(3, "adding transport monitor on %s for '%s->%s' prune on restart: %d\n",<br>+ rdata->tp_info.transport->obj_name,<br>+ sub_tree->persistence->endpoint, sub_tree->root->resource,<br>+ sub_tree->persistence->prune_on_boot);<br>+ sub_tree->transport = rdata->tp_info.transport;<br>+ ast_sip_transport_monitor_register(rdata->tp_info.transport,<br>+ sub_tree_transport_cb, sub_tree);<br>+ /*<br>+ * FYI: ast_sip_transport_monitor_register holds a reference to the sub_tree<br>+ */<br>+ }<br>+ }<br>+ }<br>+<br> pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, contact_hdr->uri,<br> sub_tree->persistence->contact_uri, sizeof(sub_tree->persistence->contact_uri));<br> } else {<br>@@ -654,6 +699,15 @@<br> {<br> if (!sub_tree->persistence) {<br> return;<br>+ }<br>+<br>+ if (sub_tree->persistence->prune_on_boot && sub_tree->transport) {<br>+ ast_debug(3, "Unregistering transport monitor on %s '%s->%s'\n",<br>+ sub_tree->transport->obj_name,<br>+ sub_tree->endpoint ? ast_sorcery_object_get_id(sub_tree->endpoint) : "Unknown",<br>+ sub_tree->root ? sub_tree->root->resource : "Unknown");<br>+ ast_sip_transport_monitor_unregister(sub_tree->transport,<br>+ sub_tree_transport_cb, sub_tree, NULL);<br> }<br> <br> ast_sorcery_delete(ast_sip_get_sorcery(), sub_tree->persistence);<br>@@ -1563,6 +1617,14 @@<br> struct ast_taskprocessor *serializer;<br> pjsip_rx_data rdata;<br> struct persistence_recreate_data recreate_data;<br>+<br>+ /* If this subscription used a reliable transport it can't be reestablished so remove it */<br>+ if (persistence->prune_on_boot) {<br>+ ast_debug(3, "Deleting subscription marked as 'prune' from persistent store '%s' %s\n",<br>+ persistence->endpoint, persistence->tag);<br>+ ast_sorcery_delete(ast_sip_get_sorcery(), persistence);<br>+ return 0;<br>+ }<br> <br> /* If this subscription has already expired remove it */<br> if (ast_tvdiff_ms(persistence->expires, ast_tvnow()) <= 0) {<br>@@ -5416,6 +5478,8 @@<br> persistence_expires_str2struct, persistence_expires_struct2str, NULL, 0, 0);<br> ast_sorcery_object_field_register(sorcery, "subscription_persistence", "contact_uri", "", OPT_CHAR_ARRAY_T, 0,<br> CHARFLDSET(struct subscription_persistence, contact_uri));<br>+ ast_sorcery_object_field_register(sorcery, "subscription_persistence", "prune_on_boot", "0", OPT_UINT_T, 0,<br>+ FLDSET(struct subscription_persistence, prune_on_boot));<br> <br> if (apply_list_configuration(sorcery)) {<br> ast_sched_context_destroy(sched);<br>@@ -5492,6 +5556,8 @@<br> AST_TEST_UNREGISTER(loop);<br> AST_TEST_UNREGISTER(bad_event);<br> <br>+ ast_sip_transport_monitor_unregister_all(sub_tree_transport_cb, NULL, NULL);<br>+<br> ast_cli_unregister_multiple(cli_commands, ARRAY_LEN(cli_commands));<br> <br> ast_manager_unregister(AMI_SHOW_SUBSCRIPTIONS_OUTBOUND);<br>diff --git a/res/res_pjsip_registrar.c b/res/res_pjsip_registrar.c<br>index 078e13e..2e519b7 100644<br>--- a/res/res_pjsip_registrar.c<br>+++ b/res/res_pjsip_registrar.c<br>@@ -327,6 +327,15 @@<br> char aor_name[0];<br> };<br> <br>+static int contact_transport_monitor_matcher(void *a, void *b)<br>+{<br>+ struct contact_transport_monitor *ma = a;<br>+ struct contact_transport_monitor *mb = b;<br>+<br>+ return strcmp(ma->aor_name, mb->aor_name) == 0<br>+ && strcmp(ma->contact_name, mb->contact_name) == 0;<br>+}<br>+<br> static void register_contact_transport_shutdown_cb(void *data)<br> {<br> struct contact_transport_monitor *monitor = data;<br>@@ -578,8 +587,7 @@<br> <br> contact = ao2_callback(contacts, OBJ_UNLINK, registrar_find_contact, &details);<br> if (!contact) {<br>- int prune_on_boot = 0;<br>- pj_str_t host_name;<br>+ int prune_on_boot;<br> <br> /* If they are actually trying to delete a contact that does not exist... be forgiving */<br> if (!expiration) {<br>@@ -588,35 +596,7 @@<br> continue;<br> }<br> <br>- /* Determine if the contact cannot survive a restart/boot. */<br>- if (details.uri->port == rdata->pkt_info.src_port<br>- && !pj_strcmp(&details.uri->host,<br>- pj_cstr(&host_name, rdata->pkt_info.src_name))<br>- /* We have already checked if the URI scheme is sip: or sips: */<br>- && PJSIP_TRANSPORT_IS_RELIABLE(rdata->tp_info.transport)) {<br>- pj_str_t type_name;<br>-<br>- /* Determine the transport parameter value */<br>- if (!strcasecmp("WSS", rdata->tp_info.transport->type_name)) {<br>- /* WSS is special, as it needs to be ws. */<br>- pj_cstr(&type_name, "ws");<br>- } else {<br>- pj_cstr(&type_name, rdata->tp_info.transport->type_name);<br>- }<br>-<br>- if (!pj_stricmp(&details.uri->transport_param, &type_name)<br>- && (endpoint->nat.rewrite_contact<br>- /* Websockets are always rewritten */<br>- || !pj_stricmp(&details.uri->transport_param,<br>- pj_cstr(&type_name, "ws")))) {<br>- /*<br>- * The contact was rewritten to the reliable transport's<br>- * source address. Disconnecting the transport for any<br>- * reason invalidates the contact.<br>- */<br>- prune_on_boot = 1;<br>- }<br>- }<br>+ prune_on_boot = !ast_sip_will_uri_survive_restart(details.uri, endpoint, rdata);<br> <br> contact = ast_sip_location_create_contact(aor, contact_uri,<br> ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1)),<br>@@ -703,6 +683,21 @@<br> contact_update->user_agent);<br> ao2_cleanup(contact_update);<br> } else {<br>+ if (contact->prune_on_boot) {<br>+ struct contact_transport_monitor *monitor;<br>+ const char *contact_name =<br>+ ast_sorcery_object_get_id(contact);<br>+<br>+ monitor = ast_alloca(sizeof(*monitor) + 2 + strlen(aor_name)<br>+ + strlen(contact_name));<br>+ strcpy(monitor->aor_name, aor_name);/* Safe */<br>+ monitor->contact_name = monitor->aor_name + strlen(aor_name) + 1;<br>+ strcpy(monitor->contact_name, contact_name);/* Safe */<br>+<br>+ ast_sip_transport_monitor_unregister(rdata->tp_info.transport,<br>+ register_contact_transport_shutdown_cb, monitor, contact_transport_monitor_matcher);<br>+ }<br>+<br> /* We want to report the user agent that was actually in the removed contact */<br> ast_sip_location_delete_contact(contact);<br> ast_verb(3, "Removed contact '%s' from AOR '%s' due to request\n", contact_uri, aor_name);<br>@@ -1114,6 +1109,19 @@<br> */<br> ao2_lock(lock);<br> if (ast_tvdiff_ms(ast_tvnow(), contact->expiration_time) > 0) {<br>+ if (contact->prune_on_boot) {<br>+ struct contact_transport_monitor *monitor;<br>+ const char *contact_name = ast_sorcery_object_get_id(contact);<br>+<br>+ monitor = ast_alloca(sizeof(*monitor) + 2 + strlen(contact->aor)<br>+ + strlen(contact_name));<br>+ strcpy(monitor->aor_name, contact->aor);/* Safe */<br>+ monitor->contact_name = monitor->aor_name + strlen(contact->aor) + 1;<br>+ strcpy(monitor->contact_name, contact_name);/* Safe */<br>+<br>+ ast_sip_transport_monitor_unregister_all(register_contact_transport_shutdown_cb,<br>+ monitor, contact_transport_monitor_matcher);<br>+ }<br> ast_sip_location_delete_contact(contact);<br> }<br> ao2_unlock(lock);<br>@@ -1221,7 +1229,7 @@<br> ast_manager_unregister(AMI_SHOW_REGISTRATIONS);<br> ast_manager_unregister(AMI_SHOW_REGISTRATION_CONTACT_STATUSES);<br> ast_sip_unregister_service(®istrar_module);<br>- ast_sip_transport_monitor_unregister_all(register_contact_transport_shutdown_cb);<br>+ ast_sip_transport_monitor_unregister_all(register_contact_transport_shutdown_cb, NULL, NULL);<br> return 0;<br> }<br> <br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/8084">change 8084</a>. To unsubscribe, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/8084"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-MessageType: merged </div>
<div style="display:none"> Gerrit-Change-Id: Iee87cf4eb9b7b2b93d5739a72af52d6ca8fbbe36 </div>
<div style="display:none"> Gerrit-Change-Number: 8084 </div>
<div style="display:none"> Gerrit-PatchSet: 5 </div>
<div style="display:none"> Gerrit-Owner: George Joseph <gjoseph@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: George Joseph <gjoseph@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins2 </div>
<div style="display:none"> Gerrit-Reviewer: Joshua Colp <jcolp@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Richard Mudgett <rmudgett@digium.com> </div>