[asterisk-dev] Change in asterisk[master]: pjsip_options: Add qualify_timeout processing and eventing

George Joseph (Code Review) asteriskteam at digium.com
Sat Apr 11 20:08:07 CDT 2015


George Joseph has uploaded a new change for review.

  https://gerrit.asterisk.org/47

Change subject: pjsip_options: Add qualify_timeout processing and eventing
......................................................................

pjsip_options: Add qualify_timeout processing and eventing

This is the second follow-on to https://reviewboard.asterisk.org/r/4572/ and the
discussion at
http://lists.digium.com/pipermail/asterisk-dev/2015-March/073921.html

The basic issues are that changes in contact status don't cause events to be
emitted for the associated endpoint.  Only dynamic contact add/delete actions
update the endpoint.  Also, the qualify timeout is fixed by pjsip at 32 seconds
which is a long time.

This patch makes use of the new transaction timeout feature in r4585 and
provides the following capabilities...

1.  A new aor/contact variable 'qualify_timeout' has been added that allows the
user to specify the maximum time in milliseconds to wait for a response to an
OPTIONS mesasge.  The default is 3000ms.  When the timer expires, the contact is
marked unavailable.

2.  Contact status changes are now propagated up to the endpoint as follows...
When any contact is 'Available', the endpoint is marked as 'Reachable'.  When
all contacts are 'Unavailable', the endpoint is marked as 'Unreachable'.  The
existing endpoint events are generated appropriately.

Testing Done:
Existing tests are unchanged.  I'm working on new testsuite tests to check the
new functionality.

Change-Id: Id0ce0528e58014da1324856ea537e7765466044a
Tested-by: Dmitriy Serov
Tested-by: George Joseph <george.joseph at fairview5.com>
---
M CHANGES
M configs/samples/pjsip.conf.sample
A contrib/ast-db-manage/config/versions/2256a84ca226_add_pjsip_qualify_timeout.py
M include/asterisk/endpoints.h
M include/asterisk/res_pjsip.h
M main/endpoints.c
M res/res_pjsip.c
M res/res_pjsip/location.c
M res/res_pjsip/pjsip_configuration.c
M res/res_pjsip/pjsip_options.c
10 files changed, 285 insertions(+), 35 deletions(-)


  git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/47/47/1

diff --git a/CHANGES b/CHANGES
index 4237c82..76af05a 100644
--- a/CHANGES
+++ b/CHANGES
@@ -139,6 +139,14 @@
  * A new CLI command has been added: "pjsip show settings", which shows
    both the global and system configuration settings.
 
+ * A new aor option has been added: "qualify_timeout", which sets the timeout
+   in seconds for a qualify.  The default is 3 seconds.  This was previously
+   hard coded to 32 seconds in pjproject.
+
+ * Endpoint status will now change to "Unreachable" when all contacts are
+   unavailable.  When any contact becomes available, the endpoint will status
+   will change back to "Reachable".
+
 res_ari_channels
 ------------------
  * Two new events, 'ChannelHold' and 'ChannelUnhold', have been added to the
diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample
index d3bb518..0dd628c 100644
--- a/configs/samples/pjsip.conf.sample
+++ b/configs/samples/pjsip.conf.sample
@@ -812,6 +812,7 @@
                         ; (default: "no")
 ;type=  ; Must be of type aor (default: "")
 ;qualify_frequency=0    ; Interval at which to qualify an AoR (default: "0")
+;qualify_timeout=3      ; Qualify timeout in seconds (default: "3")
 ;authenticate_qualify=no        ; Authenticates a qualify request if needed
                                 ; (default: "no")
 ;outbound_proxy=        ; Outbound proxy used when sending OPTIONS request
diff --git a/contrib/ast-db-manage/config/versions/2256a84ca226_add_pjsip_qualify_timeout.py b/contrib/ast-db-manage/config/versions/2256a84ca226_add_pjsip_qualify_timeout.py
new file mode 100644
index 0000000..41f1b3d
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/2256a84ca226_add_pjsip_qualify_timeout.py
@@ -0,0 +1,44 @@
+#
+# Asterisk -- An open source telephony toolkit.
+#
+# Copyright (C) 2015, Fairview 5 Engineering, LLC
+#
+# George Joseph <george.joseph at fairview5.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.
+#
+
+"""add_pjsip_qualify_timeout
+
+Revision ID: 2256a84ca226
+Revises: 23530d604b96
+Create Date: 2015-04-03 13:18:18.023787
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '2256a84ca226'
+down_revision = '23530d604b96'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+    op.add_column('ps_aors', sa.Column('qualify_timeout', sa.Integer))
+    op.add_column('ps_contacts', sa.Column('qualify_timeout', sa.Integer))
+    pass
+
+
+def downgrade():
+    op.drop_column('ps_aors', 'qualify_timeout')
+    op.drop_column('ps_contacts', 'qualify_timeout')
+    pass
diff --git a/include/asterisk/endpoints.h b/include/asterisk/endpoints.h
index 663dd94..c9cb6b9 100644
--- a/include/asterisk/endpoints.h
+++ b/include/asterisk/endpoints.h
@@ -160,6 +160,16 @@
 const char *ast_endpoint_get_id(const struct ast_endpoint *endpoint);
 
 /*!
+ * \brief Gets the state of the given endpoint.
+ *
+ * \param endpoint The endpoint.
+ * \return state.
+ * \return \c AST_ENDPOINT_UNKNOWN if endpoint is \c NULL.
+ * \since 13.4
+ */
+enum ast_endpoint_state ast_endpoint_get_state(const struct ast_endpoint *endpoint);
+
+/*!
  * \brief Updates the state of the given endpoint.
  *
  * \param endpoint Endpoint to modify.
diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h
index 2358a72..a56eded 100644
--- a/include/asterisk/res_pjsip.h
+++ b/include/asterisk/res_pjsip.h
@@ -164,6 +164,8 @@
 	struct timeval expiration_time;
 	/*! Frequency to send OPTIONS requests to contact. 0 is disabled. */
 	unsigned int qualify_frequency;
+	/*! Qualify timeout. 0 is diabled. */
+	unsigned int qualify_timeout;
 	/*! If true authenticate the qualify if needed */
 	int authenticate_qualify;
 };
@@ -188,6 +190,8 @@
 	SORCERY_OBJECT(details);
 	/*! Current status for a contact (default - unavailable) */
 	enum ast_sip_contact_status_type status;
+	/*! Last status for a contact (default - unavailable) */
+	enum ast_sip_contact_status_type last_status;
 	/*! The round trip start time set before sending a qualify request */
 	struct timeval rtt_start;
 	/*! The round trip time in microseconds */
@@ -214,6 +218,8 @@
 	unsigned int default_expiration;
 	/*! Frequency to send OPTIONS requests to AOR contacts. 0 is disabled. */
 	unsigned int qualify_frequency;
+	/*! Qualify timeout. 0 is diabled. */
+	unsigned int qualify_timeout;
 	/*! If true authenticate the qualify if needed */
 	int authenticate_qualify;
 	/*! Maximum number of external contacts, 0 to disable */
@@ -231,12 +237,12 @@
  * a consistent contact id.  Used by ast_sip_for_each_contact.
  */
 struct ast_sip_contact_wrapper {
+	/*! Pointer to the actual contact. */
+	struct ast_sip_contact *contact;
 	/*! The id of the parent aor. */
 	char *aor_id;
 	/*! The id of contact in form of aor_id/contact_uri. */
 	char *contact_id;
-	/*! Pointer to the actual contact. */
-	struct ast_sip_contact *contact;
 };
 
 /*!
@@ -905,6 +911,15 @@
 struct ast_sip_contact *ast_sip_location_retrieve_contact_from_aor_list(const char *aor_list);
 
 /*!
+ * \brief Retrieve all contacts from a list of AORs
+ *
+ * \param aor_list A comma-separated list of AOR names
+ * \retval NULL if no contacts available
+ * \retval non-NULL if contacts available
+ */
+struct ao2_container *ast_sip_location_retrieve_contacts_from_aor_list(char *aor_list);
+
+/*!
  * \brief Retrieve the first bound contact AND the AOR chosen from a list of AORs
  *
  * \param aor_list A comma-separated list of AOR names
diff --git a/main/endpoints.c b/main/endpoints.c
index 66ad461..ce0ab02 100644
--- a/main/endpoints.c
+++ b/main/endpoints.c
@@ -415,6 +415,14 @@
 	return endpoint->id;
 }
 
+enum ast_endpoint_state ast_endpoint_get_state(const struct ast_endpoint *endpoint)
+{
+	if (!endpoint) {
+		return AST_ENDPOINT_UNKNOWN;
+	}
+	return endpoint->state;
+}
+
 void ast_endpoint_set_state(struct ast_endpoint *endpoint,
 	enum ast_endpoint_state state)
 {
diff --git a/res/res_pjsip.c b/res/res_pjsip.c
index fcd8516..74f2e6a 100644
--- a/res/res_pjsip.c
+++ b/res/res_pjsip.c
@@ -1009,6 +1009,14 @@
 						If <literal>0</literal> never qualify. Time in seconds.
 					</para></description>
 				</configOption>
+				<configOption name="qualify_timeout" default="3">
+					<synopsis>Timeout for qualify</synopsis>
+					<description><para>
+						If the contact doesn't repond before the timeout,
+						the contact is marked unavailable.
+						If <literal>0</literal> no timeout. Time in seconds.
+					</para></description>
+				</configOption>
 				<configOption name="outbound_proxy">
 					<synopsis>Outbound proxy used when sending OPTIONS request</synopsis>
 					<description><para>
@@ -1123,6 +1131,14 @@
 						If <literal>0</literal> never qualify. Time in seconds.
 					</para></description>
 				</configOption>
+				<configOption name="qualify_timeout" default="3">
+					<synopsis>Timeout for qualify</synopsis>
+					<description><para>
+						If the contact doesn't repond before the timeout,
+						the contact is marked unavailable.
+						If <literal>0</literal> no timeout. Time in seconds.
+					</para></description>
+				</configOption>
 				<configOption name="authenticate_qualify" default="no">
 					<synopsis>Authenticates a qualify request if needed</synopsis>
 					<description><para>
diff --git a/res/res_pjsip/location.c b/res/res_pjsip/location.c
index 73ffdca..1dffee3 100644
--- a/res/res_pjsip/location.c
+++ b/res/res_pjsip/location.c
@@ -188,6 +188,24 @@
 	return contact;
 }
 
+static int permanent_uri_sort_fn(const void *obj_left, const void *obj_right, int flags);
+static int cli_aor_gather_contacts(void *obj, void *arg, int flags);
+
+struct ao2_container *ast_sip_location_retrieve_contacts_from_aor_list(char *aor_list)
+{
+	struct ao2_container *contacts;
+
+	contacts = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK,
+		AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT, permanent_uri_sort_fn, NULL);
+	if (!contacts) {
+		return NULL;
+	}
+
+	ast_sip_for_each_aor(aor_list, cli_aor_gather_contacts, contacts);
+
+	return contacts;
+}
+
 struct ast_sip_contact *ast_sip_location_retrieve_contact(const char *contact_name)
 {
 	return ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "contact", contact_name);
@@ -208,6 +226,7 @@
 	ast_string_field_set(contact, uri, uri);
 	contact->expiration_time = expiration_time;
 	contact->qualify_frequency = aor->qualify_frequency;
+	contact->qualify_timeout = aor->qualify_timeout;
 	contact->authenticate_qualify = aor->authenticate_qualify;
 	if (path_info && aor->support_path) {
 		ast_string_field_set(contact, path, path_info);
@@ -854,6 +873,8 @@
 	ast_sorcery_object_field_register_custom(sorcery, "contact", "expiration_time", "", expiration_str2struct, expiration_struct2str, NULL, 0, 0);
 	ast_sorcery_object_field_register(sorcery, "contact", "qualify_frequency", 0, OPT_UINT_T,
 					  PARSE_IN_RANGE, FLDSET(struct ast_sip_contact, qualify_frequency), 0, 86400);
+	ast_sorcery_object_field_register(sorcery, "contact", "qualify_timeout", "3", OPT_UINT_T,
+					  PARSE_IN_RANGE, FLDSET(struct ast_sip_contact, qualify_timeout), 0, 86400);
 	ast_sorcery_object_field_register(sorcery, "contact", "outbound_proxy", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, outbound_proxy));
 	ast_sorcery_object_field_register(sorcery, "contact", "user_agent", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, user_agent));
 
@@ -862,6 +883,7 @@
 	ast_sorcery_object_field_register(sorcery, "aor", "maximum_expiration", "7200", OPT_UINT_T, 0, FLDSET(struct ast_sip_aor, maximum_expiration));
 	ast_sorcery_object_field_register(sorcery, "aor", "default_expiration", "3600", OPT_UINT_T, 0, FLDSET(struct ast_sip_aor, default_expiration));
 	ast_sorcery_object_field_register(sorcery, "aor", "qualify_frequency", 0, OPT_UINT_T, PARSE_IN_RANGE, FLDSET(struct ast_sip_aor, qualify_frequency), 0, 86400);
+	ast_sorcery_object_field_register(sorcery, "aor", "qualify_timeout", "3", OPT_UINT_T, PARSE_IN_RANGE, FLDSET(struct ast_sip_aor, qualify_timeout), 0, 86400);
 	ast_sorcery_object_field_register(sorcery, "aor", "authenticate_qualify", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_aor, authenticate_qualify));
 	ast_sorcery_object_field_register(sorcery, "aor", "max_contacts", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_aor, max_contacts));
 	ast_sorcery_object_field_register(sorcery, "aor", "remove_existing", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_aor, remove_existing));
diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c
index 0eecb5e..0013a37 100644
--- a/res/res_pjsip/pjsip_configuration.c
+++ b/res/res_pjsip/pjsip_configuration.c
@@ -59,31 +59,66 @@
 static int persistent_endpoint_update_state(void *obj, void *arg, int flags)
 {
 	struct sip_persistent_endpoint *persistent = obj;
+	struct ast_endpoint *endpoint = persistent->endpoint;
 	char *aor = arg;
-	RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup);
-	RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
+	struct ao2_container *contacts;
+	struct ast_json *blob;
+	struct ao2_iterator i;
+	struct ast_sip_contact_wrapper *contact_wrapper;
+	enum ast_endpoint_state state = AST_ENDPOINT_OFFLINE;
 
 	if (!ast_strlen_zero(aor) && !strstr(persistent->aors, aor)) {
 		return 0;
 	}
 
-	if ((contact = ast_sip_location_retrieve_contact_from_aor_list(persistent->aors))) {
-		ast_endpoint_set_state(persistent->endpoint, AST_ENDPOINT_ONLINE);
-		blob = ast_json_pack("{s: s}", "peer_status", "Reachable");
-	} else {
-		ast_endpoint_set_state(persistent->endpoint, AST_ENDPOINT_OFFLINE);
-		blob = ast_json_pack("{s: s}", "peer_status", "Unreachable");
+	/* Find all the contacts for this endpoint.  If ANY are available,
+	 * mark the endpoint as ONLINE.
+	 */
+	contacts = ast_sip_location_retrieve_contacts_from_aor_list(persistent->aors);
+	if (contacts) {
+		i = ao2_iterator_init(contacts, 0);
+		while ((contact_wrapper = ao2_iterator_next(&i))
+			&& state == AST_ENDPOINT_OFFLINE) {
+			struct ast_sip_contact_status *contact_status;
+			const char *contact_id = ast_sorcery_object_get_id(contact_wrapper->contact);
+
+			contact_status = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(),
+				CONTACT_STATUS, contact_id);
+
+			if (contact_status && contact_status->status == AVAILABLE) {
+				state = AST_ENDPOINT_ONLINE;
+			}
+			ao2_cleanup(contact_status);
+			ao2_cleanup(contact_wrapper);
+		}
+		ao2_iterator_destroy(&i);
+		ao2_cleanup(contacts);
 	}
 
-	ast_endpoint_blob_publish(persistent->endpoint, ast_endpoint_state_type(), blob);
+	/* If there was no state change, don't publish anything. */
+	if (ast_endpoint_get_state(endpoint) == state) {
+		return 0;
+	}
 
-	ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "PJSIP/%s", ast_endpoint_get_resource(persistent->endpoint));
+	if (state == AST_ENDPOINT_ONLINE) {
+		ast_endpoint_set_state(endpoint, AST_ENDPOINT_ONLINE);
+		blob = ast_json_pack("{s: s}", "peer_status", "Reachable");
+		ast_verb(1, "Endpoint %s is now Reachable\n", ast_endpoint_get_resource(endpoint));
+	} else {
+		ast_endpoint_set_state(endpoint, AST_ENDPOINT_OFFLINE);
+		blob = ast_json_pack("{s: s}", "peer_status", "Unreachable");
+		ast_verb(1, "Endpoint %s is now Unreachable\n", ast_endpoint_get_resource(endpoint));
+	}
+
+	ast_endpoint_blob_publish(endpoint, ast_endpoint_state_type(), blob);
+	ast_json_unref(blob);
+	ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "PJSIP/%s", ast_endpoint_get_resource(endpoint));
 
 	return 0;
 }
 
 /*! \brief Function called when stuff relating to a contact happens (created/deleted) */
-static void persistent_endpoint_contact_observer(const void *object)
+static void persistent_endpoint_contact_created_observer(const void *object)
 {
 	char *id = ast_strdupa(ast_sorcery_object_get_id(object)), *aor = NULL;
 
@@ -92,12 +127,68 @@
 	ao2_callback(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, aor);
 }
 
+/*! \brief Function called when stuff relating to a contact happens (created/deleted) */
+static void persistent_endpoint_contact_deleted_observer(const void *object)
+{
+	char *id = ast_strdupa(ast_sorcery_object_get_id(object));
+	char *aor = NULL;
+	char *contact = NULL;
+
+	aor = id;
+	/* Dynamic contacts are delimited with ";@" and static ones with "@@" */
+	if ((contact = strstr(id, ";@")) || (contact = strstr(id, "@@"))) {
+		*contact = '\0';
+		contact += 2;
+	} else {
+		contact = id;
+	}
+
+	ast_verb(1, "Contact %s/%s is now Unavailable\n", aor, contact);
+
+	ao2_callback(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, aor);
+}
+
 /*! \brief Observer for contacts so state can be updated on respective endpoints */
 static const struct ast_sorcery_observer state_contact_observer = {
-	.created = persistent_endpoint_contact_observer,
-	.deleted = persistent_endpoint_contact_observer,
+	.created = persistent_endpoint_contact_created_observer,
+	.deleted = persistent_endpoint_contact_deleted_observer,
 };
 
+/*! \brief Function called when stuff relating to a contact status happens (updated) */
+static void persistent_endpoint_contact_status_observer(const void *object)
+{
+	const struct ast_sip_contact_status *contact_status = object;
+	char *id = ast_strdupa(ast_sorcery_object_get_id(object));
+	char *aor = NULL;
+	char *contact = NULL;
+
+	/* If rtt_start is set (this is the outgoing OPTIONS) or
+	 * there's no status change, ignore.
+	 */
+	if (contact_status->rtt_start.tv_sec > 0
+		|| contact_status->status == contact_status->last_status) {
+		return;
+	}
+
+	aor = id;
+	/* Dynamic contacts are delimited with ";@" and static ones with "@@" */
+	if ((contact = strstr(id, ";@")) || (contact = strstr(id, "@@"))) {
+		*contact = '\0';
+		contact += 2;
+	} else {
+		contact = id;
+	}
+
+	ast_verb(1, "Contact %s/%s is now %s\n", aor, contact,
+		contact_status->status == AVAILABLE ? "Available" : "Unavailable");
+
+	ao2_callback(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, aor);
+}
+
+/*! \brief Observer for contacts so state can be updated on respective endpoints */
+static const struct ast_sorcery_observer state_contact_status_observer = {
+	.updated = persistent_endpoint_contact_status_observer,
+};
 
 static int dtmf_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
 {
@@ -1796,6 +1887,7 @@
 	}
 
 	ast_sorcery_observer_add(sip_sorcery, "contact", &state_contact_observer);
+	ast_sorcery_observer_add(sip_sorcery, CONTACT_STATUS, &state_contact_status_observer);
 
 	if (ast_sip_initialize_sorcery_domain_alias()) {
 		ast_log(LOG_ERROR, "Failed to register SIP domain aliases support with sorcery\n");
@@ -1852,6 +1944,8 @@
 
 void ast_res_pjsip_destroy_configuration(void)
 {
+	ast_sorcery_observer_remove(sip_sorcery, CONTACT_STATUS, &state_contact_status_observer);
+	ast_sorcery_observer_remove(sip_sorcery, "contact", &state_contact_observer);
 	ast_sip_destroy_sorcery_global();
 	ast_sip_destroy_sorcery_location();
 	ast_sip_destroy_sorcery_auth();
diff --git a/res/res_pjsip/pjsip_options.c b/res/res_pjsip/pjsip_options.c
index 9794827..ff069ca 100644
--- a/res/res_pjsip/pjsip_options.c
+++ b/res/res_pjsip/pjsip_options.c
@@ -110,24 +110,26 @@
 
 	status = find_or_create_contact_status(contact);
 	if (!status) {
-		return;
-	}
-
-	update = ast_sorcery_alloc(ast_sip_get_sorcery(), CONTACT_STATUS,
-		ast_sorcery_object_get_id(status));
-	if (!update) {
-		ast_log(LOG_ERROR, "Unable to create update ast_sip_contact_status for contact %s\n",
+		ast_log(LOG_ERROR, "Unable to find ast_sip_contact_status for contact %s\n",
 			contact->uri);
-		ao2_ref(status, -1);
 		return;
 	}
 
+	update = ast_sorcery_copy(ast_sip_get_sorcery(), status);
+	ao2_ref(status, -1);
+	if (!update) {
+		ast_log(LOG_ERROR, "Unable to copy ast_sip_contact_status for contact %s\n",
+			contact->uri);
+		return;
+	}
+
+	update->last_status = update->status;
 	update->status = value;
 
 	/* if the contact is available calculate the rtt as
 	   the diff between the last start time and "now" */
 	update->rtt = update->status == AVAILABLE ?
-		ast_tvdiff_us(ast_tvnow(), status->rtt_start) : 0;
+		ast_tvdiff_us(ast_tvnow(), update->rtt_start) : 0;
 
 	update->rtt_start = ast_tv(0, 0);
 
@@ -137,7 +139,6 @@
 	}
 
 	ao2_ref(update, -1);
-	ao2_ref(status, -1);
 }
 
 /*!
@@ -152,15 +153,16 @@
 
 	status = find_or_create_contact_status(contact);
 	if (!status) {
+		ast_log(LOG_ERROR, "Unable to find ast_sip_contact_status for contact %s\n",
+			contact->uri);
 		return;
 	}
 
-	update = ast_sorcery_alloc(ast_sip_get_sorcery(), CONTACT_STATUS,
-		ast_sorcery_object_get_id(status));
+	update = ast_sorcery_copy(ast_sip_get_sorcery(), status);
+	ao2_ref(status, -1);
 	if (!update) {
-		ast_log(LOG_ERROR, "Unable to create update ast_sip_contact_status for contact %s\n",
+		ast_log(LOG_ERROR, "Unable to copy ast_sip_contact_status for contact %s\n",
 			contact->uri);
-		ao2_ref(status, -1);
 		return;
 	}
 
@@ -172,7 +174,6 @@
 	}
 
 	ao2_ref(update, -1);
-	ao2_ref(status, -1);
 }
 
 /*!
@@ -320,7 +321,7 @@
 	init_start_time(contact);
 
 	ao2_ref(contact, +1);
-	if (ast_sip_send_request(tdata, NULL, endpoint_local, contact, qualify_contact_cb)
+	if (ast_sip_send_out_of_dialog_request(tdata, endpoint_local, contact->qualify_timeout * 1000, contact, qualify_contact_cb, NULL)
 		!= PJ_SUCCESS) {
 		ast_log(LOG_ERROR, "Unable to send request to qualify contact %s\n",
 			contact->uri);
@@ -923,6 +924,32 @@
 	return CMP_MATCH;
 }
 
+static int rtt_start_handler(const struct aco_option *opt,
+	struct ast_variable *var, void *obj)
+{
+	struct ast_sip_contact_status *status = obj;
+	long int sec, usec;
+
+	if (sscanf(var->value, "%ld.%06ld", &sec, &usec) != 2) {
+		return -1;
+	}
+
+	status->rtt_start = ast_tv(sec, usec);
+
+	return 0;
+}
+
+static int rtt_start_to_str(const void *obj, const intptr_t *args, char **buf)
+{
+	const struct ast_sip_contact_status *status = obj;
+
+	if (ast_asprintf(buf, "%ld.%06ld", status->rtt_start.tv_sec, status->rtt_start.tv_usec) == -1) {
+		return -1;
+	}
+
+	return 0;
+}
+
 int ast_sip_initialize_sorcery_qualify(void)
 {
 	struct ast_sorcery *sorcery = ast_sip_get_sorcery();
@@ -936,10 +963,14 @@
 		return -1;
 	}
 
-	ast_sorcery_object_field_register_nodoc(sorcery, CONTACT_STATUS, "rtt", "0", OPT_UINT_T,
-					  1, FLDSET(struct ast_sip_contact_status, status));
-	ast_sorcery_object_field_register_nodoc(sorcery, CONTACT_STATUS, "rtt", "0", OPT_UINT_T,
-					  1, FLDSET(struct ast_sip_contact_status, rtt));
+	ast_sorcery_object_field_register_nodoc(sorcery, CONTACT_STATUS, "last_status",
+		"0", OPT_UINT_T, 1, FLDSET(struct ast_sip_contact_status, last_status));
+	ast_sorcery_object_field_register_nodoc(sorcery, CONTACT_STATUS, "status",
+		"0", OPT_UINT_T, 1, FLDSET(struct ast_sip_contact_status, status));
+	ast_sorcery_object_field_register_custom_nodoc(sorcery, CONTACT_STATUS, "rtt_start",
+		"0.0", rtt_start_handler, rtt_start_to_str, NULL, 0, 0);
+	ast_sorcery_object_field_register_nodoc(sorcery, CONTACT_STATUS, "rtt",
+		"0", OPT_UINT_T, 1, FLDSET(struct ast_sip_contact_status, rtt));
 
 	return 0;
 }
@@ -951,6 +982,7 @@
 	int initial_interval;
 
 	contact->qualify_frequency = aor->qualify_frequency;
+	contact->qualify_timeout = aor->qualify_timeout;
 	contact->authenticate_qualify = aor->authenticate_qualify;
 
 	/* Delay initial qualification by a random fraction of the specified interval */

-- 
To view, visit https://gerrit.asterisk.org/47
To unsubscribe, visit https://gerrit.asterisk.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Id0ce0528e58014da1324856ea537e7765466044a
Gerrit-PatchSet: 1
Gerrit-Project: asterisk
Gerrit-Branch: master
Gerrit-Owner: George Joseph <george.joseph at fairview5.com>



More information about the asterisk-dev mailing list