<p>Joshua Colp <strong>merged</strong> this change.</p><p><a href="https://gerrit.asterisk.org/7710">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  Corey Farrell: Looks good to me, but someone else must approve
  Joshua Colp: Looks good to me, but someone else must approve; Approved for Submit
  Kevin Harwell: Looks good to me, approved

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">pjsip: Rewrite OPTIONS support with new eyes.<br><br>The OPTIONS support in PJSIP has organically grown, like many things in<br>Asterisk.  It has been tweaked, changed, and adapted based on situations<br>run into.  Unfortunately this has taken its toll.  Configuration file<br>based objects have poor performance and even dynamic ones aren't that<br>great.<br><br>This change scraps the existing code and starts fresh with new eyes.  It<br>leverages all of the APIs made available such as sorcery observers and<br>serializers to provide a better implementation.<br><br>1.  The state of contacts, AORs, and endpoints relevant to the qualify<br>process is maintained.  This state can be updated by external forces (such<br>as a device registering/unregistering) and also the reload process.  This<br>state also includes the association between endpoints and AORs.<br><br>2.  AORs are scheduled and not contacts.  This reduces the amount of work<br>spent juggling scheduled items.<br><br>3.  Manipulation of which AORs are being qualified and the endpoint states<br>all occur within a serializer to reduce the conflict that can occur with<br>multiple threads attempting to modify things.<br><br>4.  Operations regarding an AOR use a serializer specific to that AOR.<br><br>5.  AORs and endpoint state act as state compositors.  They take input<br>from lower level objects (contacts feed AORs, AORs feed endpoint state)<br>and determine if a sufficient enough change has occurred to be fed further<br>up the chain.<br><br>6.  Realtime is supported by using observers to know when a contact has<br>been registered.  If state does not exist for the associated AOR then it<br>is retrieved and becomes active as appropriate.<br><br>The end result of all of this is best shown with a configuration file of<br>3000 endpoints each with an AOR that has a static contact.  In the old<br>code it would take over a minute to load and use all 8 of my cores.  This<br>new code takes 2-3 seconds and barely touches the CPU even while dealing<br>with all of the OPTIONS requests.<br><br>ASTERISK-26806<br><br>Change-Id: I6a5ebbfca9001dfe933eaeac4d3babd8d2e6f082<br>---<br>M funcs/func_pjsip_contact.c<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/location.c<br>M res/res_pjsip/pjsip_configuration.c<br>M res/res_pjsip/pjsip_options.c<br>7 files changed, 2,460 insertions(+), 1,461 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">diff --git a/funcs/func_pjsip_contact.c b/funcs/func_pjsip_contact.c<br>index 005f29b..6904df0 100644<br>--- a/funcs/func_pjsip_contact.c<br>+++ b/funcs/func_pjsip_contact.c<br>@@ -144,12 +144,12 @@<br>           return -1;<br>    }<br> <br>- contact_status = ast_sorcery_retrieve_by_id(pjsip_sorcery, CONTACT_STATUS, ast_sorcery_object_get_id(contact_obj));<br>+  contact_status = ast_sip_get_contact_status(contact_obj);<br> <br>  if (!strcmp(args.field_name, "status")) {<br>-          ast_str_set(buf, len, "%s", ast_sip_get_contact_status_label(contact_status->status));<br>+          ast_str_set(buf, len, "%s", ast_sip_get_contact_status_label(contact_status ? contact_status->status : UNKNOWN));<br>        } else if (!strcmp(args.field_name, "rtt")) {<br>-              if (contact_status->status == UNKNOWN) {<br>+          if (!contact_status || contact_status->status == UNKNOWN) {<br>                        ast_str_set(buf, len, "%s", "N/A");<br>               } else {<br>                      ast_str_set(buf, len, "%" PRId64, contact_status->rtt);<br>diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h<br>index d9b4536..7177ceb 100644<br>--- a/include/asterisk/res_pjsip.h<br>+++ b/include/asterisk/res_pjsip.h<br>@@ -283,8 +283,6 @@<br>    int prune_on_boot;<br> };<br> <br>-#define CONTACT_STATUS "contact_status"<br>-<br> /*!<br>  * \brief Status type for a contact.<br>  */<br>@@ -303,21 +301,20 @@<br>  *         if available.<br>  */<br> struct ast_sip_contact_status {<br>-       SORCERY_OBJECT(details);<br>-     /*! Current status for a contact (default - unavailable) */<br>-  enum ast_sip_contact_status_type status;<br>-     /*! The round trip start time set before sending a qualify request */<br>-        struct timeval rtt_start;<br>+    AST_DECLARE_STRING_FIELDS(<br>+           /*! The original contact's URI */<br>+                AST_STRING_FIELD(uri);<br>+               /*! The name of the aor this contact_status belongs to */<br>+            AST_STRING_FIELD(aor);<br>+       );<br>    /*! The round trip time in microseconds */<br>    int64_t rtt;<br>+ /*! Current status for a contact (default - unavailable) */<br>+  enum ast_sip_contact_status_type status;<br>      /*! Last status for a contact (default - unavailable) */<br>      enum ast_sip_contact_status_type last_status;<br>-        /*! The name of the aor this contact_status belongs to */<br>-    char *aor;<br>-   /*! The original contact's URI */<br>-        char *uri;<br>-   /*! TRUE if the contact was refreshed. e.g., re-registered */<br>-        unsigned int refresh:1;<br>+      /*! Name of the contact */<br>+   char name[0];<br> };<br> <br> /*!<br>@@ -1053,7 +1050,7 @@<br> /*!<br>  * \brief Change state of a persistent endpoint.<br>  *<br>- * \param endpoint The SIP endpoint name to change state.<br>+ * \param endpoint_name The SIP endpoint name to change state.<br>  * \param state The new state<br>  * \retval 0 Success<br>  * \retval -1 Endpoint not found<br>@@ -1061,6 +1058,26 @@<br> int ast_sip_persistent_endpoint_update_state(const char *endpoint_name, enum ast_endpoint_state state);<br> <br> /*!<br>+ * \brief Publish the change of state for a contact.<br>+ *<br>+ * \param endpoint_name The SIP endpoint name.<br>+ * \param contact_status The contact status.<br>+ */<br>+void ast_sip_persistent_endpoint_publish_contact_state(const char *endpoint_name, const struct ast_sip_contact_status *contact_status);<br>+<br>+/*!<br>+ * \brief Retrieve the current status for a contact.<br>+ *<br>+ * \param contact The contact.<br>+ *<br>+ * \retval non-NULL Success<br>+ * \retval NULL Status information not found<br>+ *<br>+ * \note The returned contact status object is immutable.<br>+ */<br>+struct ast_sip_contact_status *ast_sip_get_contact_status(const struct ast_sip_contact *contact);<br>+<br>+/*!<br>  * \brief Get a pointer to the PJSIP endpoint.<br>  *<br>  * This is useful when modules have specific information they need<br>diff --git a/res/res_pjsip.c b/res/res_pjsip.c<br>index 78a346c..b5f8cbd 100644<br>--- a/res/res_pjsip.c<br>+++ b/res/res_pjsip.c<br>@@ -4931,8 +4931,12 @@<br>    }<br> <br>  ast_sip_initialize_dns();<br>-<br>  ast_sip_initialize_global_headers();<br>+<br>+      if (ast_res_pjsip_preinit_options_handling()) {<br>+              ast_log(LOG_ERROR, "Failed to pre-initialize OPTIONS handling. Aborting load\n");<br>+          goto error;<br>+  }<br> <br>  if (ast_res_pjsip_initialize_configuration(ast_module_info)) {<br>                ast_log(LOG_ERROR, "Failed to initialize SIP configuration. Aborting load\n");<br>@@ -4959,7 +4963,10 @@<br>              goto error;<br>   }<br> <br>- ast_res_pjsip_init_options_handling(0);<br>+      if (ast_res_pjsip_init_options_handling(0)) {<br>+                ast_log(LOG_ERROR, "Failed to initialize OPTIONS handling. Aborting load\n");<br>+              goto error;<br>+  }<br> <br>  if (ast_res_pjsip_init_message_filter()) {<br>            ast_log(LOG_ERROR, "Failed to initialize message IP updating. Aborting load\n");<br>diff --git a/res/res_pjsip/include/res_pjsip_private.h b/res/res_pjsip/include/res_pjsip_private.h<br>index 8c271eb..0c9e8a0 100644<br>--- a/res/res_pjsip/include/res_pjsip_private.h<br>+++ b/res/res_pjsip/include/res_pjsip_private.h<br>@@ -193,6 +193,15 @@<br> <br> /*!<br>  * \internal<br>+ * \brief Pre-initialize OPTIONS request handling.<br>+ *<br>+ * \retval 0 on success<br>+ * \retval other on failure<br>+ */<br>+int ast_res_pjsip_preinit_options_handling(void);<br>+<br>+/*!<br>+ * \internal<br>  * \brief Initialize OPTIONS request handling.<br>  *<br>  * XXX This currently includes qualifying peers. It shouldn't.<br>diff --git a/res/res_pjsip/location.c b/res/res_pjsip/location.c<br>index 3abba3b..eadc115 100644<br>--- a/res/res_pjsip/location.c<br>+++ b/res/res_pjsip/location.c<br>@@ -178,7 +178,7 @@<br>    struct ast_sip_contact_status *status;<br>        int unreachable;<br> <br>-  status = ast_res_pjsip_find_or_create_contact_status(contact);<br>+       status = ast_sip_get_contact_status(contact);<br>         if (!status) {<br>                return 0;<br>     }<br>@@ -1076,7 +1076,7 @@<br>      ast_assert(contact->uri != NULL);<br>  ast_assert(context->output_buffer != NULL);<br> <br>-    status = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), CONTACT_STATUS, contact_id);<br>+      status = ast_sip_get_contact_status(contact);<br> <br>      indent = CLI_INDENT_TO_SPACES(context->indent_level);<br>      flexwidth = CLI_LAST_TABSTOP - indent - 9 - strlen(contact->aor) + 1;<br>diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c<br>index 5e5d40c..9426e92 100644<br>--- a/res/res_pjsip/pjsip_configuration.c<br>+++ b/res/res_pjsip/pjsip_configuration.c<br>@@ -41,8 +41,6 @@<br> struct sip_persistent_endpoint {<br>   /*! \brief Asterisk endpoint itself */<br>        struct ast_endpoint *endpoint;<br>-       /*! \brief AORs that we should react to */<br>-   char *aors;<br> };<br> <br> /*! \brief Container for persistent endpoint information */<br>@@ -68,242 +66,6 @@<br> <br>     return !strcmp(ast_endpoint_get_resource(persistent1->endpoint), id) ? CMP_MATCH | CMP_STOP : 0;<br> }<br>-<br>-/*! \brief Internal function for changing the state of an endpoint */<br>-static void endpoint_update_state(struct ast_endpoint *endpoint, enum ast_endpoint_state state)<br>-{<br>-     struct ast_json *blob;<br>-       char *regcontext;<br>-<br>- /* If there was no state change, don't publish anything. */<br>-      if (ast_endpoint_get_state(endpoint) == state) {<br>-             return;<br>-      }<br>-<br>- regcontext = ast_sip_get_regcontext();<br>-<br>-    if (state == AST_ENDPOINT_ONLINE) {<br>-          ast_endpoint_set_state(endpoint, AST_ENDPOINT_ONLINE);<br>-               blob = ast_json_pack("{s: s}", "peer_status", "Reachable");<br>-<br>-         if (!ast_strlen_zero(regcontext)) {<br>-                  if (!ast_exists_extension(NULL, regcontext, ast_endpoint_get_resource(endpoint), 1, NULL)) {<br>-                         ast_add_extension(regcontext, 1, ast_endpoint_get_resource(endpoint), 1, NULL, NULL,<br>-                                 "Noop", ast_strdup(ast_endpoint_get_resource(endpoint)), ast_free_ptr, "SIP");<br>-                   }<br>-            }<br>-<br>-         ast_verb(2, "Endpoint %s is now Reachable\n", ast_endpoint_get_resource(endpoint));<br>-        } else {<br>-             ast_endpoint_set_state(endpoint, AST_ENDPOINT_OFFLINE);<br>-              blob = ast_json_pack("{s: s}", "peer_status", "Unreachable");<br>-<br>-               if (!ast_strlen_zero(regcontext)) {<br>-                  struct pbx_find_info q = { .stacklen = 0 };<br>-<br>-                       if (pbx_find_extension(NULL, NULL, &q, regcontext, ast_endpoint_get_resource(endpoint), 1, NULL, "", E_MATCH)) {<br>-                               ast_context_remove_extension(regcontext, ast_endpoint_get_resource(endpoint), 1, NULL);<br>-                      }<br>-            }<br>-<br>-         ast_verb(2, "Endpoint %s is now Unreachable\n", ast_endpoint_get_resource(endpoint));<br>-      }<br>-<br>- ast_free(regcontext);<br>-<br>-     ast_endpoint_blob_publish(endpoint, ast_endpoint_state_type(), blob);<br>-        ast_json_unref(blob);<br>-        ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "PJSIP/%s", ast_endpoint_get_resource(endpoint));<br>-}<br>-<br>-static void endpoint_publish_contact_status(struct ast_endpoint *endpoint, struct ast_sip_contact_status *contact)<br>-{<br>-  struct ast_json *blob;<br>-       char rtt[32];<br>-<br>-     snprintf(rtt, sizeof(rtt), "%" PRId64, contact->rtt);<br>-   blob = ast_json_pack("{s: s, s: s, s: s, s: s, s: s}",<br>-             "contact_status", ast_sip_get_contact_status_label(contact->status),<br>-            "aor", contact->aor,<br>-            "uri", contact->uri,<br>-            "roundtrip_usec", rtt,<br>-             "endpoint_name", ast_endpoint_get_resource(endpoint));<br>-     if (blob) {<br>-          ast_endpoint_blob_publish(endpoint, ast_endpoint_contact_state_type(), blob);<br>-                ast_json_unref(blob);<br>-        }<br>-}<br>-<br>-/*! \brief Callback function for publishing the status of an endpoint */<br>-static int persistent_endpoint_publish_status(void *obj, void *arg, int flags)<br>-{<br>-     struct sip_persistent_endpoint *persistent = obj;<br>-    struct ast_endpoint *endpoint = persistent->endpoint;<br>-     struct ast_sip_contact_status *status = arg;<br>-<br>-      /* If the status' aor isn't one of the endpoint's, we skip */<br>-    if (!strstr(persistent->aors, status->aor)) {<br>-          return 0;<br>-    }<br>-<br>- endpoint_publish_contact_status(endpoint, status);<br>-   return 0;<br>-}<br>-<br>-/*! \brief Callback function for changing the state of an endpoint */<br>-static int persistent_endpoint_update_state(void *obj, void *arg, int flags)<br>-{<br>-  struct sip_persistent_endpoint *persistent = obj;<br>-    struct ast_endpoint *endpoint = persistent->endpoint;<br>-     struct ast_sip_contact_status *status = arg;<br>- struct ao2_container *contacts;<br>-      struct ao2_iterator iter;<br>-    struct ast_sip_contact *contact;<br>-     enum ast_endpoint_state state = AST_ENDPOINT_OFFLINE;<br>-<br>-     /* If the status' aor isn't one of the endpoint's, we skip */<br>-    if (!strstr(persistent->aors, status->aor)) {<br>-          return 0;<br>-    }<br>-<br>- endpoint_publish_contact_status(endpoint, status);<br>-<br>-        /* Find all the contacts for this endpoint.  If ANY are available,<br>-    * mark the endpoint as ONLINE.<br>-       */<br>-  contacts = ast_sip_location_retrieve_contacts_from_aor_list(persistent->aors);<br>-    if (contacts) {<br>-              iter = ao2_iterator_init(contacts, 0);<br>-               while (state == AST_ENDPOINT_OFFLINE && (contact = ao2_iterator_next(&iter))) {<br>-                  struct ast_sip_contact_status *contact_status;<br>-                       const char *contact_id = ast_sorcery_object_get_id(contact);<br>-<br>-                      contact_status = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(),<br>-                           CONTACT_STATUS, contact_id);<br>-                 if (contact_status && contact_status->status != UNAVAILABLE) {<br>-                            state = AST_ENDPOINT_ONLINE;<br>-                 }<br>-                    ao2_cleanup(contact_status);<br>-                 ao2_ref(contact, -1);<br>-                }<br>-            ao2_iterator_destroy(&iter);<br>-             ao2_ref(contacts, -1);<br>-       }<br>-<br>- endpoint_update_state(endpoint, state);<br>-<br>-   return 0;<br>-}<br>-<br>-/*! \brief Function called when a contact is created */<br>-static void persistent_endpoint_contact_created_observer(const void *object)<br>-{<br>-        const struct ast_sip_contact *contact = object;<br>-      struct ast_sip_contact_status *contact_status;<br>-<br>-    contact_status = ast_sorcery_alloc(ast_sip_get_sorcery(), CONTACT_STATUS,<br>-            ast_sorcery_object_get_id(contact));<br>- if (!contact_status) {<br>-               ast_log(LOG_ERROR, "Unable to create ast_sip_contact_status for contact %s/%s\n",<br>-                  contact->aor, contact->uri);<br>-           return;<br>-      }<br>-    contact_status->uri = ast_strdup(contact->uri);<br>-        if (!contact_status->uri) {<br>-               ao2_cleanup(contact_status);<br>-         return;<br>-      }<br>-    contact_status->status = CREATED;<br>-<br>-      ast_verb(2, "Contact %s/%s has been created\n", contact->aor, contact->uri);<br>-<br>-      ao2_callback(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, contact_status);<br>-    ao2_cleanup(contact_status);<br>-}<br>-<br>-/*! \brief Function called when a contact is deleted */<br>-static void persistent_endpoint_contact_deleted_observer(const void *object)<br>-{<br>-     const struct ast_sip_contact *contact = object;<br>-      struct ast_sip_contact_status *contact_status;<br>-<br>-    contact_status = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), CONTACT_STATUS, ast_sorcery_object_get_id(contact));<br>-      if (!contact_status) {<br>-               ast_log(LOG_ERROR, "Unable to find ast_sip_contact_status for contact %s/%s\n",<br>-                    contact->aor, contact->uri);<br>-           return;<br>-      }<br>-<br>- ast_verb(2, "Contact %s/%s has been deleted\n", contact->aor, contact->uri);<br>- ast_statsd_log_string_va("PJSIP.contacts.states.%s", AST_STATSD_GAUGE,<br>-             "-1", 1.0, ast_sip_get_contact_status_label(contact_status->status));<br>-   ast_statsd_log_string_va("PJSIP.contacts.states.%s", AST_STATSD_GAUGE,<br>-             "+1", 1.0, ast_sip_get_contact_status_label(REMOVED));<br>-<br>-  ao2_callback(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, contact_status);<br>-    ast_sorcery_delete(ast_sip_get_sorcery(), contact_status);<br>-   ao2_cleanup(contact_status);<br>-}<br>-<br>-/*! \brief Observer for contacts so state can be updated on respective endpoints */<br>-static const struct ast_sorcery_observer state_contact_observer = {<br>-      .created = persistent_endpoint_contact_created_observer,<br>-     .deleted = persistent_endpoint_contact_deleted_observer,<br>-};<br>-<br>-/*! \brief Function called when a contact_status is updated */<br>-static void persistent_endpoint_contact_status_observer(const void *object)<br>-{<br>-  struct ast_sip_contact_status *contact_status = (struct ast_sip_contact_status *)object;<br>-<br>-  if (contact_status->refresh) {<br>-            /* We are only re-publishing the contact status. */<br>-          ao2_callback(persistent_endpoints, OBJ_NODATA,<br>-                       persistent_endpoint_publish_status, contact_status);<br>-         return;<br>-      }<br>-<br>- /* If rtt_start is set (this is the outgoing OPTIONS), ignore. */<br>-    if (contact_status->rtt_start.tv_sec > 0) {<br>-            return;<br>-      }<br>-<br>- if (contact_status->status != contact_status->last_status) {<br>-           ast_verb(3, "Contact %s/%s is now %s.  RTT: %.3f msec\n",<br>-                  contact_status->aor, contact_status->uri,<br>-                      ast_sip_get_contact_status_label(contact_status->status),<br>-                 contact_status->rtt / 1000.0);<br>-<br>-         ast_statsd_log_string_va("PJSIP.contacts.states.%s", AST_STATSD_GAUGE,<br>-                     "-1", 1.0, ast_sip_get_contact_status_label(contact_status->last_status));<br>-              ast_statsd_log_string_va("PJSIP.contacts.states.%s", AST_STATSD_GAUGE,<br>-                     "+1", 1.0, ast_sip_get_contact_status_label(contact_status->status));<br>-<br>-                ast_test_suite_event_notify("AOR_CONTACT_UPDATE",<br>-                  "Contact: %s\r\n"<br>-                  "Status: %s",<br>-                      ast_sorcery_object_get_id(contact_status),<br>-                   ast_sip_get_contact_status_label(contact_status->status));<br>-<br>-             ao2_callback(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state,<br>-                     contact_status);<br>-     } else {<br>-             ast_debug(3, "Contact %s/%s status didn't change: %s, RTT: %.3f msec\n",<br>-                       contact_status->aor, contact_status->uri,<br>-                      ast_sip_get_contact_status_label(contact_status->status),<br>-                 contact_status->rtt / 1000.0);<br>-    }<br>-<br>- ast_statsd_log_full_va("PJSIP.contacts.%s.rtt", AST_STATSD_TIMER,<br>-          contact_status->status != AVAILABLE ? -1 : contact_status->rtt / 1000,<br>-         1.0,<br>-         ast_sorcery_object_get_id(contact_status));<br>-}<br>-<br>-/*! \brief Observer for contacts so state can be updated on respective endpoints */<br>-static const struct ast_sorcery_observer state_contact_status_observer = {<br>-        .updated = persistent_endpoint_contact_status_observer,<br>-};<br> <br> static void endpoint_deleted_observer(const void *object)<br> {<br>@@ -1347,21 +1109,89 @@<br>      struct sip_persistent_endpoint *persistent = obj;<br> <br>  ast_endpoint_shutdown(persistent->endpoint);<br>-      ast_free(persistent->aors);<br> }<br> <br> int ast_sip_persistent_endpoint_update_state(const char *endpoint_name, enum ast_endpoint_state state)<br> {<br>    struct sip_persistent_endpoint *persistent;<br>+  struct ast_json *blob;<br>+       char *regcontext;<br> <br>- ao2_lock(persistent_endpoints);<br>-      persistent = ao2_find(persistent_endpoints, endpoint_name, OBJ_SEARCH_KEY | OBJ_NOLOCK);<br>-     if (persistent) {<br>-            endpoint_update_state(persistent->endpoint, state);<br>-               ao2_ref(persistent, -1);<br>+     persistent = ao2_find(persistent_endpoints, endpoint_name, OBJ_SEARCH_KEY);<br>+  if (!persistent) {<br>+           return -1;<br>    }<br>-    ao2_unlock(persistent_endpoints);<br>-    return persistent ? 0 : -1;<br>+<br>+       /* If there was no state change, don't publish anything. */<br>+      if (ast_endpoint_get_state(persistent->endpoint) == state) {<br>+              ao2_ref(persistent, -1);<br>+             return 0;<br>+    }<br>+<br>+ regcontext = ast_sip_get_regcontext();<br>+<br>+    if (state == AST_ENDPOINT_ONLINE) {<br>+          ast_endpoint_set_state(persistent->endpoint, AST_ENDPOINT_ONLINE);<br>+                blob = ast_json_pack("{s: s}", "peer_status", "Reachable");<br>+<br>+         if (!ast_strlen_zero(regcontext)) {<br>+                  if (!ast_exists_extension(NULL, regcontext, ast_endpoint_get_resource(persistent->endpoint), 1, NULL)) {<br>+                          ast_add_extension(regcontext, 1, ast_endpoint_get_resource(persistent->endpoint), 1, NULL, NULL,<br>+                                  "Noop", ast_strdup(ast_endpoint_get_resource(persistent->endpoint)), ast_free_ptr, "SIP");<br>+                    }<br>+            }<br>+<br>+         ast_verb(2, "Endpoint %s is now Reachable\n", ast_endpoint_get_resource(persistent->endpoint));<br>+ } else {<br>+             ast_endpoint_set_state(persistent->endpoint, AST_ENDPOINT_OFFLINE);<br>+               blob = ast_json_pack("{s: s}", "peer_status", "Unreachable");<br>+<br>+               if (!ast_strlen_zero(regcontext)) {<br>+                  struct pbx_find_info q = { .stacklen = 0 };<br>+<br>+                       if (pbx_find_extension(NULL, NULL, &q, regcontext, ast_endpoint_get_resource(persistent->endpoint), 1, NULL, "", E_MATCH)) {<br>+                                ast_context_remove_extension(regcontext, ast_endpoint_get_resource(persistent->endpoint), 1, NULL);<br>+                       }<br>+            }<br>+<br>+         ast_verb(2, "Endpoint %s is now Unreachable\n", ast_endpoint_get_resource(persistent->endpoint));<br>+       }<br>+<br>+ ast_free(regcontext);<br>+<br>+     ast_endpoint_blob_publish(persistent->endpoint, ast_endpoint_state_type(), blob);<br>+ ast_json_unref(blob);<br>+        ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "PJSIP/%s", ast_endpoint_get_resource(persistent->endpoint));<br>+<br>+        ao2_ref(persistent, -1);<br>+<br>+  return 0;<br>+}<br>+<br>+void ast_sip_persistent_endpoint_publish_contact_state(const char *endpoint_name, const struct ast_sip_contact_status *contact_status)<br>+{<br>+        struct sip_persistent_endpoint *persistent;<br>+  struct ast_json *blob;<br>+       char rtt[32];<br>+<br>+     persistent = ao2_find(persistent_endpoints, endpoint_name, OBJ_SEARCH_KEY);<br>+  if (!persistent) {<br>+           return;<br>+      }<br>+<br>+ snprintf(rtt, sizeof(rtt), "%" PRId64, contact_status->rtt);<br>+    blob = ast_json_pack("{s: s, s: s, s: s, s: s, s: s}",<br>+             "contact_status", ast_sip_get_contact_status_label(contact_status->status),<br>+             "aor", contact_status->aor,<br>+             "uri", contact_status->uri,<br>+             "roundtrip_usec", rtt,<br>+             "endpoint_name", ast_endpoint_get_resource(persistent->endpoint));<br>+      if (blob) {<br>+          ast_endpoint_blob_publish(persistent->endpoint, ast_endpoint_contact_state_type(), blob);<br>+         ast_json_unref(blob);<br>+        }<br>+<br>+ ao2_ref(persistent, -1);<br> }<br> <br> /*! \brief Internal function which finds (or creates) persistent endpoint information */<br>@@ -1385,22 +1215,9 @@<br>                    return NULL;<br>          }<br> <br>-         persistent->aors = ast_strdup(endpoint->aors);<br>-         if (!persistent->aors) {<br>-                  return NULL;<br>-         }<br>-<br>          ast_endpoint_set_state(persistent->endpoint, AST_ENDPOINT_OFFLINE);<br> <br>             ao2_link_flags(persistent_endpoints, persistent, OBJ_NOLOCK);<br>-        } else if (strcmp(persistent->aors, endpoint->aors)) {<br>-         char *new_aors = ast_strdup(endpoint->aors);<br>-<br>-           /* make sure we don't NULL persistent->aors if allocation fails. */<br>-           if (new_aors) {<br>-                      ast_free(persistent->aors);<br>-                       persistent->aors = new_aors;<br>-              }<br>     }<br> <br>  ao2_ref(persistent->endpoint, +1);<br>@@ -2052,16 +1869,7 @@<br>                 return -1;<br>    }<br> <br>- if (ast_sip_initialize_sorcery_qualify()) {<br>-          ast_log(LOG_ERROR, "Failed to register SIP qualify support with sorcery\n");<br>-               ast_sorcery_unref(sip_sorcery);<br>-              sip_sorcery = NULL;<br>-          return -1;<br>-   }<br>-<br>  ast_sorcery_observer_add(sip_sorcery, "endpoint", &endpoint_observers);<br>-        ast_sorcery_observer_add(sip_sorcery, "contact", &state_contact_observer);<br>-     ast_sorcery_observer_add(sip_sorcery, CONTACT_STATUS, &state_contact_status_observer);<br> <br>         if (ast_sip_initialize_sorcery_domain_alias()) {<br>              ast_log(LOG_ERROR, "Failed to register SIP domain aliases support with sorcery\n");<br>@@ -2110,8 +1918,6 @@<br>          return;<br>       }<br> <br>- ast_sorcery_observer_remove(sip_sorcery, CONTACT_STATUS, &state_contact_status_observer);<br>-        ast_sorcery_observer_remove(sip_sorcery, "contact", &state_contact_observer);<br>   ast_sip_destroy_sorcery_global();<br>     ast_sip_destroy_sorcery_location();<br>   ast_sip_destroy_sorcery_auth();<br>diff --git a/res/res_pjsip/pjsip_options.c b/res/res_pjsip/pjsip_options.c<br>index a0352a6..d05d7bd 100644<br>--- a/res/res_pjsip/pjsip_options.c<br>+++ b/res/res_pjsip/pjsip_options.c<br>@@ -1,9 +1,10 @@<br> /*<br>  * Asterisk -- An open source telephony toolkit.<br>  *<br>- * Copyright (C) 2013, Digium, Inc.<br>+ * Copyright (C) 2018, Digium, Inc.<br>  *<br>- * Matt Jordan <mjordan@digium.com><br>+ * Joshua Colp <jcolp@digium.com><br>+ * Richard Mudgett <rmudgett@digium.com><br>  *<br>  * See http://www.asterisk.org for more information about<br>  * the Asterisk project. Please do not directly contact<br>@@ -32,674 +33,184 @@<br> #include "asterisk/statsd.h"<br> #include "include/res_pjsip_private.h"<br> #include "asterisk/taskprocessor.h"<br>+#include "asterisk/threadpool.h"<br>+<br>+/*<br>+ * This implementation for OPTIONS support is based around the idea<br>+ * that realistically an AOR generally has very few contacts and is<br>+ * referenced by only a few endpoints. While it is perfectly fine for<br>+ * use in opposite scenarios it works best in the above case. It is<br>+ * also not shy to keeping state but it is reactive to outside changes<br>+ * so it can be updated.<br>+ *<br>+ * The lowest level object in here is a contact and its associated<br>+ * contact status. The result of an OPTIONS request to a contact is<br>+ * reflected in the contact status. The scheduling of these OPTIONS<br>+ * request is driven by the AOR. The AOR periodicially (according to<br>+ * configuration) sends OPTIONS requests out to any contacts<br>+ * associated with it. Contacts themselves are not individually<br>+ * scheduled. Contacts can be added or deleted as appropriate with no<br>+ * requirement to reschedule.<br>+ *<br>+ * The next level object up is the AOR itself. The result of a contact<br>+ * status change is fed into it and the result composited with all<br>+ * other contacts. This may result in the AOR itself changing state<br>+ * (it can be either AVAILABLE or UNAVAILABLE).<br>+ *<br>+ * The highest level object up is the endpoint state compositor (ESC).<br>+ * The result of AOR state changes is fed into it and the result<br>+ * composited with all other referenced AORs. This may result in the<br>+ * endpoint itself changing state (it can be either ONLINE or<br>+ * OFFLINE).  If this occurs the permanent endpoint is updated to<br>+ * reflect it.<br>+ *<br>+ * The threading model errs on the side of a world where things are<br>+ * not constantly changing. That is: A world where AORs and endpoints<br>+ * are not being constantly added/removed. This more closely mirrors<br>+ * the usage of the vast majority of people. This scenario can still<br>+ * be done but it may not be applied immediately.<br>+ *<br>+ * Manipulation of which AORs, endpoint state compositors, and<br>+ * contacts exist is done within a single serializer. This ensures<br>+ * that no matter the source threads order is preserved and you won't<br>+ * get into a weird situation where things are referencing other<br>+ * things that should have already been destroyed.<br>+ *<br>+ * Operations which impact the state of an AOR are done within a<br>+ * serializer that is specific to the AOR. This includes the result of<br>+ * a contact status change. This change is queued and executed on the<br>+ * AOR serializer afterwards.<br>+ *<br>+ * Operations which impact an endpoint state compositor are protected<br>+ * by a lock. This is done as the endpoint state compositor usage is<br>+ * minimal and the overhead of using a serializer and queueing things<br>+ * is not warranted.<br>+ *<br>+ * AORs which do not have a qualify frequency are also kept in here<br>+ * but do not require the same criteria as qualified AORs to be<br>+ * considered available. In their case as long as at least 1 contact<br>+ * is configured on the AOR (or added to it by registration) it is<br>+ * considered available.<br>+ */<br> <br> #define DEFAULT_LANGUAGE "en"<br> #define DEFAULT_ENCODING "text/plain"<br>-#define QUALIFIED_BUCKETS 211<br> <br>-static const char *status_map [] = {<br>-        [UNAVAILABLE] = "Unreachable",<br>-     [AVAILABLE] = "Reachable",<br>- [UNKNOWN] = "Unknown",<br>-     [CREATED] = "Created",<br>-     [REMOVED] = "Removed",<br>+/*! \brief These are the number of buckets to store AORs in */<br>+#ifdef LOW_MEMORY<br>+#define AOR_BUCKETS 61<br>+#else<br>+#define AOR_BUCKETS 1567<br>+#endif<br>+<br>+/*! \brief These are the number of contact status buckets */<br>+#ifdef LOW_MEMORY<br>+#define CONTACT_STATUS_BUCKETS 61<br>+#else<br>+#define CONTACT_STATUS_BUCKETS 1567<br>+#endif<br>+<br>+/*! \brief These are the number of buckets (per AOR) to use to store contacts */<br>+#define CONTACT_BUCKETS 13<br>+<br>+/*! \brief These are the number of buckets to store endpoint state compositors */<br>+#define ENDPOINT_STATE_COMPOSITOR_BUCKETS 13<br>+<br>+/*! \brief The initial vector size for the endpoint state compositors on an AOR */<br>+#define ENDPOINT_STATE_COMPOSITOR_INITIAL_SIZE 1<br>+<br>+/*! \brief These are the number of buckets (per endpoint state compositor) to use to store AOR statuses */<br>+#define AOR_STATUS_BUCKETS 3<br>+<br>+/*! \brief Maximum wait time to join the below shutdown group */<br>+#define MAX_UNLOAD_TIMEOUT_TIME          10      /* Seconds */<br>+<br>+/*! \brief Shutdown group for options serializers */<br>+static struct ast_serializer_shutdown_group *shutdown_group;<br>+<br>+/*!<br>+ * \brief Structure which contains status information for an AOR feeding an endpoint state compositor<br>+ */<br>+struct sip_options_endpoint_aor_status {<br>+     /*! \brief The last contributed available status of the named AOR (1 if available, 0 if not available) */<br>+    char available;<br>+      /*! \brief The name of the AOR */<br>+    char name[0];<br> };<br> <br>-static const char *short_status_map [] = {<br>-   [UNAVAILABLE] = "Unavail",<br>- [AVAILABLE] = "Avail",<br>-     [UNKNOWN] = "Unknown",<br>-     [CREATED] = "Created",<br>-     [REMOVED] = "Removed",<br>-};<br>-<br>-static void contact_deleted(const void *obj);<br>-static void qualify_and_schedule(struct ast_sip_contact *contact);<br>-<br>-const char *ast_sip_get_contact_status_label(const enum ast_sip_contact_status_type status)<br>-{<br>-   return status_map[status];<br>-}<br>-<br>-const char *ast_sip_get_contact_short_status_label(const enum ast_sip_contact_status_type status)<br>-{<br>-    return short_status_map[status];<br>-}<br>-<br> /*!<br>- * \internal<br>- * \brief Destroy a ast_sip_contact_status object.<br>+ * \brief Structure which contains composites information for endpoint state<br>  */<br>-static void contact_status_destroy(void * obj)<br>-{<br>-  struct ast_sip_contact_status *status = obj;<br>-<br>-      ast_free(status->aor);<br>-    ast_free(status->uri);<br>-}<br>-<br>-/*!<br>- * \internal<br>- * \brief Create a ast_sip_contact_status object.<br>- */<br>-static void *contact_status_alloc(const char *name)<br>-{<br>-    struct ast_sip_contact_status *status = ast_sorcery_generic_alloc(sizeof(*status), contact_status_destroy);<br>-  char *id = ast_strdupa(name);<br>-        char *aor = id;<br>-      char *aor_separator = NULL;<br>-<br>-       if (!status) {<br>-               ast_log(LOG_ERROR, "Unable to allocate ast_sip_contact_status\n");<br>-         return NULL;<br>- }<br>-<br>- /* Dynamic contacts are delimited with ";@" and static ones with "@@" */<br>- if ((aor_separator = strstr(id, ";@")) || (aor_separator = strstr(id, "@@"))) {<br>-          *aor_separator = '\0';<br>-       }<br>-    ast_assert(aor_separator != NULL);<br>-<br>-        status->aor = ast_strdup(aor);<br>-    if (!status->aor) {<br>-               ao2_cleanup(status);<br>-         return NULL;<br>- }<br>-<br>- return status;<br>-}<br>-<br>-static int qualify_and_schedule_aor_contact(void *obj)<br>-{<br>-   struct ast_sip_contact *contact = obj;<br>-       struct ast_sip_aor *aor;<br>-<br>-  if (!contact || ast_strlen_zero(contact->aor) ||<br>-          !(aor = ast_sip_location_retrieve_aor(contact->aor))) {<br>-           ao2_ref(contact, -1);<br>-                return -1;<br>-   }<br>-<br>- contact->qualify_frequency = aor->qualify_frequency;<br>-   contact->qualify_timeout = aor->qualify_timeout;<br>-       contact->authenticate_qualify = aor->authenticate_qualify;<br>-<br>-  ao2_ref(aor, -1);<br>-<br>- qualify_and_schedule(contact);<br>-       ao2_ref(contact, -1);<br>-<br>-     return 0;<br>-}<br>-<br>-AST_MUTEX_DEFINE_STATIC(creation_lock);<br>-<br>-/*!<br>- * \brief Retrieve a ast_sip_contact_status object from sorcery creating<br>- *        one if not found.<br>- */<br>-struct ast_sip_contact_status *ast_res_pjsip_find_or_create_contact_status(const struct ast_sip_contact *contact)<br>-{<br>-   struct ast_sip_contact_status *status;<br>-       SCOPED_MUTEX(lock, &creation_lock);<br>-<br>-   status = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), CONTACT_STATUS,<br>-           ast_sorcery_object_get_id(contact));<br>- if (status) {<br>-                return status;<br>-       }<br>-<br>- status = ast_sorcery_alloc(ast_sip_get_sorcery(), CONTACT_STATUS,<br>-            ast_sorcery_object_get_id(contact));<br>- if (!status) {<br>-               ast_log(LOG_ERROR, "Unable to create ast_sip_contact_status for contact %s/%s\n",<br>-                  contact->aor, contact->uri);<br>-           return NULL;<br>- }<br>-<br>- status->uri = ast_strdup(contact->uri);<br>-        if (!status->uri) {<br>-               ao2_cleanup(status);<br>-         return NULL;<br>- }<br>-<br>- status->rtt_start = ast_tv(0, 0);<br>- status->rtt = 0;<br>-<br>-       if (ast_sorcery_create(ast_sip_get_sorcery(), status)) {<br>-             ast_log(LOG_ERROR, "Unable to persist ast_sip_contact_status for contact %s\n",<br>-                    contact->uri);<br>-            ao2_ref(status, -1);<br>-         return NULL;<br>- }<br>-<br>- /* The permanent contact added after asterisk start should be qualified. */<br>-  if (ast_test_flag(&ast_options, AST_OPT_FLAG_FULLY_BOOTED) && ast_tvzero(contact->expiration_time)) {<br>-         /*<br>-            * The FULLY_BOOTED to filter out contacts that already existed when asterisk started.<br>-                * The zero expiration_time to select only permanent contacts.<br>-                */<br>-          ao2_ref((struct ast_sip_contact *) contact, +1);<br>-             if (ast_sip_push_task(NULL, qualify_and_schedule_aor_contact, (struct ast_sip_contact *) contact)) {<br>-                 ao2_ref((struct ast_sip_contact *) contact, -1);<br>-             }<br>-    }<br>-<br>- ast_statsd_log_string_va("PJSIP.contacts.states.%s", AST_STATSD_GAUGE,<br>-             "+1", 1.0, ast_sip_get_contact_status_label(status->status));<br>-<br>-        return status;<br>-}<br>-<br>-/*!<br>- * \internal<br>- * \brief Update an ast_sip_contact_status's elements.<br>- */<br>-static void update_contact_status(const struct ast_sip_contact *contact,<br>-     enum ast_sip_contact_status_type value, int is_contact_refresh)<br>-{<br>-  RAII_VAR(struct ast_sip_contact_status *, status, NULL, ao2_cleanup);<br>-        RAII_VAR(struct ast_sip_contact_status *, update, NULL, ao2_cleanup);<br>-<br>-     status = ast_res_pjsip_find_or_create_contact_status(contact);<br>-       if (!status) {<br>-               ast_log(LOG_ERROR, "Unable to find ast_sip_contact_status for contact %s\n",<br>-                       contact->uri);<br>-            return;<br>-      }<br>-<br>- if (is_contact_refresh<br>-               && status->status == CREATED) {<br>-           /*<br>-            * The contact status hasn't been updated since creation<br>-          * and we don't want to re-send a created status.<br>-                 */<br>-          if (contact->qualify_frequency<br>-                    || status->rtt_start.tv_sec > 0) {<br>-                     /* Ignore, the status will change soon. */<br>-                   return;<br>-              }<br>-<br>-         /*<br>-            * Convert to a regular contact status update<br>-                 * because the status may never change.<br>-               */<br>-          is_contact_refresh = 0;<br>-              value = UNKNOWN;<br>-     }<br>-<br>- update = ast_sorcery_alloc(ast_sip_get_sorcery(), CONTACT_STATUS,<br>-            ast_sorcery_object_get_id(status));<br>-  if (!update) {<br>-               ast_log(LOG_ERROR, "Unable to allocate ast_sip_contact_status for contact %s\n",<br>-                   contact->uri);<br>-            return;<br>-      }<br>-<br>- update->uri = ast_strdup(contact->uri);<br>-        if (!update->uri) {<br>-               return;<br>-      }<br>-<br>- if (is_contact_refresh) {<br>-            /* Copy everything just to set the refresh flag. */<br>-          update->status = status->status;<br>-               update->last_status = status->last_status;<br>-             update->rtt = status->rtt;<br>-             update->rtt_start = status->rtt_start;<br>-         update->refresh = 1;<br>-      } else {<br>-             update->last_status = status->status;<br>-          update->status = value;<br>-<br>-                /*<br>-            * if the contact is available calculate the rtt as<br>-           * the diff between the last start time and "now"<br>-           */<br>-          update->rtt = update->status == AVAILABLE && status->rtt_start.tv_sec > 0<br>-                        ? ast_tvdiff_us(ast_tvnow(), status->rtt_start)<br>-                   : 0;<br>-         update->rtt_start = ast_tv(0, 0);<br>-<br>-              ast_test_suite_event_notify("AOR_CONTACT_QUALIFY_RESULT",<br>-                  "Contact: %s\r\n"<br>-                  "Status: %s\r\n"<br>-                   "RTT: %" PRId64,<br>-                   ast_sorcery_object_get_id(update),<br>-                   ast_sip_get_contact_status_label(update->status),<br>-                 update->rtt);<br>-     }<br>-<br>- if (ast_sorcery_update(ast_sip_get_sorcery(), update)) {<br>-             ast_log(LOG_ERROR, "Unable to update ast_sip_contact_status for contact %s\n",<br>-                     contact->uri);<br>-    }<br>-}<br>-<br>-/*!<br>- * \internal<br>- * \brief Initialize the start time on a contact status so the round<br>- *        trip time can be calculated upon a valid response.<br>- */<br>-static void init_start_time(const struct ast_sip_contact *contact)<br>-{<br>-   RAII_VAR(struct ast_sip_contact_status *, status, NULL, ao2_cleanup);<br>-        RAII_VAR(struct ast_sip_contact_status *, update, NULL, ao2_cleanup);<br>-<br>-     status = ast_res_pjsip_find_or_create_contact_status(contact);<br>-       if (!status) {<br>-               ast_log(LOG_ERROR, "Unable to find ast_sip_contact_status for contact %s\n",<br>-                       contact->uri);<br>-            return;<br>-      }<br>-<br>- update = ast_sorcery_alloc(ast_sip_get_sorcery(), CONTACT_STATUS,<br>-            ast_sorcery_object_get_id(status));<br>-  if (!update) {<br>-               ast_log(LOG_ERROR, "Unable to copy ast_sip_contact_status for contact %s\n",<br>-                       contact->uri);<br>-            return;<br>-      }<br>-<br>- update->uri = ast_strdup(contact->uri);<br>-        if (!update->uri) {<br>-               return;<br>-      }<br>-<br>- update->status = status->status;<br>-       update->last_status = status->last_status;<br>-     update->rtt = status->rtt;<br>-     update->rtt_start = ast_tvnow();<br>-<br>-       if (ast_sorcery_update(ast_sip_get_sorcery(), update)) {<br>-             ast_log(LOG_ERROR, "Unable to update ast_sip_contact_status for contact %s\n",<br>-                     contact->uri);<br>-    }<br>-}<br>-<br>-/*!<br>- * \internal<br>- * \brief For an endpoint try to match the given contact->aor.<br>- */<br>-static int on_endpoint(void *obj, void *arg, int flags)<br>-{<br>-        struct ast_sip_endpoint *endpoint = obj;<br>-     char *contact_aor = arg;<br>-     char *aor_name;<br>-      char *aors;<br>-<br>-       if (!arg || ast_strlen_zero(endpoint->aors)) {<br>-            return 0;<br>-    }<br>-<br>- aors = ast_strdupa(endpoint->aors);<br>-       while ((aor_name = ast_strip(strsep(&aors, ",")))) {<br>-           if (!strcmp(contact_aor, aor_name)) {<br>-                        return CMP_MATCH;<br>-            }<br>-    }<br>-<br>- return 0;<br>-}<br>-<br>-/*!<br>- * \internal<br>- * \brief Find an endpoint associated with the given contact.<br>- */<br>-static struct ast_sip_endpoint *find_an_endpoint(struct ast_sip_contact *contact)<br>-{<br>-  struct ao2_container *endpoints;<br>-     struct ast_sip_endpoint *endpoint;<br>-   struct ast_variable *var;<br>-    char *aor = ast_alloca(strlen(contact->aor) + 3);<br>-<br>-      sprintf(aor, "%%%s%%", contact->aor);<br>-   var = ast_variable_new("aors LIKE", aor, "");<br>-    endpoints = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(),<br>-            "endpoint", AST_RETRIEVE_FLAG_MULTIPLE, var);<br>-<br>-   ast_variables_destroy(var);<br>-<br>-       /*<br>-    * Because aors are a string list, we have to use a pattern match but since a simple<br>-  * pattern match could return an endpoint that has an aor of "aaabccc" when searching<br>-       * for "abc", we still have to iterate over them to find an exact aor match.<br>+struct sip_options_endpoint_state_compositor {<br>+      /*! \brief The last contributed available status of the AORs feeding this compositor */<br>+      struct ao2_container *aor_statuses;<br>+  /*!<br>+   * \brief Non-zero if the compositor is in normal operation. i.e. Not being setup/reconfigured.<br>+       *<br>+    * \details<br>+   * The aor layer can only update its aor_statuses record when not active.<br>+     * When active the aor layer can update its aor_statuses record, calculate the new<br>+    * number of available aors, determine if the endpoint compositor changed state,<br>+      * and report it.<br>      */<br>-  endpoint = ao2_callback(endpoints, 0, on_endpoint, contact->aor);<br>- ao2_ref(endpoints, -1);<br>-<br>-   return endpoint;<br>-}<br>-<br>-/*!<br>- * \internal<br>- * \brief Receive a response to the qualify contact request.<br>- */<br>-static void qualify_contact_cb(void *token, pjsip_event *e)<br>-{<br>-  struct ast_sip_contact *contact = token;<br>-<br>-  switch(e->body.tsx_state.type) {<br>-  default:<br>-             ast_log(LOG_ERROR, "Unexpected PJSIP event %u\n", e->body.tsx_state.type);<br>-              /* Fall through */<br>-   case PJSIP_EVENT_TRANSPORT_ERROR:<br>-    case PJSIP_EVENT_TIMER:<br>-              update_contact_status(contact, UNAVAILABLE, 0);<br>-              break;<br>-       case PJSIP_EVENT_RX_MSG:<br>-             update_contact_status(contact, AVAILABLE, 0);<br>-                break;<br>-       }<br>-    ao2_cleanup(contact);<br>-}<br>-<br>-/*!<br>- * \internal<br>- * \brief Attempt to qualify the contact<br>- *<br>- * \details Sends a SIP OPTIONS request to the given contact in order to make<br>- *         sure that contact is available.<br>- */<br>-static int qualify_contact(struct ast_sip_endpoint *endpoint, struct ast_sip_contact *contact)<br>-{<br>-    pjsip_tx_data *tdata;<br>-        RAII_VAR(struct ast_sip_endpoint *, endpoint_local, NULL, ao2_cleanup);<br>-<br>-   if (endpoint) {<br>-              endpoint_local = ao2_bump(endpoint);<br>- } else {<br>-             if (!ast_strlen_zero(contact->endpoint_name)) {<br>-                   endpoint_local = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", contact->endpoint_name);<br>-         }<br>-            if (!endpoint_local) {<br>-                       endpoint_local = find_an_endpoint(contact);<br>-          }<br>-            if (!endpoint_local) {<br>-                       ast_log(LOG_WARNING, "Unable to find an endpoint to qualify contact %s. Deleting this contact\n",<br>-                          contact->uri);<br>-                    contact_deleted(contact);<br>-                    return -1;<br>-           }<br>-    }<br>-<br>- if (ast_sip_create_request("OPTIONS", NULL, endpoint_local, NULL, contact, &tdata)) {<br>-          ast_log(LOG_ERROR, "Unable to create request to qualify contact %s\n",<br>-                     contact->uri);<br>-            return -1;<br>-   }<br>-<br>- /* If an outbound proxy is specified set it on this request */<br>-       if (!ast_strlen_zero(contact->outbound_proxy) &&<br>-          ast_sip_set_outbound_proxy(tdata, contact->outbound_proxy)) {<br>-             pjsip_tx_data_dec_ref(tdata);<br>-                ast_log(LOG_ERROR, "Unable to apply outbound proxy on request to qualify contact %s\n",<br>-                    contact->uri);<br>-            return -1;<br>-   }<br>-<br>- init_start_time(contact);<br>-<br>- ao2_ref(contact, +1);<br>-        if (ast_sip_send_out_of_dialog_request(tdata, endpoint_local, (int)(contact->qualify_timeout * 1000), contact, qualify_contact_cb)<br>-                != PJ_SUCCESS) {<br>-             ast_log(LOG_ERROR, "Unable to send request to qualify contact %s\n",<br>-                       contact->uri);<br>-            update_contact_status(contact, UNAVAILABLE, 0);<br>-              ao2_ref(contact, -1);<br>-                return -1;<br>-   }<br>-<br>- return 0;<br>-}<br>-<br>-/*!<br>- * \internal<br>- * \brief Scheduling context for sending QUALIFY request at specified intervals.<br>- */<br>-static struct ast_sched_context *sched;<br>-<br>-/*!<br>- * \internal<br>- * \brief Container to hold all actively scheduled qualifies.<br>- */<br>-static struct ao2_container *sched_qualifies;<br>-<br>-/*!<br>- * \internal<br>- * \brief Structure to hold qualify contact scheduling information.<br>- */<br>-struct sched_data {<br>-     /*! The scheduling id */<br>-     int id;<br>-      /*! The the contact being checked */<br>- struct ast_sip_contact *contact;<br>+     char active;<br>+ /*! \brief The name of the endpoint */<br>+       char name[0];<br> };<br> <br> /*!<br>- * \internal<br>- * \brief Destroy the scheduled data and remove from scheduler.<br>+ * \brief Structure which contains an AOR and contacts for qualifying purposes<br>  */<br>-static void sched_data_destructor(void *obj)<br>-{<br>-       struct sched_data *data = obj;<br>-<br>-    ao2_cleanup(data->contact);<br>-}<br>-/*!<br>- * \internal<br>- * \brief Create the scheduling data object.<br>- */<br>-static struct sched_data *sched_data_create(struct ast_sip_contact *contact)<br>-{<br>-      struct sched_data *data;<br>-<br>-  data = ao2_t_alloc(sizeof(*data), sched_data_destructor, contact->uri);<br>-   if (!data) {<br>-         ast_log(LOG_ERROR, "Unable to create schedule qualify data for contact %s\n",<br>-                      contact->uri);<br>-            return NULL;<br>- }<br>-<br>- data->contact = contact;<br>-  ao2_ref(data->contact, +1);<br>-<br>-    return data;<br>-}<br>-<br>-/*!<br>- * \internal<br>- * \brief Send a qualify contact request within a threaded task.<br>- */<br>-static int qualify_contact_task(void *obj)<br>-{<br>-   struct ast_sip_contact *contact = obj;<br>-       int res;<br>-<br>-  res = qualify_contact(NULL, contact);<br>-        ao2_ref(contact, -1);<br>-        return res;<br>-}<br>-<br>-/*!<br>- * \internal<br>- * \brief Send a scheduled qualify contact request.<br>- */<br>-static int qualify_contact_sched(const void *obj)<br>-{<br>-  struct sched_data *data = (struct sched_data *) obj;<br>-<br>-      ao2_ref(data->contact, +1);<br>-       if (ast_sip_push_task(NULL, qualify_contact_task, data->contact)) {<br>-               ao2_ref(data->contact, -1);<br>-       }<br>-<br>- /*<br>-    * Always reschedule rather than have a potential race cleaning<br>-       * up the data object ref between self deletion and an external<br>-       * deletion.<br>+struct sip_options_aor {<br>+      /*! \brief The scheduler task for this AOR */<br>+        struct ast_sip_sched_task *sched_task;<br>+       /*! \brief The serializer for this AOR */<br>+    struct ast_taskprocessor *serializer;<br>+        /*! \brief All contacts associated with this AOR */<br>+  struct ao2_container *contacts;<br>+      /*!<br>+   * \brief Only dynamic contacts associated with this AOR<br>+      * \note Used to speed up applying AOR configuration by<br>+       * minimizing wild card sorcery access.<br>        */<br>-  return data->contact->qualify_frequency * 1000;<br>-}<br>-<br>-/*!<br>- * \internal<br>- * \brief Set up a scheduled qualify contact check.<br>- */<br>-static void schedule_qualify(struct ast_sip_contact *contact, int initial_interval)<br>-{<br>-      struct sched_data *data;<br>-<br>-  data = sched_data_create(contact);<br>-   if (!data) {<br>-         return;<br>-      }<br>-<br>- ast_assert(contact->qualify_frequency != 0);<br>-<br>-   ao2_t_ref(data, +1, "Ref for qualify_contact_sched() scheduler entry");<br>-    data->id = ast_sched_add_variable(sched, initial_interval,<br>-                qualify_contact_sched, data, 1);<br>-     if (data->id < 0) {<br>-            ao2_t_ref(data, -1, "Cleanup failed scheduler add");<br>-               ast_log(LOG_ERROR, "Unable to schedule qualify for contact %s\n",<br>-                  contact->uri);<br>-    } else if (!ao2_link(sched_qualifies, data)) {<br>-               AST_SCHED_DEL_UNREF(sched, data->id,<br>-                      ao2_t_ref(data, -1, "Cleanup scheduler for failed ao2_link"));<br>-     }<br>-    ao2_t_ref(data, -1, "Done setting up scheduler entry");<br>-}<br>-<br>-/*!<br>- * \internal<br>- * \brief Remove the contact from the scheduler.<br>- */<br>-static void unschedule_qualify(struct ast_sip_contact *contact)<br>-{<br>- struct sched_data *data;<br>-<br>-  data = ao2_find(sched_qualifies, contact, OBJ_UNLINK | OBJ_SEARCH_KEY);<br>-      if (!data) {<br>-         return;<br>-      }<br>-<br>- AST_SCHED_DEL_UNREF(sched, data->id,<br>-              ao2_t_ref(data, -1, "Delete scheduler entry ref"));<br>-        ao2_t_ref(data, -1, "Done with ao2_find ref");<br>-}<br>-<br>-/*!<br>- * \internal<br>- * \brief Qualify the given contact and set up scheduling if configured.<br>- */<br>-static void qualify_and_schedule(struct ast_sip_contact *contact)<br>-{<br>-        unschedule_qualify(contact);<br>-<br>-      if (contact->qualify_frequency) {<br>-         ao2_ref(contact, +1);<br>-                if (ast_sip_push_task(NULL, qualify_contact_task, contact)) {<br>-                        ao2_ref(contact, -1);<br>-                }<br>-<br>-         schedule_qualify(contact, contact->qualify_frequency * 1000);<br>-     } else {<br>-             update_contact_status(contact, UNKNOWN, 0);<br>-  }<br>-}<br>-<br>-/*!<br>- * \internal<br>- * \brief A new contact has been created make sure it is available.<br>- */<br>-static void contact_created(const void *obj)<br>-{<br>- qualify_and_schedule((struct ast_sip_contact *) obj);<br>-}<br>-<br>-/*!<br>- * \internal<br>- * \brief A contact has been updated.<br>- */<br>-static void contact_updated(const void *obj)<br>-{<br>-   update_contact_status(obj, AVAILABLE, 1);<br>-}<br>-<br>-/*!<br>- * \internal<br>- * \brief A contact has been deleted remove status tracking.<br>- */<br>-static void contact_deleted(const void *obj)<br>-{<br>-        struct ast_sip_contact *contact = (struct ast_sip_contact *) obj;<br>-    struct ast_sip_contact_status *status;<br>-<br>-    unschedule_qualify(contact);<br>-<br>-      status = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), CONTACT_STATUS,<br>-           ast_sorcery_object_get_id(contact));<br>- if (!status) {<br>-               return;<br>-      }<br>-<br>- if (ast_sorcery_delete(ast_sip_get_sorcery(), status)) {<br>-             ast_log(LOG_ERROR, "Unable to delete ast_sip_contact_status for contact %s\n",<br>-                     contact->uri);<br>-    }<br>-    ao2_ref(status, -1);<br>-}<br>-<br>-static const struct ast_sorcery_observer contact_observer = {<br>-  .created = contact_created,<br>-  .updated = contact_updated,<br>-  .deleted = contact_deleted,<br>+  struct ao2_container *dynamic_contacts;<br>+      /*! \brief The endpoint state compositors we are feeding, a reference is held to each */<br>+     AST_VECTOR(, struct sip_options_endpoint_state_compositor *) compositors;<br>+    /*! \brief The number of available contacts on this AOR */<br>+   unsigned int available;<br>+      /*! \brief Frequency to send OPTIONS requests to AOR contacts. 0 is disabled. */<br>+     unsigned int qualify_frequency;<br>+      /*! If true authenticate the qualify challenge response if needed */<br>+ int authenticate_qualify;<br>+    /*! \brief Qualify timeout. 0 is diabled. */<br>+ double qualify_timeout;<br>+      /*! \brief The name of the AOR */<br>+    char name[0];<br> };<br> <br>-static pj_bool_t options_start(void)<br>-{<br>-     sched = ast_sched_context_create();<br>-  if (!sched) {<br>-                return -1;<br>-   }<br>-    if (ast_sched_start_thread(sched)) {<br>-         ast_sched_context_destroy(sched);<br>-            sched = NULL;<br>-                return -1;<br>-   }<br>+/*!<br>+ * \internal<br>+ * \brief Container of active SIP AORs for qualifying<br>+ */<br>+static struct ao2_container *sip_options_aors;<br> <br>-     if (ast_sorcery_observer_add(ast_sip_get_sorcery(), "contact", &contact_observer)) {<br>-           ast_log(LOG_WARNING, "Unable to add contact observer\n");<br>-          ast_sched_context_destroy(sched);<br>-            sched = NULL;<br>-                return -1;<br>-   }<br>+/*!<br>+ * \internal<br>+ * \brief Container of contact statuses<br>+ */<br>+static struct ao2_container *sip_options_contact_statuses;<br> <br>-       return PJ_SUCCESS;<br>-}<br>+/*!<br>+ * \internal<br>+ * \brief Container of endpoint state compositors<br>+ */<br>+static struct ao2_container *sip_options_endpoint_state_compositors;<br> <br>-static int sched_qualifies_empty(void *obj, void *arg, int flags)<br>-{<br>-      ao2_t_ref(obj, -1, "Release ref held by destroyed scheduler context.");<br>-    return CMP_MATCH;<br>-}<br>-<br>-static pj_bool_t options_stop(void)<br>-{<br>-   ast_sorcery_observer_remove(ast_sip_get_sorcery(), "contact", &contact_observer);<br>-<br>-   if (sched) {<br>-         ast_sched_context_destroy(sched);<br>-            sched = NULL;<br>-        }<br>-<br>- /* Empty the container of scheduling data refs. */<br>-   ao2_callback(sched_qualifies, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE,<br>-                sched_qualifies_empty, NULL);<br>-<br>-     return PJ_SUCCESS;<br>-}<br>+/*!<br>+ * \internal<br>+ * \brief Serializer for AOR, endpoint state compositor, and contact existence management<br>+ */<br>+static struct ast_taskprocessor *management_serializer;<br> <br> static pj_status_t send_options_response(pjsip_rx_data *rdata, int code)<br> {<br>@@ -803,102 +314,2004 @@<br>   .name = {"Options Module", 14},<br>     .id = -1,<br>     .priority = PJSIP_MOD_PRIORITY_APPLICATION,<br>-  .start = options_start,<br>-      .stop = options_stop,<br>         .on_rx_request = options_on_rx_request,<br> };<br> <br>-/*!<br>- * \internal<br>- * \brief Send qualify request to the given contact.<br>- */<br>-static int cli_on_contact(void *obj, void *arg, void *data, int flags)<br>-{<br>-       struct ast_sip_contact *contact = obj;<br>-       struct ast_sip_endpoint *endpoint = data;<br>-    int *cli_fd = arg;<br>-<br>-        ast_cli(*cli_fd, " contact %s\n", contact->uri);<br>-        qualify_contact(endpoint, contact);<br>-  return 0;<br>-}<br>-<br>-/*!<br>- * \brief Data pushed to threadpool to qualify endpoints from the CLI<br>- */<br>-struct qualify_data {<br>- /*! Endpoint that is being qualified */<br>-      struct ast_sip_endpoint *endpoint;<br>-   /*! CLI File descriptor for printing messages */<br>-     int cli_fd;<br>+static const char *status_map[] = {<br>+    [UNAVAILABLE] = "Unreachable",<br>+     [AVAILABLE] = "Reachable",<br>+ [UNKNOWN] = "Unknown",<br>+     [CREATED] = "Created",<br>+     [REMOVED] = "Removed",<br> };<br> <br>-static struct qualify_data *qualify_data_alloc(struct ast_sip_endpoint *endpoint, int cli_fd)<br>-{<br>- struct qualify_data *qual_data;<br>+static const char *short_status_map[] = {<br>+  [UNAVAILABLE] = "Unavail",<br>+ [AVAILABLE] = "Avail",<br>+     [UNKNOWN] = "Unknown",<br>+     [CREATED] = "Created",<br>+     [REMOVED] = "Removed",<br>+};<br> <br>-     qual_data = ast_malloc(sizeof(*qual_data));<br>-  if (!qual_data) {<br>+const char *ast_sip_get_contact_status_label(const enum ast_sip_contact_status_type status)<br>+{<br>+  ast_assert(0 <= status && status < ARRAY_LEN(status_map));<br>+     return status_map[status];<br>+}<br>+<br>+const char *ast_sip_get_contact_short_status_label(const enum ast_sip_contact_status_type status)<br>+{<br>+    ast_assert(0 <= status && status < ARRAY_LEN(short_status_map));<br>+       return short_status_map[status];<br>+}<br>+<br>+/*! \brief Destructor for contact statuses */<br>+static void sip_contact_status_dtor(void *obj)<br>+{<br>+ struct ast_sip_contact_status *contact_status = obj;<br>+<br>+      ast_string_field_free_memory(contact_status);<br>+}<br>+<br>+static struct ast_sip_contact_status *sip_contact_status_alloc(const char *name)<br>+{<br>+  struct ast_sip_contact_status *contact_status;<br>+       size_t size = sizeof(*contact_status) + strlen(name) + 1;<br>+<br>+ contact_status = ao2_alloc_options(size, sip_contact_status_dtor,<br>+            AO2_ALLOC_OPT_LOCK_NOLOCK);<br>+  if (!contact_status) {<br>+               return NULL;<br>+ }<br>+    if (ast_string_field_init(contact_status, 256)) {<br>+            ao2_ref(contact_status, -1);<br>+         return NULL;<br>+ }<br>+    strcpy(contact_status->name, name); /* SAFE */<br>+    return contact_status;<br>+}<br>+<br>+static struct ast_sip_contact_status *sip_contact_status_copy(const struct ast_sip_contact_status *src)<br>+{<br>+  struct ast_sip_contact_status *dst;<br>+<br>+       dst = sip_contact_status_alloc(src->name);<br>+        if (!dst) {<br>           return NULL;<br>  }<br> <br>- qual_data->endpoint = ao2_bump(endpoint);<br>- qual_data->cli_fd = cli_fd;<br>-       return qual_data;<br>+    if (ast_string_fields_copy(dst, src)) {<br>+              ao2_ref(dst, -1);<br>+            return NULL;<br>+ }<br>+    dst->rtt = src->rtt;<br>+   dst->status = src->status;<br>+     dst->last_status = src->last_status;<br>+   return dst;<br> }<br> <br>-static void qualify_data_destroy(struct qualify_data *qual_data)<br>+/*! \brief Hashing function for contact statuses */<br>+AO2_STRING_FIELD_HASH_FN(ast_sip_contact_status, name);<br>+<br>+/*! \brief Sort function for contact statuses */<br>+AO2_STRING_FIELD_SORT_FN(ast_sip_contact_status, name);<br>+<br>+/*! \brief Comparator function for contact statuses */<br>+AO2_STRING_FIELD_CMP_FN(ast_sip_contact_status, name);<br>+<br>+/*! \brief Helper function to allocate a contact statuses container */<br>+static struct ao2_container *sip_options_contact_statuses_alloc(void)<br> {<br>-   ao2_cleanup(qual_data->endpoint);<br>- ast_free(qual_data);<br>+ /*<br>+    * Replace duplicate objects so we can update the immutable<br>+   * contact status objects by simply linking in a new object.<br>+  */<br>+  return ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX,<br>+            AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, CONTACT_STATUS_BUCKETS,<br>+                ast_sip_contact_status_hash_fn, ast_sip_contact_status_sort_fn,<br>+              ast_sip_contact_status_cmp_fn);<br>+}<br>+<br>+/*! \brief Function which publishes a contact status update to all interested endpoints */<br>+static void sip_options_publish_contact_state(const struct sip_options_aor *aor_options,<br>+       const struct ast_sip_contact_status *contact_status)<br>+{<br>+     int i;<br>+<br>+    for (i = 0; i < AST_VECTOR_SIZE(&aor_options->compositors); ++i) {<br>+         const struct sip_options_endpoint_state_compositor *endpoint_state_compositor;<br>+<br>+            endpoint_state_compositor = AST_VECTOR_GET(&aor_options->compositors, i);<br>+             ast_sip_persistent_endpoint_publish_contact_state(endpoint_state_compositor->name,<br>+                        contact_status);<br>+     }<br> }<br> <br> /*!<br>- * \internal<br>- * \brief For an endpoint iterate over and qualify all aors/contacts<br>+ * \brief Task to notify endpoints of a contact status change<br>+ * \note Run by management_serializer<br>  */<br>-static int cli_qualify_contacts(void *data)<br>+static int contact_status_publish_update_task(void *obj)<br> {<br>-      char *aors;<br>-  char *aor_name;<br>-      RAII_VAR(struct qualify_data *, qual_data, data, qualify_data_destroy);<br>-      struct ast_sip_endpoint *endpoint = qual_data->endpoint;<br>-  int cli_fd = qual_data->cli_fd;<br>-   const char *endpoint_name = ast_sorcery_object_get_id(endpoint);<br>+     struct ast_sip_contact_status *contact_status = obj;<br>+ struct sip_options_aor *aor_options;<br> <br>-      if (ast_strlen_zero(endpoint->aors)) {<br>-            ast_cli(cli_fd, "Endpoint %s has no AoR's configured\n",<br>-                       endpoint_name);<br>+      aor_options = ao2_find(sip_options_aors, contact_status->aor, OBJ_SEARCH_KEY);<br>+    if (aor_options) {<br>+           sip_options_publish_contact_state(aor_options, contact_status);<br>+              ao2_ref(aor_options, -1);<br>+    }<br>+    ao2_ref(contact_status, -1);<br>+<br>+      return 0;<br>+}<br>+<br>+static void sip_options_contact_status_update(struct ast_sip_contact_status *contact_status)<br>+{<br>+  struct ast_taskprocessor *mgmt_serializer = management_serializer;<br>+<br>+        if (mgmt_serializer) {<br>+               ao2_ref(contact_status, +1);<br>+         if (ast_sip_push_task(mgmt_serializer, contact_status_publish_update_task,<br>+                   contact_status)) {<br>+                   ao2_ref(contact_status, -1);<br>+         }<br>+    }<br>+}<br>+<br>+struct ast_sip_contact_status *ast_res_pjsip_find_or_create_contact_status(const struct ast_sip_contact *contact)<br>+{<br>+     struct ast_sip_contact_status *contact_status;<br>+       int res;<br>+<br>+  /*<br>+    * At startup a contact status can be retrieved when static contacts<br>+  * are themselves being setup.  This happens before we are fully setup.<br>+       * Since we don't actually trigger qualify or anything as a result it<br>+     * is safe to do so.  They'll just get back a contact status that will<br>+    * be updated later.  At this time they only care that the contact<br>+    * status gets created for the static contact anyway.<br>+         */<br>+  if (!sip_options_contact_statuses) {<br>+         /*<br>+            * We haven't been pre-initialized or we are shutting down.<br>+               * Neither situation should happen.<br>+           */<br>+          ast_assert(0);<br>+               return NULL;<br>+ }<br>+<br>+ ao2_lock(sip_options_contact_statuses);<br>+<br>+   /* If contact status for this contact already exists just return it */<br>+       contact_status = ao2_find(sip_options_contact_statuses,<br>+              ast_sorcery_object_get_id(contact), OBJ_SEARCH_KEY | OBJ_NOLOCK);<br>+    if (contact_status) {<br>+                ao2_unlock(sip_options_contact_statuses);<br>+            return contact_status;<br>+       }<br>+<br>+ /* Otherwise we have to create and store a new contact status */<br>+     contact_status = sip_contact_status_alloc(ast_sorcery_object_get_id(contact));<br>+       if (!contact_status) {<br>+               ao2_unlock(sip_options_contact_statuses);<br>+            return NULL;<br>+ }<br>+<br>+ contact_status->rtt = 0;<br>+  contact_status->status = CREATED;<br>+ contact_status->last_status = CREATED;<br>+    res = ast_string_field_set(contact_status, uri, contact->uri);<br>+    res |= ast_string_field_set(contact_status, aor, contact->aor);<br>+   if (res) {<br>+           ao2_unlock(sip_options_contact_statuses);<br>+            ao2_ref(contact_status, -1);<br>+         return NULL;<br>+ }<br>+<br>+ ao2_link_flags(sip_options_contact_statuses, contact_status, OBJ_NOLOCK);<br>+    ao2_unlock(sip_options_contact_statuses);<br>+<br>+ ast_statsd_log_string_va("PJSIP.contacts.states.%s", AST_STATSD_GAUGE,<br>+             "+1", 1.0, ast_sip_get_contact_status_label(contact_status->status));<br>+<br>+        sip_options_contact_status_update(contact_status);<br>+<br>+        return contact_status;<br>+}<br>+<br>+struct ast_sip_contact_status *ast_sip_get_contact_status(const struct ast_sip_contact *contact)<br>+{<br>+ return ao2_find(sip_options_contact_statuses, ast_sorcery_object_get_id(contact),<br>+            OBJ_SEARCH_KEY);<br>+}<br>+<br>+/*! \brief Hashing function for OPTIONS AORs */<br>+AO2_STRING_FIELD_HASH_FN(sip_options_aor, name);<br>+<br>+/*! \brief Comparator function for SIP OPTIONS AORs */<br>+AO2_STRING_FIELD_CMP_FN(sip_options_aor, name);<br>+<br>+/*! \brief Hashing function for endpoint state compositors */<br>+AO2_STRING_FIELD_HASH_FN(sip_options_endpoint_state_compositor, name);<br>+<br>+/*! \brief Comparator function for endpoint state compositors */<br>+AO2_STRING_FIELD_CMP_FN(sip_options_endpoint_state_compositor, name);<br>+<br>+/*! \brief Structure used to contain information for an OPTIONS callback */<br>+struct sip_options_contact_callback_data {<br>+   /*! \brief The contact we qualified */<br>+       struct ast_sip_contact *contact;<br>+     /*! \brief The AOR options */<br>+        struct sip_options_aor *aor_options;<br>+ /*! \brief The time at which this OPTIONS attempt was started */<br>+     struct timeval rtt_start;<br>+    /*! \brief The new status of the contact */<br>+  enum ast_sip_contact_status_type status;<br>+};<br>+<br>+/*!<br>+ * \brief Return the current state of an endpoint state compositor<br>+ * \pre The endpoint_state_compositor lock must be held.<br>+ */<br>+static enum ast_endpoint_state sip_options_get_endpoint_state_compositor_state(<br>+       const struct sip_options_endpoint_state_compositor *endpoint_state_compositor)<br>+{<br>+   struct ao2_iterator it_aor_statuses;<br>+ struct sip_options_endpoint_aor_status *aor_status;<br>+  enum ast_endpoint_state state = AST_ENDPOINT_OFFLINE;<br>+<br>+     it_aor_statuses = ao2_iterator_init(endpoint_state_compositor->aor_statuses, 0);<br>+  for (; (aor_status = ao2_iterator_next(&it_aor_statuses)); ao2_ref(aor_status, -1)) {<br>+            if (aor_status->available) {<br>+                      state = AST_ENDPOINT_ONLINE;<br>+                 break;<br>+               }<br>+    }<br>+    ao2_iterator_destroy(&it_aor_statuses);<br>+<br>+       return state;<br>+}<br>+<br>+/*!<br>+ * \brief Update the AOR status on an endpoint state compositor<br>+ * \pre The endpoint_state_compositor lock must be held.<br>+ */<br>+static void sip_options_update_endpoint_state_compositor_aor(struct sip_options_endpoint_state_compositor *endpoint_state_compositor,<br>+        const char *name, enum ast_sip_contact_status_type status)<br>+{<br>+       struct sip_options_endpoint_aor_status *aor_status;<br>+  enum ast_endpoint_state endpoint_state;<br>+<br>+   aor_status = ao2_find(endpoint_state_compositor->aor_statuses, name,<br>+              OBJ_SEARCH_KEY | OBJ_NOLOCK);<br>+        if (!aor_status) {<br>+           /* The AOR status doesn't exist already so we don't need to go any further */<br>+                if (status == REMOVED) {<br>+                     return;<br>+              }<br>+<br>+         aor_status = ao2_alloc_options(sizeof(*aor_status) + strlen(name) + 1, NULL,<br>+                 AO2_ALLOC_OPT_LOCK_NOLOCK);<br>+          if (!aor_status) {<br>+                   return;<br>+              }<br>+<br>+         strcpy(aor_status->name, name); /* SAFE */<br>+                ao2_link(endpoint_state_compositor->aor_statuses, aor_status);<br>+    }<br>+<br>+ if (status == REMOVED) {<br>+             /*<br>+            * If the AOR is being removed then remove its AOR status<br>+             * from the endpoint compositor.<br>+              */<br>+          ao2_unlink(endpoint_state_compositor->aor_statuses, aor_status);<br>+  } else {<br>+             aor_status->available = (status == AVAILABLE ? 1 : 0);<br>+    }<br>+    ao2_ref(aor_status, -1);<br>+<br>+  if (!endpoint_state_compositor->active) {<br>+         return;<br>+      }<br>+<br>+ /* If this AOR is available then the endpoint itself has to be online */<br>+     if (status == AVAILABLE) {<br>+           ast_debug(3, "Endpoint state compositor '%s' is online as AOR '%s' is available\n",<br>+                        endpoint_state_compositor->name, name);<br>+           endpoint_state = AST_ENDPOINT_ONLINE;<br>+        } else {<br>+             endpoint_state =<br>+                     sip_options_get_endpoint_state_compositor_state(endpoint_state_compositor);<br>+  }<br>+<br>+ ast_sip_persistent_endpoint_update_state(endpoint_state_compositor->name,<br>+         endpoint_state);<br>+}<br>+<br>+/*! \brief Function which notifies endpoint state compositors of a state change of an AOR */<br>+static void sip_options_notify_endpoint_state_compositors(struct sip_options_aor *aor_options,<br>+      enum ast_sip_contact_status_type status)<br>+{<br>+ int i;<br>+<br>+    /* Iterate through the associated endpoint state compositors updating them */<br>+        for (i = 0; i < AST_VECTOR_SIZE(&aor_options->compositors); ++i) {<br>+         struct sip_options_endpoint_state_compositor *endpoint_state_compositor;<br>+<br>+          endpoint_state_compositor = AST_VECTOR_GET(&aor_options->compositors, i);<br>+<br>+          ao2_lock(endpoint_state_compositor);<br>+         sip_options_update_endpoint_state_compositor_aor(endpoint_state_compositor,<br>+                  aor_options->name, status);<br>+               ao2_unlock(endpoint_state_compositor);<br>+       }<br>+<br>+ if (status == REMOVED) {<br>+             AST_VECTOR_RESET(&aor_options->compositors, ao2_cleanup);<br>+     }<br>+}<br>+<br>+/*!<br>+ * \brief Task to notify an AOR of a contact status change<br>+ * \note Run by aor_options->serializer<br>+ */<br>+static int sip_options_contact_status_notify_task(void *obj)<br>+{<br>+    struct sip_options_contact_callback_data *contact_callback_data = obj;<br>+       struct ast_sip_contact *contact;<br>+     struct ast_sip_contact_status *cs_old;<br>+       struct ast_sip_contact_status *cs_new;<br>+<br>+    /*<br>+    * Determine if this is a late arriving notification, as it is<br>+        * possible that we get a callback from PJSIP giving us contact<br>+       * status but in the mean time said contact has been removed<br>+  * from the controlling AOR.<br>+  */<br>+<br>+       if (!contact_callback_data->aor_options->qualify_frequency) {<br>+          /* Contact qualify response is late */<br>+               ao2_ref(contact_callback_data, -1);<br>           return 0;<br>     }<br> <br>- aors = ast_strdupa(endpoint->aors);<br>-       while ((aor_name = ast_strip(strsep(&aors, ",")))) {<br>-           struct ast_sip_aor *aor;<br>+     contact = ao2_find(contact_callback_data->aor_options->contacts,<br>+               contact_callback_data->contact, OBJ_SEARCH_OBJECT);<br>+       if (!contact) {<br>+              /* Contact qualify response is late */<br>+               ao2_ref(contact_callback_data, -1);<br>+          return 0;<br>+    }<br>+    ao2_ref(contact, -1);<br>+<br>+     cs_old = ao2_find(sip_options_contact_statuses,<br>+              ast_sorcery_object_get_id(contact_callback_data->contact), OBJ_SEARCH_KEY);<br>+       if (!cs_old) {<br>+               /* Contact qualify response is late */<br>+               ao2_ref(contact_callback_data, -1);<br>+          return 0;<br>+    }<br>+<br>+ /* Update the contact specific status information */<br>+ cs_new = sip_contact_status_copy(cs_old);<br>+    ao2_ref(cs_old, -1);<br>+ if (!cs_new) {<br>+               ao2_ref(contact_callback_data, -1);<br>+          return 0;<br>+    }<br>+    cs_new->last_status = cs_new->status;<br>+  cs_new->status = contact_callback_data->status;<br>+        cs_new->rtt =<br>+             cs_new->status == AVAILABLE<br>+                       ? ast_tvdiff_us(ast_tvnow(), contact_callback_data->rtt_start)<br>+                    : 0;<br>+ ao2_link(sip_options_contact_statuses, cs_new);<br>+<br>+   /*<br>+    * If the status has changed then notify the endpoint state compositors<br>+       * and publish our events.<br>+    */<br>+  if (cs_new->last_status != cs_new->status) {<br>+           if (cs_new->status == AVAILABLE) {<br>+                        /* If this is the first available contact then the AOR has become available */<br>+                       ++contact_callback_data->aor_options->available;<br>+                       if (contact_callback_data->aor_options->available == 1) {<br>+                              sip_options_notify_endpoint_state_compositors(<br>+                                       contact_callback_data->aor_options, AVAILABLE);<br>+                   }<br>+            } else if (cs_new->last_status == AVAILABLE) {<br>+                    ast_assert(cs_new->status == UNAVAILABLE);<br>+<br>+                     /* If there are no more available contacts then this AOR is unavailable */<br>+                   --contact_callback_data->aor_options->available;<br>+                       if (!contact_callback_data->aor_options->available) {<br>+                          sip_options_notify_endpoint_state_compositors(<br>+                                       contact_callback_data->aor_options, UNAVAILABLE);<br>+                 }<br>+            }<br>+<br>+         ast_verb(3, "Contact %s/%s is now %s.  RTT: %.3f msec\n",<br>+                  cs_new->aor,<br>+                      cs_new->uri,<br>+                      ast_sip_get_contact_status_label(cs_new->status),<br>+                 cs_new->rtt / 1000.0);<br>+<br>+         ast_statsd_log_string_va("PJSIP.contacts.states.%s", AST_STATSD_GAUGE,<br>+                     "-1", 1.0, ast_sip_get_contact_status_label(cs_new->last_status));<br>+              ast_statsd_log_string_va("PJSIP.contacts.states.%s", AST_STATSD_GAUGE,<br>+                     "+1", 1.0, ast_sip_get_contact_status_label(cs_new->status));<br>+<br>+                sip_options_contact_status_update(cs_new);<br>+<br>+                ast_test_suite_event_notify("AOR_CONTACT_UPDATE",<br>+                  "Contact: %s\r\n"<br>+                  "Status: %s",<br>+                      cs_new->name,<br>+                     ast_sip_get_contact_status_label(cs_new->status));<br>+        } else {<br>+             ast_debug(3, "Contact %s/%s status didn't change: %s, RTT: %.3f msec\n",<br>+                       cs_new->aor,<br>+                      cs_new->uri,<br>+                      ast_sip_get_contact_status_label(cs_new->status),<br>+                 cs_new->rtt / 1000.0);<br>+    }<br>+<br>+ ast_statsd_log_full_va("PJSIP.contacts.%s.rtt", AST_STATSD_TIMER,<br>+          cs_new->status != AVAILABLE ? -1 : cs_new->rtt / 1000,<br>+         1.0,<br>+         cs_new->name);<br>+<br>+ ast_test_suite_event_notify("AOR_CONTACT_QUALIFY_RESULT",<br>+          "Contact: %s\r\n"<br>+          "Status: %s\r\n"<br>+           "RTT: %" PRId64,<br>+           cs_new->name,<br>+             ast_sip_get_contact_status_label(cs_new->status),<br>+         cs_new->rtt);<br>+<br>+  ast_debug(3, "AOR '%s' now has %d available contacts\n",<br>+           contact_callback_data->aor_options->name,<br>+              contact_callback_data->aor_options->available);<br>+<br>+     ao2_ref(cs_new, -1);<br>+ ao2_ref(contact_callback_data, -1);<br>+<br>+       return 0;<br>+}<br>+<br>+/*! \brief Callback for when we get a result from a SIP OPTIONS request (a response or a timeout) */<br>+static void qualify_contact_cb(void *token, pjsip_event *e)<br>+{<br>+    struct sip_options_contact_callback_data *contact_callback_data = token;<br>+     enum ast_sip_contact_status_type status;<br>+<br>+  switch(e->body.tsx_state.type) {<br>+  default:<br>+             ast_log(LOG_ERROR, "Unexpected PJSIP event %u\n", e->body.tsx_state.type);<br>+              /* Fall through */<br>+   case PJSIP_EVENT_TRANSPORT_ERROR:<br>+    case PJSIP_EVENT_TIMER:<br>+              status = UNAVAILABLE;<br>+                break;<br>+       case PJSIP_EVENT_RX_MSG:<br>+             status = AVAILABLE;<br>+          break;<br>+       }<br>+<br>+ /* Update the callback data with the new status, this will get handled in the AOR serializer */<br>+      contact_callback_data->status = status;<br>+<br>+        if (ast_sip_push_task(contact_callback_data->aor_options->serializer,<br>+          sip_options_contact_status_notify_task, contact_callback_data)) {<br>+            ast_log(LOG_NOTICE, "Unable to queue contact status update for '%s' on AOR '%s', state will be incorrect\n",<br>+                       ast_sorcery_object_get_id(contact_callback_data->contact),<br>+                        contact_callback_data->aor_options->name);<br>+             ao2_ref(contact_callback_data, -1);<br>+  }<br>+<br>+ /* The task inherited our reference so we don't unreference here */<br>+}<br>+<br>+/*! \brief Destructor for contact callback data */<br>+static void sip_options_contact_callback_data_dtor(void *obj)<br>+{<br>+      struct sip_options_contact_callback_data *contact_callback_data = obj;<br>+<br>+    ao2_cleanup(contact_callback_data->contact);<br>+      ao2_cleanup(contact_callback_data->aor_options);<br>+}<br>+<br>+/*! \brief Contact callback data allocator */<br>+static struct sip_options_contact_callback_data *sip_options_contact_callback_data_alloc(<br>+       struct ast_sip_contact *contact, struct sip_options_aor *aor_options)<br>+{<br>+    struct sip_options_contact_callback_data *contact_callback_data;<br>+<br>+  contact_callback_data = ao2_alloc_options(sizeof(*contact_callback_data),<br>+            sip_options_contact_callback_data_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK);<br>+  if (!contact_callback_data) {<br>+                return NULL;<br>+ }<br>+<br>+ contact_callback_data->contact = ao2_bump(contact);<br>+       contact_callback_data->aor_options = ao2_bump(aor_options);<br>+       contact_callback_data->rtt_start = ast_tvnow();<br>+<br>+        return contact_callback_data;<br>+}<br>+<br>+/*! \brief Send a SIP OPTIONS request for a contact */<br>+static int sip_options_qualify_contact(void *obj, void *arg, int flags)<br>+{<br>+  struct ast_sip_contact *contact = obj;<br>+       struct sip_options_aor *aor_options = arg;<br>+   RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);<br>+    pjsip_tx_data *tdata;<br>+        struct ast_sip_contact_status *contact_status;<br>+       struct sip_options_contact_callback_data *contact_callback_data;<br>+<br>+  ast_debug(3, "Qualifying contact '%s' on AOR '%s'\n",<br>+              ast_sorcery_object_get_id(contact), aor_options->name);<br>+<br>+        if (!ast_strlen_zero(contact->endpoint_name)) {<br>+           endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint",<br>+                   contact->endpoint_name);<br>+  }<br>+    if (!endpoint && AST_VECTOR_SIZE(&aor_options->compositors)) {<br>+                struct sip_options_endpoint_state_compositor *endpoint_state_compositor;<br>+<br>+          endpoint_state_compositor = AST_VECTOR_GET(&aor_options->compositors, 0);<br>+             endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint",<br>+                   endpoint_state_compositor->name);<br>+ }<br>+    if (!endpoint) {<br>+             ast_debug(3, "Could not find an endpoint to qualify contact '%s' on AOR '%s'\n",<br>+                   ast_sorcery_object_get_id(contact), aor_options->name);<br>+           return 0;<br>+    }<br>+<br>+ if (ast_sip_create_request("OPTIONS", NULL, endpoint, NULL, contact, &tdata)) {<br>+                ast_log(LOG_ERROR, "Unable to create request to qualify contact %s on AOR %s\n",<br>+                   contact->uri, aor_options->name);<br>+              return 0;<br>+    }<br>+<br>+ /* If an outbound proxy is specified set it on this request */<br>+       if (!ast_strlen_zero(contact->outbound_proxy) &&<br>+          ast_sip_set_outbound_proxy(tdata, contact->outbound_proxy)) {<br>+             ast_log(LOG_ERROR, "Unable to apply outbound proxy on request to qualify contact %s\n",<br>+                    contact->uri);<br>+            pjsip_tx_data_dec_ref(tdata);<br>+                return 0;<br>+    }<br>+<br>+ contact_status = ast_res_pjsip_find_or_create_contact_status(contact);<br>+       if (!contact_status) {<br>+               ast_log(LOG_ERROR, "Unable to retrieve contact status information for contact %s on AOR %s\n",<br>+                     contact->uri, aor_options->name);<br>+              pjsip_tx_data_dec_ref(tdata);<br>+                return 0;<br>+    }<br>+    ao2_ref(contact_status, -1);<br>+<br>+      contact_callback_data = sip_options_contact_callback_data_alloc(contact, aor_options);<br>+       if (!contact_callback_data) {<br>+                ast_log(LOG_ERROR, "Unable to create object to contain callback data for contact %s on AOR %s\n",<br>+                  contact->uri, aor_options->name);<br>+              pjsip_tx_data_dec_ref(tdata);<br>+                return 0;<br>+    }<br>+<br>+ if (ast_sip_send_out_of_dialog_request(tdata, endpoint,<br>+              (int)(aor_options->qualify_timeout * 1000), contact_callback_data,<br>+                qualify_contact_cb)) {<br>+               ast_log(LOG_ERROR, "Unable to send request to qualify contact %s on AOR %s\n",<br>+                     contact->uri, aor_options->name);<br>+              ao2_ref(contact_callback_data, -1);<br>+  }<br>+<br>+ return 0;<br>+}<br>+<br>+/*!<br>+ * \brief Task to qualify contacts of an AOR<br>+ * \note Run by aor_options->serializer<br>+ */<br>+static int sip_options_qualify_aor(void *obj)<br>+{<br>+ struct sip_options_aor *aor_options = obj;<br>+<br>+        ast_debug(3, "Qualifying all contacts on AOR '%s'\n", aor_options->name);<br>+<br>+    /* Attempt to send an OPTIONS request to every contact on this AOR */<br>+        ao2_callback(aor_options->contacts, OBJ_NODATA, sip_options_qualify_contact,<br>+              (struct sip_options_aor *) aor_options);<br>+<br>+  /* Always reschedule to the frequency we should go */<br>+        return aor_options->qualify_frequency * 1000;<br>+}<br>+<br>+/*! \brief Forward declaration of this helpful function */<br>+static int sip_options_remove_contact(void *obj, void *arg, int flags);<br>+<br>+/*! \brief Destructor function for SIP OPTIONS AORs */<br>+static void sip_options_aor_dtor(void *obj)<br>+{<br>+ struct sip_options_aor *aor_options = obj;<br>+<br>+        /*<br>+    * Any contacts are unreachable since the AOR is being destroyed<br>+      * so remove their contact status<br>+     */<br>+  if (aor_options->contacts) {<br>+              ao2_callback(aor_options->contacts, OBJ_NODATA | OBJ_UNLINK,<br>+                      sip_options_remove_contact, aor_options);<br>+            ao2_ref(aor_options->contacts, -1);<br>+       }<br>+    ao2_cleanup(aor_options->dynamic_contacts);<br>+<br>+    ast_taskprocessor_unreference(aor_options->serializer);<br>+<br>+        ast_assert(AST_VECTOR_SIZE(&aor_options->compositors) == 0);<br>+  AST_VECTOR_FREE(&aor_options->compositors);<br>+}<br>+<br>+/*! \brief Allocator for AOR OPTIONS */<br>+static struct sip_options_aor *sip_options_aor_alloc(struct ast_sip_aor *aor)<br>+{<br>+      struct sip_options_aor *aor_options;<br>+ char tps_name[AST_TASKPROCESSOR_MAX_NAME + 1];<br>+<br>+    aor_options = ao2_alloc_options(sizeof(*aor_options) + strlen(ast_sorcery_object_get_id(aor)) + 1,<br>+           sip_options_aor_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK);<br>+    if (!aor_options) {<br>+          return NULL;<br>+ }<br>+<br>+ strcpy(aor_options->name, ast_sorcery_object_get_id(aor)); /* SAFE */<br>+<br>+  ast_taskprocessor_build_name(tps_name, sizeof(tps_name), "pjsip/options/%s",<br>+               ast_sorcery_object_get_id(aor));<br>+     aor_options->serializer = ast_sip_create_serializer_group_named(tps_name,<br>+         shutdown_group);<br>+     if (!aor_options->serializer) {<br>+           ao2_ref(aor_options, -1);<br>+            return NULL;<br>+ }<br>+<br>+ if (AST_VECTOR_INIT(&aor_options->compositors, ENDPOINT_STATE_COMPOSITOR_INITIAL_SIZE)) {<br>+             ao2_ref(aor_options, -1);<br>+            return NULL;<br>+ }<br>+<br>+ aor_options->contacts = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_NOLOCK,<br>+               AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT, CONTACT_BUCKETS, ast_sorcery_object_id_hash,<br>+            ast_sorcery_object_id_sort, ast_sorcery_object_id_compare);<br>+  if (!aor_options->contacts) {<br>+             ao2_ref(aor_options, -1);<br>+            return NULL;<br>+ }<br>+<br>+ aor_options->dynamic_contacts = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_NOLOCK,<br>+               AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT, CONTACT_BUCKETS, ast_sorcery_object_id_hash,<br>+            ast_sorcery_object_id_sort, ast_sorcery_object_id_compare);<br>+  if (!aor_options->dynamic_contacts) {<br>+             ao2_ref(aor_options, -1);<br>+            return NULL;<br>+ }<br>+<br>+ return aor_options;<br>+}<br>+<br>+/*! \brief Remove contact status for a hint */<br>+static void sip_options_remove_contact_status(struct sip_options_aor *aor_options,<br>+     struct ast_sip_contact *contact)<br>+{<br>+ struct ast_sip_contact_status *cs_new;<br>+       struct ast_sip_contact_status *cs_old;<br>+<br>+    cs_old = ao2_find(sip_options_contact_statuses, ast_sorcery_object_get_id(contact),<br>+          OBJ_SEARCH_KEY | OBJ_UNLINK);<br>+        if (!cs_old) {<br>+               ast_debug(3, "Attempted to remove contact status for '%s' but it does not exist\n",<br>+                        ast_sorcery_object_get_id(contact));<br>+         return;<br>+      }<br>+<br>+ ast_verb(2, "Contact %s/%s has been deleted\n", contact->aor, contact->uri);<br>+<br>+      /* Update the contact status to reflect its new state */<br>+     cs_new = sip_contact_status_copy(cs_old);<br>+    if (!cs_new) {<br>+               /*<br>+            * We'll have to violate the immutable property because we<br>+                * couldn't create a new one to modify and we are deleting<br>+                * the contact status anyway.<br>+                 */<br>+          cs_new = cs_old;<br>+     } else {<br>+             ao2_ref(cs_old, -1);<br>+ }<br>+    cs_new->last_status = cs_new->status;<br>+  cs_new->status = REMOVED;<br>+ cs_new->rtt = 0;<br>+<br>+       ast_statsd_log_string_va("PJSIP.contacts.states.%s", AST_STATSD_GAUGE,<br>+             "-1", 1.0, ast_sip_get_contact_status_label(cs_new->last_status));<br>+      ast_statsd_log_string_va("PJSIP.contacts.states.%s", AST_STATSD_GAUGE,<br>+             "+1", 1.0, ast_sip_get_contact_status_label(cs_new->status));<br>+<br>+        sip_options_contact_status_update(cs_new);<br>+<br>+        /*<br>+    * The only time we need to update the AOR is if this contact was<br>+     * available and qualify is in use, otherwise we can just stop<br>+        * early.<br>+     */<br>+  if (!aor_options->qualify_frequency || cs_new->last_status != AVAILABLE) {<br>+             ao2_ref(cs_new, -1);<br>+         return;<br>+      }<br>+<br>+ --aor_options->available;<br>+ if (!aor_options->available) {<br>+            sip_options_notify_endpoint_state_compositors(aor_options, UNAVAILABLE);<br>+     }<br>+<br>+ ast_debug(3, "AOR '%s' now has %d available contacts\n", aor_options->name,<br>+             aor_options->available);<br>+<br>+       ao2_ref(cs_new, -1);<br>+}<br>+<br>+/*! \brief Task data for AOR creation or updating */<br>+struct sip_options_synchronize_aor_task_data {<br>+  /*! \brief The AOR options for this AOR */<br>+   struct sip_options_aor *aor_options;<br>+ /*! \brief The AOR which contains the new configuraton */<br>+    struct ast_sip_aor *aor;<br>+     /*! \brief Optional container of existing AOR s*/<br>+    struct ao2_container *existing;<br>+      /*! \brief Whether this AOR is being added */<br>+        int added;<br>+};<br>+<br>+/*! \brief Callback function to remove a contact and its contact status from an AOR */<br>+static int sip_options_remove_contact(void *obj, void *arg, int flags)<br>+{<br>+     struct ast_sip_contact *contact = obj;<br>+       struct sip_options_aor *aor_options = arg;<br>+<br>+        sip_options_remove_contact_status(aor_options, contact);<br>+<br>+  return CMP_MATCH;<br>+}<br>+<br>+/*! \brief Determine an initial time for scheduling AOR qualifying */<br>+static int sip_options_determine_initial_qualify_time(int qualify_frequency)<br>+{<br>+  int initial_interval;<br>+        int max_time = ast_sip_get_max_initial_qualify_time();<br>+<br>+    if (max_time && max_time < qualify_frequency) {<br>+           initial_interval = max_time;<br>+ } else {<br>+             initial_interval = qualify_frequency;<br>+        }<br>+<br>+ initial_interval = (int)((initial_interval * 1000) * ast_random_double());<br>+   return 0 < initial_interval ? initial_interval : 1;<br>+}<br>+<br>+/*! \brief Set the contact status for a contact */<br>+static void sip_options_set_contact_status(struct ast_sip_contact_status *contact_status,<br>+       enum ast_sip_contact_status_type status)<br>+{<br>+ struct ast_sip_contact_status *cs_new;<br>+<br>+    /* Update the contact specific status information */<br>+ cs_new = sip_contact_status_copy(contact_status);<br>+    if (!cs_new) {<br>+               return;<br>+      }<br>+    cs_new->last_status = cs_new->status;<br>+  cs_new->status = status;<br>+<br>+       /*<br>+    * We need to always set the RTT to zero because we haven't completed<br>+     * an OPTIONS ping so RTT is unknown.  If the OPTIONS ping were still<br>+         * running it will be refreshed on the next go round anyway.<br>+  */<br>+  cs_new->rtt = 0;<br>+<br>+       ao2_link(sip_options_contact_statuses, cs_new);<br>+<br>+   if (cs_new->status != cs_new->last_status) {<br>+           ast_verb(3, "Contact %s/%s is now %s.\n",<br>+                  cs_new->aor, cs_new->uri,<br>+                      ast_sip_get_contact_status_label(cs_new->status));<br>+<br>+             ast_statsd_log_string_va("PJSIP.contacts.states.%s", AST_STATSD_GAUGE,<br>+                     "-1", 1.0, ast_sip_get_contact_status_label(cs_new->last_status));<br>+              ast_statsd_log_string_va("PJSIP.contacts.states.%s", AST_STATSD_GAUGE,<br>+                     "+1", 1.0, ast_sip_get_contact_status_label(cs_new->status));<br>+<br>+                sip_options_contact_status_update(cs_new);<br>+<br>+                ast_test_suite_event_notify("AOR_CONTACT_UPDATE",<br>+                  "Contact: %s\r\n"<br>+                  "Status: %s",<br>+                      cs_new->name,<br>+                     ast_sip_get_contact_status_label(cs_new->status));<br>+        }<br>+    ao2_ref(cs_new, -1);<br>+}<br>+<br>+/*! \brief Transition the contact status to unqualified mode */<br>+static int sip_options_set_contact_status_unqualified(void *obj, void *arg, int flags)<br>+{<br>+   struct ast_sip_contact *contact = obj;<br>+       struct ast_sip_contact_status *contact_status;<br>+<br>+    contact_status = ast_res_pjsip_find_or_create_contact_status(contact);<br>+       if (!contact_status) {<br>+               return 0;<br>+    }<br>+<br>+ switch (contact_status->status) {<br>+ case AVAILABLE:<br>+      case UNAVAILABLE:<br>+    case CREATED:<br>+                sip_options_set_contact_status(contact_status, UNKNOWN);<br>+             break;<br>+       case UNKNOWN:<br>+        case REMOVED:<br>+                break;<br>+       }<br>+<br>+ ao2_ref(contact_status, -1);<br>+<br>+      return 0;<br>+}<br>+<br>+/*! \brief Transition the contact status to qualified mode */<br>+static int sip_options_set_contact_status_qualified(void *obj, void *arg, int flags)<br>+{<br>+  struct ast_sip_contact *contact = obj;<br>+       struct ast_sip_contact_status *contact_status;<br>+<br>+    contact_status = ast_res_pjsip_find_or_create_contact_status(contact);<br>+       if (!contact_status) {<br>+               return 0;<br>+    }<br>+<br>+ switch (contact_status->status) {<br>+ case AVAILABLE:<br>+              sip_options_set_contact_status(contact_status, UNAVAILABLE);<br>+         break;<br>+       case UNAVAILABLE:<br>+    case UNKNOWN:<br>+        case CREATED:<br>+        case REMOVED:<br>+                break;<br>+       }<br>+<br>+ ao2_ref(contact_status, -1);<br>+<br>+      return 0;<br>+}<br>+<br>+/*! \brief Count AVAILABLE qualified contacts. */<br>+static int sip_options_contact_status_available_count(void *obj, void *arg, int flags)<br>+{<br>+    struct ast_sip_contact *contact = obj;<br>+       unsigned int *available = arg;<br>+       struct ast_sip_contact_status *contact_status;<br>+<br>+    contact_status = ast_res_pjsip_find_or_create_contact_status(contact);<br>+       if (!contact_status) {<br>+               return 0;<br>+    }<br>+<br>+ /* Count qualified available contacts. */<br>+    switch (contact_status->status) {<br>+ case AVAILABLE:<br>+              ++*available;<br>+                break;<br>+       case UNAVAILABLE:<br>+    case UNKNOWN:<br>+        case CREATED:<br>+        case REMOVED:<br>+                break;<br>+       }<br>+<br>+ ao2_ref(contact_status, -1);<br>+<br>+      return 0;<br>+}<br>+<br>+/*!<br>+ * \brief Function which applies configuration to an AOR options structure<br>+ * \note Run by aor_options->serializer (or management_serializer on aor_options creation)<br>+ */<br>+static void sip_options_apply_aor_configuration(struct sip_options_aor *aor_options,<br>+     struct ast_sip_aor *aor, int is_new)<br>+{<br>+     struct ao2_container *existing_contacts;<br>+     struct ast_sip_contact *contact;<br>+     struct ao2_iterator iter;<br>+<br>+ ast_debug(3, "Configuring AOR '%s' with current state of configuration and world\n",<br>+               aor_options->name);<br>+<br>+    /*<br>+    * Permanent contacts, since we receive no notification that they<br>+     * are gone, follow the same approach as AORs.  We create a copy<br>+      * of the existing container and any reused contacts are removed<br>+      * from it.  Any contacts remaining in the container after<br>+    * processing no longer exist so we need to remove their state.<br>+       */<br>+  existing_contacts = ao2_container_clone(aor_options->contacts, 0);<br>+        if (!existing_contacts) {<br>+            ast_log(LOG_WARNING, "Synchronization of AOR '%s' failed for qualify, retaining existing state\n",<br>+                 aor_options->name);<br>+               return;<br>+      }<br>+<br>+ ao2_callback(aor_options->contacts, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE,<br>+               NULL, NULL);<br>+<br>+      /* Process permanent contacts */<br>+     if (aor->permanent_contacts) {<br>+            iter = ao2_iterator_init(aor->permanent_contacts, 0);<br>+             for (; (contact = ao2_iterator_next(&iter)); ao2_ref(contact, -1)) {<br>+                     ao2_find(existing_contacts, ast_sorcery_object_get_id(contact),<br>+                              OBJ_NODATA | OBJ_UNLINK | OBJ_SEARCH_KEY);<br>+                   ao2_link(aor_options->contacts, contact);<br>+         }<br>+            ao2_iterator_destroy(&iter);<br>+     }<br>+<br>+ /*<br>+    * If this is newly added we need to see if there are any<br>+     * existing dynamic contacts to add.  Ones that are added<br>+     * after creation will occur as a result of the contact<br>+       * observer creation callback.<br>+        */<br>+  if (is_new) {<br>+                size_t prefix_len = strlen(ast_sorcery_object_get_id(aor)) + sizeof(";@") - 1;<br>+             char prefix[prefix_len + 1];<br>          struct ao2_container *contacts;<br> <br>-           aor = ast_sip_location_retrieve_aor(aor_name);<br>-               if (!aor) {<br>+          sprintf(prefix, "%s;@", ast_sorcery_object_get_id(aor)); /* Safe */<br>+                contacts = ast_sorcery_retrieve_by_prefix(ast_sip_get_sorcery(), "contact",<br>+                        prefix, prefix_len);<br>+         if (contacts) {<br>+                      ao2_container_dup(aor_options->dynamic_contacts, contacts, 0);<br>+                    ao2_ref(contacts, -1);<br>+               }<br>+    }<br>+<br>+ /* Process dynamic contacts */<br>+       iter = ao2_iterator_init(aor_options->dynamic_contacts, 0);<br>+       for (; (contact = ao2_iterator_next(&iter)); ao2_ref(contact, -1)) {<br>+             ao2_find(existing_contacts, ast_sorcery_object_get_id(contact),<br>+                      OBJ_NODATA | OBJ_UNLINK | OBJ_SEARCH_KEY);<br>+           ao2_link(aor_options->contacts, contact);<br>+ }<br>+    ao2_iterator_destroy(&iter);<br>+<br>+  /* Any contacts left no longer exist, so raise events and make them disappear */<br>+     ao2_callback(existing_contacts, OBJ_NODATA | OBJ_UNLINK,<br>+             sip_options_remove_contact, aor_options);<br>+    ao2_ref(existing_contacts, -1);<br>+<br>+   /*<br>+    * Update the available count if we transition between qualified<br>+      * and unqualified.  In the qualified case we need to start with<br>+      * 0 available as the qualify process will take care of it.  In<br>+       * the unqualified case it is based on the number of contacts<br>+         * present.<br>+   */<br>+  if (!aor->qualify_frequency) {<br>+            ao2_callback(aor_options->contacts, OBJ_NODATA,<br>+                   sip_options_set_contact_status_unqualified, NULL);<br>+           aor_options->available = ao2_container_count(aor_options->contacts);<br>+           ast_debug(3, "AOR '%s' is unqualified, number of available contacts is therefore '%d'\n",<br>+                  aor_options->name, aor_options->available);<br>+    } else if (!aor_options->qualify_frequency) {<br>+             ao2_callback(aor_options->contacts, OBJ_NODATA,<br>+                   sip_options_set_contact_status_qualified, NULL);<br>+             aor_options->available = 0;<br>+               ast_debug(3, "AOR '%s' has transitioned from unqualified to qualified, reset available contacts to 0\n",<br>+                   aor_options->name);<br>+       } else {<br>+             /*<br>+           * Count the number of AVAILABLE qualified contacts to ensure<br>+         * the count is in sync with reality.<br>+         */<br>+           aor_options->available = 0;<br>+               ao2_callback(aor_options->contacts, OBJ_NODATA,<br>+                   sip_options_contact_status_available_count, &aor_options->available);<br>+ }<br>+<br>+ aor_options->authenticate_qualify = aor->authenticate_qualify;<br>+ aor_options->qualify_timeout = aor->qualify_timeout;<br>+<br>+        /*<br>+    * If we need to stop or start the scheduled callback then do so.<br>+     * This occurs due to the following:<br>+  * 1. The qualify frequency has changed<br>+       * 2. Contacts were added when previously there were none<br>+     * 3. There are no contacts but previously there were some<br>+    */<br>+  if (aor_options->qualify_frequency != aor->qualify_frequency<br>+           || (!aor_options->sched_task && ao2_container_count(aor_options->contacts))<br>+            || (aor_options->sched_task && !ao2_container_count(aor_options->contacts))) {<br>+         if (aor_options->sched_task) {<br>+                    ast_sip_sched_task_cancel(aor_options->sched_task);<br>+                       ao2_ref(aor_options->sched_task, -1);<br>+                     aor_options->sched_task = NULL;<br>+           }<br>+<br>+         /* If there is still a qualify frequency then schedule this */<br>+               aor_options->qualify_frequency = aor->qualify_frequency;<br>+               if (aor_options->qualify_frequency<br>+                        && ao2_container_count(aor_options->contacts)) {<br>+                  aor_options->sched_task = ast_sip_schedule_task(aor_options->serializer,<br>+                               sip_options_determine_initial_qualify_time(aor_options->qualify_frequency),<br>+                               sip_options_qualify_aor, ast_taskprocessor_name(aor_options->serializer),<br>+                         aor_options, AST_SIP_SCHED_TASK_VARIABLE | AST_SIP_SCHED_TASK_DATA_AO2);<br>+                     if (!aor_options->sched_task) {<br>+                           ast_log(LOG_ERROR, "Unable to schedule qualify for contacts of AOR '%s'\n",<br>+                                        aor_options->name);<br>+                       }<br>+            }<br>+    }<br>+<br>+ ast_debug(3, "AOR '%s' now has %d available contacts\n", aor_options->name,<br>+             aor_options->available);<br>+}<br>+<br>+/*!<br>+ * \brief Task to synchronize an AOR with our local state<br>+ * \note Run by aor_options->serializer (or management_serializer on aor_options creation)<br>+ */<br>+static int sip_options_synchronize_aor_task(void *obj)<br>+{<br>+      struct sip_options_synchronize_aor_task_data *task_data = obj;<br>+       int i;<br>+<br>+    ast_debug(3, "Synchronizing AOR '%s' with current state of configuration and world\n",<br>+             task_data->aor_options->name);<br>+<br>+      sip_options_apply_aor_configuration(task_data->aor_options, task_data->aor,<br>+            task_data->added);<br>+<br>+     /*<br>+    * Endpoint state compositors are removed in this operation but not<br>+   * added.  To reduce the amount of work done they are done later.  In<br>+         * the mean time things can still qualify and once an endpoint state<br>+  * compositor is added to the AOR it will be updated with the current<br>+         * state.<br>+     */<br>+  for (i = 0; i < AST_VECTOR_SIZE(&task_data->aor_options->compositors); ++i) {<br>+           struct sip_options_endpoint_state_compositor *endpoint_state_compositor;<br>+<br>+          endpoint_state_compositor = AST_VECTOR_GET(&task_data->aor_options->compositors, i);<br>+<br>+            ao2_lock(endpoint_state_compositor);<br>+         endpoint_state_compositor->active = 0;<br>+            sip_options_update_endpoint_state_compositor_aor(endpoint_state_compositor,<br>+                  task_data->aor_options->name, REMOVED);<br>+                ao2_unlock(endpoint_state_compositor);<br>+       }<br>+    AST_VECTOR_RESET(&task_data->aor_options->compositors, ao2_cleanup);<br>+<br>+    return 0;<br>+}<br>+<br>+/*!<br>+ * \brief Synchronize an AOR with our local state<br>+ * \note Run by management_serializer<br>+ */<br>+static int sip_options_synchronize_aor(void *obj, void *arg, int flags)<br>+{<br>+       struct sip_options_synchronize_aor_task_data task_data = {<br>+           .aor = obj,<br>+          .existing = arg,<br>+     };<br>+<br>+        task_data.aor_options = ao2_find(sip_options_aors,<br>+           ast_sorcery_object_get_id(task_data.aor), OBJ_SEARCH_KEY);<br>+   if (!task_data.aor_options) {<br>+                task_data.aor_options = sip_options_aor_alloc(task_data.aor);<br>+                if (!task_data.aor_options) {<br>+                        return 0;<br>+            }<br>+<br>+         task_data.added = 1;<br>+<br>+              /* Nothing is aware of this AOR yet so we can just update it in this thread */<br>+               sip_options_synchronize_aor_task(&task_data);<br>+            ao2_link(sip_options_aors, task_data.aor_options);<br>+   } else {<br>+             /* This AOR already exists so we have to do manipulation in its serializer */<br>+                ast_sip_push_task_wait_serializer(task_data.aor_options->serializer,<br>+                      sip_options_synchronize_aor_task, &task_data);<br>+   }<br>+<br>+ ao2_ref(task_data.aor_options, -1);<br>+<br>+       if (task_data.existing) {<br>+            ao2_find(task_data.existing, ast_sorcery_object_get_id(task_data.aor),<br>+                       OBJ_SEARCH_KEY | OBJ_UNLINK | OBJ_NODATA);<br>+   }<br>+<br>+ return 0;<br>+}<br>+<br>+/*! \brief Destructor for endpoint state compositors */<br>+static void sip_options_endpoint_state_compositor_dtor(void *obj)<br>+{<br>+   struct sip_options_endpoint_state_compositor *endpoint_state_compositor = obj;<br>+<br>+    ao2_cleanup(endpoint_state_compositor->aor_statuses);<br>+}<br>+<br>+/*! \brief Hashing function for endpoint AOR status */<br>+AO2_STRING_FIELD_HASH_FN(sip_options_endpoint_aor_status, name);<br>+<br>+/*! \brief Comparator function for endpoint AOR status */<br>+AO2_STRING_FIELD_CMP_FN(sip_options_endpoint_aor_status, name);<br>+<br>+/*! \brief Find (or create) an endpoint state compositor */<br>+static struct sip_options_endpoint_state_compositor *sip_options_endpoint_state_compositor_find_or_alloc(const struct ast_sip_endpoint *endpoint)<br>+{<br>+        struct sip_options_endpoint_state_compositor *endpoint_state_compositor;<br>+<br>+  ao2_lock(sip_options_endpoint_state_compositors);<br>+    endpoint_state_compositor = ao2_find(sip_options_endpoint_state_compositors,<br>+         ast_sorcery_object_get_id(endpoint), OBJ_SEARCH_KEY | OBJ_NOLOCK);<br>+   if (endpoint_state_compositor) {<br>+             ao2_unlock(sip_options_endpoint_state_compositors);<br>+          return endpoint_state_compositor;<br>+    }<br>+<br>+ endpoint_state_compositor = ao2_alloc(sizeof(*endpoint_state_compositor)<br>+             + strlen(ast_sorcery_object_get_id(endpoint)) + 1,<br>+           sip_options_endpoint_state_compositor_dtor);<br>+ if (!endpoint_state_compositor) {<br>+            ao2_unlock(sip_options_endpoint_state_compositors);<br>+          return NULL;<br>+ }<br>+<br>+ /*<br>+    * NOTE: The endpoint_state_compositor->aor_statuses container is<br>+  * externally protected by the endpoint_state_compositor lock.<br>+        */<br>+  endpoint_state_compositor->aor_statuses = ao2_container_alloc_hash(<br>+               AO2_ALLOC_OPT_LOCK_NOLOCK, 0, AOR_STATUS_BUCKETS,<br>+            sip_options_endpoint_aor_status_hash_fn, NULL,<br>+               sip_options_endpoint_aor_status_cmp_fn);<br>+     if (!endpoint_state_compositor->aor_statuses) {<br>+           ao2_unlock(sip_options_endpoint_state_compositors);<br>+          ao2_ref(endpoint_state_compositor, -1);<br>+              return NULL;<br>+ }<br>+<br>+ strcpy(endpoint_state_compositor->name, ast_sorcery_object_get_id(endpoint)); /* SAFE */<br>+<br>+       ao2_link_flags(sip_options_endpoint_state_compositors, endpoint_state_compositor,<br>+            OBJ_NOLOCK);<br>+ ao2_unlock(sip_options_endpoint_state_compositors);<br>+<br>+       return endpoint_state_compositor;<br>+}<br>+<br>+/*! \brief Task details for adding an AOR to an endpoint state compositor */<br>+struct sip_options_endpoint_compositor_task_data {<br>+ /*! \brief The AOR options that the endpoint state compositor should be added to */<br>+  struct sip_options_aor *aor_options;<br>+ /*! \brief The endpoint state compositor */<br>+  struct sip_options_endpoint_state_compositor *endpoint_state_compositor;<br>+};<br>+<br>+/*!<br>+ * \brief Task which adds an AOR to an endpoint state compositor<br>+ * \note Run by aor_options->serializer<br>+ */<br>+static int sip_options_endpoint_compositor_add_task(void *obj)<br>+{<br>+    struct sip_options_endpoint_compositor_task_data *task_data = obj;<br>+<br>+        ast_debug(3, "Adding endpoint compositor '%s' to AOR '%s'\n",<br>+              task_data->endpoint_state_compositor->name, task_data->aor_options->name);<br>+<br>+    if (AST_VECTOR_APPEND(&task_data->aor_options->compositors,<br>+                ao2_bump(task_data->endpoint_state_compositor))) {<br>+                /* Failed to add so no need to update the endpoint status.  Nothing changed. */<br>+              ao2_cleanup(task_data->endpoint_state_compositor);<br>+                return 0;<br>+    }<br>+<br>+ ao2_lock(task_data->endpoint_state_compositor);<br>+   sip_options_update_endpoint_state_compositor_aor(task_data->endpoint_state_compositor,<br>+            task_data->aor_options->name,<br>+          task_data->aor_options->available ? AVAILABLE : UNAVAILABLE);<br>+  ao2_unlock(task_data->endpoint_state_compositor);<br>+<br>+      return 0;<br>+}<br>+<br>+/*!<br>+ * \brief Task which adds removes an AOR from an endpoint state compositor<br>+ * \note Run by aor_options->serializer<br>+ */<br>+static int sip_options_endpoint_compositor_remove_task(void *obj)<br>+{<br>+       struct sip_options_endpoint_compositor_task_data *task_data = obj;<br>+   int i;<br>+<br>+    ast_debug(3, "Removing endpoint compositor '%s' from AOR '%s'\n",<br>+          task_data->endpoint_state_compositor->name,<br>+            task_data->aor_options->name);<br>+<br>+      for (i = 0; i < AST_VECTOR_SIZE(&task_data->aor_options->compositors); ++i) {<br>+           struct sip_options_endpoint_state_compositor *endpoint_state_compositor;<br>+<br>+          endpoint_state_compositor = AST_VECTOR_GET(&task_data->aor_options->compositors, i);<br>+               if (endpoint_state_compositor != task_data->endpoint_state_compositor) {<br>                   continue;<br>             }<br> <br>-         contacts = ast_sip_location_retrieve_aor_contacts(aor);<br>-              if (contacts) {<br>-                      ast_cli(cli_fd, "Sending qualify to endpoint %s\n", endpoint_name);<br>-                        ao2_callback_data(contacts, OBJ_NODATA, cli_on_contact, &cli_fd, endpoint);<br>-                      ao2_ref(contacts, -1);<br>-               }<br>-<br>-         ao2_ref(aor, -1);<br>+            AST_VECTOR_REMOVE(&task_data->aor_options->compositors, i, 0);<br>+             ao2_ref(endpoint_state_compositor, -1);<br>+              break;<br>        }<br>+<br>  return 0;<br> }<br>+<br>+/*!<br>+ * \brief Synchronize an endpoint with our local state<br>+ * \note Run by management_serializer<br>+ */<br>+static int sip_options_synchronize_endpoint(void *obj, void *arg, int flags)<br>+{<br>+     struct ast_sip_endpoint *endpoint = obj;<br>+     struct ast_sip_aor *aor = arg;<br>+       char *aors;<br>+  char *aor_name;<br>+      struct sip_options_endpoint_compositor_task_data task_data = { NULL, };<br>+<br>+   if (ast_strlen_zero(endpoint->aors)) {<br>+            /* There are no AORs, so really... who the heck knows */<br>+             ast_debug(3, "Endpoint '%s' is not interested in any AORs so not creating endpoint state compositor\n",<br>+                    ast_sorcery_object_get_id(endpoint));<br>+                return 0;<br>+    }<br>+<br>+ ast_debug(3, "Synchronizing endpoint '%s' with AORs '%s'\n",<br>+               ast_sorcery_object_get_id(endpoint), endpoint->aors);<br>+<br>+  aors = ast_strdupa(endpoint->aors);<br>+       while ((aor_name = ast_strip(strsep(&aors, ",")))) {<br>+           if (ast_strlen_zero(aor_name)) {<br>+                     continue;<br>+            }<br>+            if (aor && strcasecmp(ast_sorcery_object_get_id(aor), aor_name)) {<br>+                   ast_debug(3, "Filtered AOR '%s' on endpoint '%s' as we are looking for '%s'\n",<br>+                            aor_name, ast_sorcery_object_get_id(endpoint),<br>+                               ast_sorcery_object_get_id(aor));<br>+                     continue;<br>+            }<br>+<br>+         task_data.aor_options = ao2_find(sip_options_aors, aor_name, OBJ_SEARCH_KEY);<br>+                if (!task_data.aor_options) {<br>+                        /*<br>+                    * They have referenced an invalid AOR. If that's all they've<br>+                         * done we will set them to offline at the end.<br>+                       */<br>+                  ast_debug(3, "Endpoint '%s' referenced invalid AOR '%s'\n",<br>+                                ast_sorcery_object_get_id(endpoint), aor_name);<br>+                      continue;<br>+            }<br>+<br>+         if (!task_data.endpoint_state_compositor) {<br>+                  /*<br>+                    * We create an endpoint state compositor only after we know<br>+                  * for sure we need it.<br>+                       */<br>+                  task_data.endpoint_state_compositor =<br>+                                sip_options_endpoint_state_compositor_find_or_alloc(endpoint);<br>+                       if (!task_data.endpoint_state_compositor) {<br>+                          ast_log(LOG_WARNING,<br>+                                 "Could not create endpoint state compositor for '%s', endpoint state will be incorrect\n",<br>+                                 ast_sorcery_object_get_id(endpoint));<br>+                                ao2_ref(task_data.aor_options, -1);<br>+                          ast_sip_persistent_endpoint_update_state(ast_sorcery_object_get_id(endpoint),<br>+                                        AST_ENDPOINT_OFFLINE);<br>+                               return 0;<br>+                    }<br>+            }<br>+<br>+         /* We use a synchronous task so that we don't flood the system */<br>+                ast_sip_push_task_wait_serializer(task_data.aor_options->serializer,<br>+                      sip_options_endpoint_compositor_add_task, &task_data);<br>+<br>+                ao2_ref(task_data.aor_options, -1);<br>+<br>+               /*<br>+            * If we filtered on a specific AOR name then the endpoint can<br>+                * only reference it once so break early.<br>+             */<br>+          if (aor) {<br>+                   break;<br>+               }<br>+    }<br>+<br>+ if (task_data.endpoint_state_compositor) {<br>+           /*<br>+            * If an endpoint state compositor is present determine the current state<br>+             * of the endpoint and update it.<br>+             */<br>+          ao2_lock(task_data.endpoint_state_compositor);<br>+               task_data.endpoint_state_compositor->active = 1;<br>+          ast_sip_persistent_endpoint_update_state(ast_sorcery_object_get_id(endpoint),<br>+                        sip_options_get_endpoint_state_compositor_state(task_data.endpoint_state_compositor));<br>+               ao2_unlock(task_data.endpoint_state_compositor);<br>+<br>+          ao2_ref(task_data.endpoint_state_compositor, -1);<br>+    } else {<br>+             /* If there is none then they may have referenced an invalid AOR or none at all */<br>+           ast_debug(3, "Endpoint '%s' has no AORs feeding it, setting it to offline state as default\n",<br>+                     ast_sorcery_object_get_id(endpoint));<br>+                ast_sip_persistent_endpoint_update_state(ast_sorcery_object_get_id(endpoint),<br>+                        AST_ENDPOINT_OFFLINE);<br>+       }<br>+<br>+ return 0;<br>+}<br>+<br>+/*!<br>+ * \brief Task which removes an AOR from all of the ESCs it is reporting to<br>+ * \note Run by aor_options->serializer<br>+ */<br>+static int sip_options_aor_remove_task(void *obj)<br>+{<br>+      struct sip_options_aor *aor_options = obj;<br>+<br>+        sip_options_notify_endpoint_state_compositors(aor_options, REMOVED);<br>+<br>+      if (aor_options->sched_task) {<br>+            ast_sip_sched_task_cancel(aor_options->sched_task);<br>+               ao2_ref(aor_options->sched_task, -1);<br>+             aor_options->sched_task = NULL;<br>+   }<br>+<br>+ return 0;<br>+}<br>+<br>+/*!<br>+ * \brief Callback which removes any unused AORs that remained after reloading<br>+ * \note Run by management_serializer<br>+ */<br>+static int sip_options_unused_aor(void *obj, void *arg, int flags)<br>+{<br>+       struct sip_options_aor *aor_options = obj;<br>+<br>+        ast_debug(3, "AOR '%s' is no longer configured, removing it\n", aor_options->name);<br>+<br>+  ast_sip_push_task_wait_serializer(aor_options->serializer, sip_options_aor_remove_task,<br>+           aor_options);<br>+        ao2_unlink(sip_options_aors, aor_options);<br>+<br>+        return CMP_MATCH;<br>+}<br>+<br>+/*!<br>+ * \brief Callback function used to unlink and remove event state compositors that have no AORs feeding them<br>+ * \note Run by management_serializer<br>+ */<br>+static int sip_options_unused_endpoint_state_compositor(void *obj, void *arg, int flags)<br>+{<br>+   struct sip_options_endpoint_state_compositor *endpoint_state_compositor = obj;<br>+<br>+    if (ao2_container_count(endpoint_state_compositor->aor_statuses)) {<br>+               return 0;<br>+    }<br>+<br>+ /* No AORs are feeding this endpoint state compositor */<br>+     ast_sip_persistent_endpoint_update_state(endpoint_state_compositor->name,<br>+         AST_ENDPOINT_OFFLINE);<br>+<br>+    return CMP_MATCH;<br>+}<br>+<br>+/*! \brief Structure which contains information required to synchronize */<br>+struct sip_options_synchronize_task_data {<br>+   /*! \brief Whether this is a reload or not */<br>+        int reload;<br>+};<br>+<br>+/*!<br>+ * \brief Task to synchronize our local container of AORs and endpoint state compositors with the current configuration<br>+ * \note Run by management_serializer<br>+ */<br>+static int sip_options_synchronize_task(void *obj)<br>+{<br>+   struct sip_options_synchronize_task_data *task_data = obj;<br>+   struct ao2_container *existing = NULL;<br>+       struct ao2_container *objects;<br>+<br>+    /*<br>+    * When reloading we keep track of the existing AORs so we can<br>+        * terminate old ones that are no longer referenced or used.<br>+  */<br>+  if (task_data->reload) {<br>+          existing = ao2_container_clone(sip_options_aors, 0);<br>+         if (!existing) {<br>+                     return 0;<br>+            }<br>+    }<br>+<br>+ objects = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "aor",<br>+             AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);<br>+   if (objects) {<br>+               /* Go through the returned AORs and synchronize with our local state */<br>+              ao2_callback(objects, OBJ_NODATA, sip_options_synchronize_aor, existing);<br>+            ao2_ref(objects, -1);<br>+        }<br>+<br>+ /*<br>+    * Any AORs remaining in existing are no longer referenced by<br>+         * the current container of AORs we retrieved, so remove them.<br>+        */<br>+  if (existing) {<br>+              ao2_callback(existing, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK,<br>+                       sip_options_unused_aor, NULL);<br>+               ao2_ref(existing, -1);<br>+       }<br>+<br>+ objects = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "endpoint",<br>+                AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);<br>+   if (objects) {<br>+               /* Go through the provided endpoints and update AORs */<br>+              ao2_callback(objects, OBJ_NODATA, sip_options_synchronize_endpoint, NULL);<br>+           ao2_ref(objects, -1);<br>+        }<br>+<br>+ /*<br>+    * All endpoint state compositors that don't have any AORs<br>+        * feeding them information can be removed.  If they end<br>+      * up getting needed later they'll just be recreated.<br>+     */<br>+  ao2_callback(sip_options_endpoint_state_compositors,<br>+         OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK,<br>+              sip_options_unused_endpoint_state_compositor, NULL);<br>+<br>+      return 0;<br>+}<br>+<br>+/*! \brief Synchronize our local container of AORs and endpoint state compositors with the current configuration */<br>+static void sip_options_synchronize(int reload)<br>+{<br>+ struct sip_options_synchronize_task_data task_data = {<br>+               .reload = reload,<br>+    };<br>+<br>+        ast_sip_push_task_wait_serializer(management_serializer, sip_options_synchronize_task,<br>+               &task_data);<br>+}<br>+<br>+/*!<br>+ * \brief Unlink AORs feeding the endpoint status compositor<br>+ * \note Run by management_serializer<br>+ */<br>+static void sip_options_endpoint_unlink_aor_feeders(struct ast_sip_endpoint *endpoint,<br>+  struct sip_options_endpoint_state_compositor *endpoint_state_compositor)<br>+{<br>+ struct ao2_iterator it_aor_statuses;<br>+ struct sip_options_endpoint_aor_status *aor_status;<br>+  struct sip_options_endpoint_compositor_task_data task_data = {<br>+               .endpoint_state_compositor = endpoint_state_compositor,<br>+      };<br>+<br>+        ao2_lock(endpoint_state_compositor);<br>+ endpoint_state_compositor->active = 0;<br>+<br>+ /* Unlink AOR feeders pointing to endpoint */<br>+        it_aor_statuses = ao2_iterator_init(endpoint_state_compositor->aor_statuses, 0);<br>+  for (; (aor_status = ao2_iterator_next(&it_aor_statuses)); ao2_ref(aor_status, -1)) {<br>+            task_data.aor_options = ao2_find(sip_options_aors, aor_status->name,<br>+                      OBJ_SEARCH_KEY);<br>+             if (!task_data.aor_options) {<br>+                        continue;<br>+            }<br>+<br>+         ast_debug(3, "Removing endpoint state compositor '%s' from AOR '%s'\n",<br>+                    ast_sorcery_object_get_id(endpoint), aor_status->name);<br>+           ao2_unlock(endpoint_state_compositor);<br>+               ast_sip_push_task_wait_serializer(task_data.aor_options->serializer,<br>+                      sip_options_endpoint_compositor_remove_task, &task_data);<br>+                ao2_lock(endpoint_state_compositor);<br>+         ao2_ref(task_data.aor_options, -1);<br>+  }<br>+    ao2_iterator_destroy(&it_aor_statuses);<br>+<br>+       /*<br>+    * We do not need to remove the AOR feeder status memory from the<br>+     * aor_statuses container.  The endpoint_state_compositor is about<br>+    * to die and do it for us.<br>+   */<br>+<br>+       ao2_unlock(endpoint_state_compositor);<br>+}<br>+<br>+/*!<br>+ * \brief Task to delete an endpoint from the known universe<br>+ * \note Run by management_serializer<br>+ */<br>+static int sip_options_endpoint_observer_deleted_task(void *obj)<br>+{<br>+      struct ast_sip_endpoint *endpoint = obj;<br>+     struct sip_options_endpoint_state_compositor *endpoint_state_compositor;<br>+<br>+  endpoint_state_compositor = ao2_find(sip_options_endpoint_state_compositors,<br>+         ast_sorcery_object_get_id(endpoint), OBJ_SEARCH_KEY | OBJ_UNLINK);<br>+   if (!endpoint_state_compositor) {<br>+            return 0;<br>+    }<br>+<br>+ ast_debug(3, "Endpoint '%s' has been deleted, removing endpoint state compositor from AORs\n",<br>+             ast_sorcery_object_get_id(endpoint));<br>+        sip_options_endpoint_unlink_aor_feeders(endpoint, endpoint_state_compositor);<br>+        ao2_ref(endpoint_state_compositor, -1);<br>+<br>+   return 0;<br>+}<br>+<br>+/*! \brief Observer callback invoked on endpoint deletion */<br>+static void endpoint_observer_deleted(const void *obj)<br>+{<br>+ ast_sip_push_task_wait_serializer(management_serializer,<br>+             sip_options_endpoint_observer_deleted_task, (void *) obj);<br>+}<br>+<br>+/*!<br>+ * \brief Task to synchronize the endpoint<br>+ * \note Run by management_serializer<br>+ */<br>+static int sip_options_endpoint_observer_modified_task(void *obj)<br>+{<br>+   struct ast_sip_endpoint *endpoint = obj;<br>+     struct sip_options_endpoint_state_compositor *endpoint_state_compositor;<br>+<br>+  ast_debug(3, "Endpoint '%s' has been created or modified, updating state\n",<br>+               ast_sorcery_object_get_id(endpoint));<br>+<br>+     endpoint_state_compositor = ao2_find(sip_options_endpoint_state_compositors,<br>+         ast_sorcery_object_get_id(endpoint), OBJ_SEARCH_KEY | OBJ_UNLINK);<br>+   if (endpoint_state_compositor) {<br>+             /* Unlink the AORs currently feeding the endpoint. */<br>+                sip_options_endpoint_unlink_aor_feeders(endpoint, endpoint_state_compositor);<br>+                ao2_ref(endpoint_state_compositor, -1);<br>+      }<br>+<br>+ /* Connect the AORs that now feed the endpoint. */<br>+   sip_options_synchronize_endpoint(endpoint, NULL, 0);<br>+ return 0;<br>+}<br>+<br>+/*! \brief Observer callback invoked on endpoint creation or modification */<br>+static void endpoint_observer_modified(const void *obj)<br>+{<br>+        ast_sip_push_task_wait_serializer(management_serializer,<br>+             sip_options_endpoint_observer_modified_task, (void *)obj);<br>+}<br>+<br>+/*! \brief Observer callbacks for endpoints */<br>+static const struct ast_sorcery_observer endpoint_observer_callbacks = {<br>+        .created = endpoint_observer_modified,<br>+       .updated = endpoint_observer_modified,<br>+       .deleted = endpoint_observer_deleted,<br>+};<br>+<br>+/*!<br>+ * \brief Task to synchronize an AOR with our local state<br>+ * \note Run by aor_options->serializer<br>+ */<br>+static int sip_options_update_aor_task(void *obj)<br>+{<br>+   struct sip_options_synchronize_aor_task_data *task_data = obj;<br>+       int available = task_data->aor_options->available;<br>+<br>+  ast_debug(3, "Individually updating AOR '%s' with current state of configuration and world\n",<br>+             task_data->aor_options->name);<br>+<br>+      sip_options_apply_aor_configuration(task_data->aor_options, task_data->aor,<br>+            task_data->added);<br>+<br>+     if (!available && task_data->aor_options->available) {<br>+         ast_debug(3, "After modifying AOR '%s' it has now become available\n",<br>+                     task_data->aor_options->name);<br>+         sip_options_notify_endpoint_state_compositors(task_data->aor_options, AVAILABLE);<br>+ } else if (available && !task_data->aor_options->available) {<br>+          ast_debug(3, "After modifying AOR '%s' it has become unavailable\n",<br>+                       task_data->aor_options->name);<br>+         sip_options_notify_endpoint_state_compositors(task_data->aor_options, UNAVAILABLE);<br>+       }<br>+<br>+ return 0;<br>+}<br>+<br>+/*!<br>+ * \brief Task to synchronize the AOR<br>+ * \note Run by management_serializer<br>+ */<br>+static int sip_options_aor_observer_modified_task(void *obj)<br>+{<br>+      struct ast_sip_aor *aor = obj;<br>+       struct sip_options_aor *aor_options;<br>+<br>+      aor_options = ao2_find(sip_options_aors, ast_sorcery_object_get_id(aor),<br>+             OBJ_SEARCH_KEY);<br>+     if (!aor_options) {<br>+          struct ao2_container *endpoints;<br>+<br>+          aor_options = sip_options_aor_alloc(aor);<br>+            if (!aor_options) {<br>+                  return 0;<br>+            }<br>+<br>+         /*<br>+            * This is a newly added AOR and we need to establish any<br>+             * endpoint state compositors that may reference only the<br>+             * AOR.  If these need to be updated later then they'll<br>+           * be done by modifying the endpoint or issuing a reload.<br>+             */<br>+          sip_options_apply_aor_configuration(aor_options, aor, 1);<br>+            ao2_link(sip_options_aors, aor_options);<br>+<br>+          /*<br>+            * Using LIKE doesn't seem to work very well with non-realtime so we<br>+              * fetch everything right now and do a filter on our side.<br>+            */<br>+          endpoints = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(),<br>+                    "endpoint", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);<br>+             if (endpoints) {<br>+                     ao2_callback(endpoints, OBJ_NODATA, sip_options_synchronize_endpoint, aor);<br>+                  ao2_ref(endpoints, -1);<br>+              }<br>+    } else {<br>+             struct sip_options_synchronize_aor_task_data task_data = {<br>+                   .aor_options = aor_options,<br>+                  .aor = aor,<br>+          };<br>+<br>+                /*<br>+            * If this AOR was modified we have to do our work in its serializer<br>+          * instead of this thread to ensure that things aren't modified by<br>+                * multiple threads.<br>+          */<br>+          ast_sip_push_task_wait_serializer(aor_options->serializer,<br>+                        sip_options_update_aor_task, &task_data);<br>+        }<br>+<br>+ ao2_ref(aor_options, -1);<br>+<br>+ return 0;<br>+}<br>+<br>+/*! \brief Observer callback invoked on AOR creation or modification */<br>+static void aor_observer_modified(const void *obj)<br>+{<br>+  ast_sip_push_task_wait_serializer(management_serializer,<br>+             sip_options_aor_observer_modified_task, (void *) obj);<br>+}<br>+<br>+/*!<br>+ * \brief Task to delete an AOR from the known universe<br>+ * \note Run by management_serializer<br>+ */<br>+static int sip_options_aor_observer_deleted_task(void *obj)<br>+{<br>+        struct ast_sip_aor *aor = obj;<br>+       struct sip_options_aor *aor_options;<br>+<br>+      aor_options = ao2_find(sip_options_aors, ast_sorcery_object_get_id(aor),<br>+             OBJ_SEARCH_KEY | OBJ_UNLINK);<br>+        if (!aor_options) {<br>+          return 0;<br>+    }<br>+<br>+ ast_debug(3, "AOR '%s' has been deleted, removing it\n", aor_options->name);<br>+<br>+ ast_sip_push_task_wait_serializer(aor_options->serializer, sip_options_aor_remove_task,<br>+           aor_options);<br>+        ao2_ref(aor_options, -1);<br>+<br>+ return 0;<br>+}<br>+<br>+/*! \brief Observer callback invoked on AOR deletion */<br>+static void aor_observer_deleted(const void *obj)<br>+{<br>+   ast_sip_push_task_wait_serializer(management_serializer,<br>+             sip_options_aor_observer_deleted_task, (void *) obj);<br>+}<br>+<br>+/*! \brief Observer callbacks for AORs */<br>+static const struct ast_sorcery_observer aor_observer_callbacks = {<br>+       .created = aor_observer_modified,<br>+    .updated = aor_observer_modified,<br>+    .deleted = aor_observer_deleted,<br>+};<br>+<br>+/*! \brief Task details for adding an AOR to an endpoint state compositor */<br>+struct sip_options_contact_observer_task_data {<br>+    /*! \brief The AOR options that the contact is referring to */<br>+       struct sip_options_aor *aor_options;<br>+ /*! \brief The contact itself */<br>+     struct ast_sip_contact *contact;<br>+};<br>+<br>+/*!<br>+ * \brief Task which adds a dynamic contact to an AOR<br>+ * \note Run by aor_options->serializer<br>+ */<br>+static int sip_options_contact_add_task(void *obj)<br>+{<br>+   struct sip_options_contact_observer_task_data *task_data = obj;<br>+      struct ast_sip_contact_status *contact_status;<br>+<br>+    ao2_link(task_data->aor_options->dynamic_contacts, task_data->contact);<br>+     ao2_link(task_data->aor_options->contacts, task_data->contact);<br>+<br>+  contact_status = ast_res_pjsip_find_or_create_contact_status(task_data->contact);<br>+ if (contact_status) {<br>+                if (!task_data->aor_options->qualify_frequency<br>+                 && contact_status->status == CREATED) {<br>+                   sip_options_set_contact_status(contact_status, UNKNOWN);<br>+             }<br>+            ao2_ref(contact_status, -1);<br>+ }<br>+<br>+ if (task_data->aor_options->qualify_frequency) {<br>+               /* If this is the first contact we need to schedule up qualification */<br>+              if (ao2_container_count(task_data->aor_options->contacts) == 1) {<br>+                      ast_debug(3, "Starting scheduled callback on AOR '%s' for qualifying as there is now a contact on it\n",<br>+                           task_data->aor_options->name);<br>+                 /*<br>+                    * We immediately schedule the initial qualify so that we get<br>+                         * reachable/unreachable as soon as possible.  Realistically<br>+                  * since they pretty much just registered they should be<br>+                      * reachable.<br>+                         */<br>+                  if (task_data->aor_options->sched_task) {<br>+                              ast_sip_sched_task_cancel(task_data->aor_options->sched_task);<br>+                         ao2_ref(task_data->aor_options->sched_task, -1);<br>+                               task_data->aor_options->sched_task = NULL;<br>+                     }<br>+                    task_data->aor_options->sched_task = ast_sip_schedule_task(<br>+                            task_data->aor_options->serializer, 1, sip_options_qualify_aor,<br>+                                ast_taskprocessor_name(task_data->aor_options->serializer),<br>+                            task_data->aor_options,<br>+                           AST_SIP_SCHED_TASK_VARIABLE | AST_SIP_SCHED_TASK_DATA_AO2);<br>+                  if (!task_data->aor_options->sched_task) {<br>+                             ast_log(LOG_ERROR, "Unable to schedule qualify for contacts of AOR '%s'\n",<br>+                                        task_data->aor_options->name);<br>+                 }<br>+            }<br>+    } else {<br>+             /*<br>+            * If this was the first contact added to a non-qualified AOR then<br>+            * it should become available.<br>+                */<br>+          task_data->aor_options->available =<br>+                    ao2_container_count(task_data->aor_options->contacts);<br>+         if (task_data->aor_options->available == 1) {<br>+                  ast_debug(3, "An unqualified contact has been added to AOR '%s' so it is now available\n",<br>+                         task_data->aor_options->name);<br>+                 sip_options_notify_endpoint_state_compositors(task_data->aor_options,<br>+                             AVAILABLE);<br>+          }<br>+    }<br>+<br>+ return 0;<br>+}<br>+<br>+/*!<br>+ * \brief Task to add a dynamic contact to an AOR in its serializer<br>+ * \note Run by management_serializer<br>+ */<br>+static int sip_options_contact_add_management_task(void *obj)<br>+{<br>+       struct sip_options_contact_observer_task_data task_data;<br>+<br>+  task_data.contact = obj;<br>+     task_data.aor_options = ao2_find(sip_options_aors, task_data.contact->aor,<br>+                OBJ_SEARCH_KEY);<br>+     if (!task_data.aor_options) {<br>+                struct ast_sip_aor *aor;<br>+<br>+          /*<br>+            * The only reason this would occur is if the AOR was sourced<br>+                 * after the last reload happened.  To handle this we fetch the<br>+               * AOR and treat it as if we received notification that it had<br>+                * been created.  This will create the needed AOR feeder<br>+              * compositor and will cause any associated contact statuses and<br>+              * endpoint state compositors to also get created if needed.<br>+          */<br>+          aor = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "aor",<br>+                     task_data.contact->aor);<br>+          if (aor) {<br>+                   sip_options_aor_observer_modified_task(aor);<br>+                 ao2_ref(aor, -1);<br>+            }<br>+            return 0;<br>+    }<br>+<br>+ ast_sip_push_task_wait_serializer(task_data.aor_options->serializer,<br>+              sip_options_contact_add_task, &task_data);<br>+       ao2_ref(task_data.aor_options, -1);<br>+<br>+       return 0;<br>+}<br>+<br>+/*! \brief Observer callback invoked on contact creation */<br>+static void contact_observer_created(const void *obj)<br>+{<br>+   ast_sip_push_task_wait_serializer(management_serializer,<br>+             sip_options_contact_add_management_task, (void *) obj);<br>+}<br>+<br>+/*!<br>+ * \brief Task which updates a dynamic contact to an AOR<br>+ * \note Run by aor_options->serializer<br>+ */<br>+static int sip_options_contact_update_task(void *obj)<br>+{<br>+       struct sip_options_contact_observer_task_data *task_data = obj;<br>+      struct ast_sip_contact_status *contact_status;<br>+<br>+    contact_status = ast_sip_get_contact_status(task_data->contact);<br>+  if (contact_status) {<br>+                switch (contact_status->status) {<br>+         case CREATED:<br>+                        sip_options_set_contact_status(contact_status, UNKNOWN);<br>+                     break;<br>+               case UNAVAILABLE:<br>+            case AVAILABLE:<br>+              case UNKNOWN:<br>+                        /* Refresh the ContactStatus AMI events. */<br>+                  sip_options_contact_status_update(contact_status);<br>+                   break;<br>+               case REMOVED:<br>+                        break;<br>+               }<br>+            ao2_ref(contact_status, -1);<br>+ }<br>+<br>+ ao2_ref(task_data->contact, -1);<br>+  ao2_ref(task_data->aor_options, -1);<br>+      ast_free(task_data);<br>+ return 0;<br>+}<br>+<br>+/*! \brief Observer callback invoked on contact update */<br>+static void contact_observer_updated(const void *obj)<br>+{<br>+     struct sip_options_contact_observer_task_data *task_data;<br>+<br>+ task_data = ast_malloc(sizeof(*task_data));<br>+  if (!task_data) {<br>+            return;<br>+      }<br>+<br>+ task_data->contact = (struct ast_sip_contact *) obj;<br>+      task_data->aor_options = ao2_find(sip_options_aors, task_data->contact->aor,<br>+                OBJ_SEARCH_KEY);<br>+     if (!task_data->aor_options) {<br>+            ast_free(task_data);<br>+         return;<br>+      }<br>+<br>+ ao2_ref(task_data->contact, +1);<br>+  if (ast_sip_push_task(task_data->aor_options->serializer,<br>+              sip_options_contact_update_task, task_data)) {<br>+               ao2_ref(task_data->contact, -1);<br>+          ao2_ref(task_data->aor_options, -1);<br>+              ast_free(task_data);<br>+ }<br>+}<br>+<br>+/*!<br>+ * \brief Task which deletes a dynamic contact from an AOR<br>+ * \note Run by aor_options->serializer<br>+ */<br>+static int sip_options_contact_delete_task(void *obj)<br>+{<br>+   struct sip_options_contact_observer_task_data *task_data = obj;<br>+<br>+   ao2_find(task_data->aor_options->dynamic_contacts, task_data->contact,<br>+              OBJ_NODATA | OBJ_UNLINK | OBJ_SEARCH_OBJECT);<br>+        ao2_find(task_data->aor_options->contacts, task_data->contact,<br>+              OBJ_NODATA | OBJ_UNLINK | OBJ_SEARCH_OBJECT);<br>+<br>+     sip_options_remove_contact_status(task_data->aor_options, task_data->contact);<br>+<br>+      if (task_data->aor_options->qualify_frequency) {<br>+               /* If this is the last contact then we need to stop the scheduled callback */<br>+                if (!ao2_container_count(task_data->aor_options->contacts)) {<br>+                  ast_debug(3, "Terminating scheduled callback on AOR '%s' as there are no contacts to qualify\n",<br>+                           task_data->aor_options->name);<br>+                 if (task_data->aor_options->sched_task) {<br>+                              ast_sip_sched_task_cancel(task_data->aor_options->sched_task);<br>+                         ao2_ref(task_data->aor_options->sched_task, -1);<br>+                               task_data->aor_options->sched_task = NULL;<br>+                     }<br>+            }<br>+    } else {<br>+             task_data->aor_options->available =<br>+                    ao2_container_count(task_data->aor_options->contacts);<br>+         if (!task_data->aor_options->available) {<br>+                      ast_debug(3, "An unqualified contact has been removed from AOR '%s' leaving no remaining contacts\n",<br>+                              task_data->aor_options->name);<br>+                 sip_options_notify_endpoint_state_compositors(task_data->aor_options,<br>+                             UNAVAILABLE);<br>+                }<br>+    }<br>+<br>+ return 0;<br>+}<br>+<br>+/*!<br>+ * \brief Task to delete a contact from an AOR in its serializer<br>+ * \note Run by management_serializer<br>+ */<br>+static int sip_options_contact_delete_management_task(void *obj)<br>+{<br>+       struct sip_options_contact_observer_task_data task_data;<br>+<br>+  task_data.contact = obj;<br>+     task_data.aor_options = ao2_find(sip_options_aors, task_data.contact->aor,<br>+                OBJ_SEARCH_KEY);<br>+     if (!task_data.aor_options) {<br>+                /* For contacts that are deleted we don't really care if there is no AOR locally */<br>+              return 0;<br>+    }<br>+<br>+ ast_sip_push_task_wait_serializer(task_data.aor_options->serializer,<br>+              sip_options_contact_delete_task, &task_data);<br>+    ao2_ref(task_data.aor_options, -1);<br>+<br>+       return 0;<br>+}<br>+<br>+/*! \brief Observer callback invoked on contact deletion */<br>+static void contact_observer_deleted(const void *obj)<br>+{<br>+   ast_sip_push_task_wait_serializer(management_serializer,<br>+             sip_options_contact_delete_management_task, (void *) obj);<br>+}<br>+<br>+/*! \brief Observer callbacks for contacts */<br>+static const struct ast_sorcery_observer contact_observer_callbacks = {<br>+  .created = contact_observer_created,<br>+ .updated = contact_observer_updated,<br>+ .deleted = contact_observer_deleted,<br>+};<br> <br> static char *cli_qualify(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)<br> {<br>         RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);<br>     const char *endpoint_name;<br>-   struct qualify_data *qual_data;<br>+      char *aors;<br>+  char *aor_name;<br> <br>    switch (cmd) {<br>        case CLI_INIT:<br>@@ -917,38 +2330,34 @@<br> <br>     endpoint_name = a->argv[2];<br> <br>-    if (!(endpoint = ast_sorcery_retrieve_by_id(<br>-               ast_sip_get_sorcery(), "endpoint", endpoint_name))) {<br>+        endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint",<br>+           endpoint_name);<br>+      if (!endpoint) {<br>              ast_cli(a->fd, "Unable to retrieve endpoint %s\n", endpoint_name);<br>               return CLI_FAILURE;<br>   }<br> <br>- qual_data = qualify_data_alloc(endpoint, a->fd);<br>-  if (!qual_data) {<br>+    if (ast_strlen_zero(endpoint->aors)) {<br>+            ast_cli(a->fd, "No AORs configured for endpoint '%s'\n", endpoint_name);<br>                 return CLI_FAILURE;<br>   }<br> <br>- if (ast_sip_push_task(NULL, cli_qualify_contacts, qual_data)) {<br>-              qualify_data_destroy(qual_data);<br>-             return CLI_FAILURE;<br>+  aors = ast_strdupa(endpoint->aors);<br>+       while ((aor_name = ast_strip(strsep(&aors, ",")))) {<br>+           struct sip_options_aor *aor_options;<br>+<br>+              aor_options = ao2_find(sip_options_aors, aor_name, OBJ_SEARCH_KEY);<br>+          if (!aor_options) {<br>+                  continue;<br>+            }<br>+<br>+         ast_cli(a->fd, "Qualifying AOR '%s' on endpoint '%s'\n", aor_name, endpoint_name);<br>+              ast_sip_push_task_wait_serializer(aor_options->serializer, sip_options_qualify_aor,<br>+                       aor_options);<br>+                ao2_ref(aor_options, -1);<br>     }<br> <br>  return CLI_SUCCESS;<br>-}<br>-<br>-/*!<br>- * \internal<br>- * \brief Send qualify request to the given contact.<br>- */<br>-static int ami_contact_cb(void *obj, void *arg, int flags)<br>-{<br>-        struct ast_sip_contact *contact = obj;<br>-<br>-    ao2_ref(contact, +1);<br>-        if (ast_sip_push_task(NULL, qualify_contact_task, contact)) {<br>-                ao2_ref(contact, -1);<br>-        }<br>-    return 0;<br> }<br> <br> static int ami_sip_qualify(struct mansession *s, const struct message *m)<br>@@ -978,21 +2387,16 @@<br> <br>       aors = ast_strdupa(endpoint->aors);<br>        while ((aor_name = ast_strip(strsep(&aors, ",")))) {<br>-           struct ast_sip_aor *aor;<br>-             struct ao2_container *contacts;<br>+              struct sip_options_aor *aor_options;<br> <br>-              aor = ast_sip_location_retrieve_aor(aor_name);<br>-               if (!aor) {<br>+          aor_options = ao2_find(sip_options_aors, aor_name, OBJ_SEARCH_KEY);<br>+          if (!aor_options) {<br>                   continue;<br>             }<br> <br>-         contacts = ast_sip_location_retrieve_aor_contacts(aor);<br>-              if (contacts) {<br>-                      ao2_callback(contacts, OBJ_NODATA, ami_contact_cb, NULL);<br>-                    ao2_ref(contacts, -1);<br>-               }<br>-<br>-         ao2_ref(aor, -1);<br>+            ast_sip_push_task_wait_serializer(aor_options->serializer, sip_options_qualify_aor,<br>+                       aor_options);<br>+                ao2_ref(aor_options, -1);<br>     }<br> <br>  astman_send_ack(s, m, "Endpoint found, will qualify");<br>@@ -1003,233 +2407,6 @@<br>     AST_CLI_DEFINE(cli_qualify, "Send an OPTIONS request to a PJSIP endpoint")<br> };<br> <br>-static int sched_qualifies_hash_fn(const void *obj, int flags)<br>-{<br>-    const struct sched_data *object;<br>-     const struct ast_sip_contact *key;<br>-<br>-        switch (flags & OBJ_SEARCH_MASK) {<br>-       case OBJ_SEARCH_KEY:<br>-         key = obj;<br>-           break;<br>-       case OBJ_SEARCH_OBJECT:<br>-              object = obj;<br>-                key = object->contact;<br>-            break;<br>-       default:<br>-             /* Hash can only work on something with a full key. */<br>-               ast_assert(0);<br>-               return 0;<br>-    }<br>-    return ast_str_hash(ast_sorcery_object_get_id(key));<br>-}<br>-<br>-static int sched_qualifies_cmp_fn(void *obj, void *arg, int flags)<br>-{<br>- const struct sched_data *object_left = obj;<br>-  const struct sched_data *object_right = arg;<br>- struct ast_sip_contact *right_key = arg;<br>-     int cmp;<br>-<br>-  switch (flags & OBJ_SEARCH_MASK) {<br>-       case OBJ_SEARCH_OBJECT:<br>-              right_key = object_right->contact;<br>-                /* Fall through */<br>-   case OBJ_SEARCH_KEY:<br>-         cmp = strcmp(ast_sorcery_object_get_id(object_left->contact),<br>-                     ast_sorcery_object_get_id(right_key));<br>-               break;<br>-       case OBJ_SEARCH_PARTIAL_KEY:<br>-         /* Not supported by container. */<br>-            ast_assert(0);<br>-               return 0;<br>-    default:<br>-             /*<br>-            * What arg points to is specific to this traversal callback<br>-          * and has no special meaning to astobj2.<br>-             */<br>-          cmp = 0;<br>-             break;<br>-       }<br>-    if (cmp) {<br>-           return 0;<br>-    }<br>-    /*<br>-    * At this point the traversal callback is identical to a sorted<br>-      * container.<br>-         */<br>-  return CMP_MATCH;<br>-}<br>-<br>-static int rtt_start_handler(const struct aco_option *opt,<br>-        struct ast_variable *var, void *obj)<br>-{<br>-     struct ast_sip_contact_status *status = obj;<br>- long int sec, usec;<br>-<br>-       if (sscanf(var->value, "%ld.%06ld", &sec, &usec) != 2) {<br>-                return -1;<br>-   }<br>-<br>- status->rtt_start = ast_tv(sec, usec);<br>-<br>- return 0;<br>-}<br>-<br>-static int rtt_start_to_str(const void *obj, const intptr_t *args, char **buf)<br>-{<br>-        const struct ast_sip_contact_status *status = obj;<br>-<br>-        if (ast_asprintf(buf, "%ld.%06ld", (long)status->rtt_start.tv_sec, (long)status->rtt_start.tv_usec) == -1) {<br>-         return -1;<br>-   }<br>-<br>- return 0;<br>-}<br>-<br>-static char status_value_unknown[2];<br>-static char status_value_created[2];<br>-<br>-int ast_sip_initialize_sorcery_qualify(void)<br>-{<br>- struct ast_sorcery *sorcery = ast_sip_get_sorcery();<br>-<br>-      /* initialize sorcery ast_sip_contact_status resource */<br>-     ast_sorcery_apply_default(sorcery, CONTACT_STATUS, "memory", NULL);<br>-        ast_sorcery_object_set_congestion_levels(sorcery, CONTACT_STATUS, -1,<br>-                3 * AST_TASKPROCESSOR_HIGH_WATER_LEVEL);<br>-<br>-  if (ast_sorcery_internal_object_register(sorcery, CONTACT_STATUS,<br>-                                    contact_status_alloc, NULL, NULL)) {<br>-         ast_log(LOG_ERROR, "Unable to register ast_sip_contact_status in sorcery\n");<br>-              return -1;<br>-   }<br>-<br>- snprintf(status_value_unknown, sizeof(status_value_unknown), "%u", UNKNOWN);<br>-       ast_sorcery_object_field_register_nodoc(sorcery, CONTACT_STATUS, "last_status",<br>-            status_value_unknown, OPT_UINT_T, 0, FLDSET(struct ast_sip_contact_status, last_status));<br>-    snprintf(status_value_created, sizeof(status_value_created), "%u", CREATED);<br>-       ast_sorcery_object_field_register_nodoc(sorcery, CONTACT_STATUS, "status",<br>-         status_value_created, OPT_UINT_T, 0, FLDSET(struct ast_sip_contact_status, status));<br>- ast_sorcery_object_field_register_custom_nodoc(sorcery, CONTACT_STATUS, "rtt_start",<br>-               "0.0", rtt_start_handler, rtt_start_to_str, NULL, 0, 0);<br>-   ast_sorcery_object_field_register_nodoc(sorcery, CONTACT_STATUS, "rtt",<br>-            "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_contact_status, rtt));<br>-<br>-        return 0;<br>-}<br>-<br>-static void qualify_and_schedule_contact(struct ast_sip_contact *contact)<br>-{<br>-     int initial_interval;<br>-        int max_time = ast_sip_get_max_initial_qualify_time();<br>-<br>-    /* Delay initial qualification by a random fraction of the specified interval */<br>-     if (max_time && max_time < contact->qualify_frequency) {<br>-               initial_interval = max_time;<br>- } else {<br>-             initial_interval = contact->qualify_frequency;<br>-    }<br>-<br>- initial_interval = (int)((initial_interval * 1000) * ast_random_double());<br>-<br>-        unschedule_qualify(contact);<br>- if (contact->qualify_frequency) {<br>-         schedule_qualify(contact, initial_interval);<br>- } else {<br>-             update_contact_status(contact, UNKNOWN, 0);<br>-  }<br>-}<br>-<br>-static int qualify_and_schedule_cb_with_aor(void *obj, void *arg, int flags)<br>-{<br>-  struct ast_sip_contact *contact = obj;<br>-       struct ast_sip_aor *aor = arg;<br>-<br>-    contact->qualify_frequency = aor->qualify_frequency;<br>-   contact->qualify_timeout = aor->qualify_timeout;<br>-       contact->authenticate_qualify = aor->authenticate_qualify;<br>-<br>-  qualify_and_schedule_contact(contact);<br>-<br>-    return 0;<br>-}<br>-<br>-static int qualify_and_schedule_cb_without_aor(void *obj, void *arg, int flags)<br>-{<br>-       /*<br>-    * These are really dynamic contacts. We need to retrieve the aor associated<br>-  * with the contact since it's possible some of the aor's fields were updated<br>-         * since last load.<br>-   */<br>-  struct ast_sip_contact *contact = obj;<br>-       struct ast_sip_aor *aor = ast_sip_location_retrieve_aor(contact->aor);<br>-<br>- if (aor) {<br>-           qualify_and_schedule_cb_with_aor(obj, aor, flags);<br>-           ao2_ref(aor, -1);<br>-    } else {<br>-             ast_log(LOG_WARNING, "Unable to locate AOR for contact '%s'. Keeping old "<br>-                 "associated settings: frequency=%d, timeout=%f, authenticate=%s\n",<br>-                        contact->uri, contact->qualify_frequency, contact->qualify_timeout,<br>-                 contact->authenticate_qualify ? "yes" : "no");<br>-                qualify_and_schedule_contact(contact);<br>-       }<br>-<br>- return 0;<br>-}<br>-<br>-/*!<br>- * \internal<br>- * \brief Qualify and schedule an aor's contacts<br>- *<br>- * \details For the given aor check if it has permanent contacts,<br>- *         qualify all contacts and schedule for checks if configured.<br>- */<br>-static int qualify_and_schedule_all_cb(void *obj, void *arg, int flags)<br>-{<br>-   struct ast_sip_aor *aor = obj;<br>-       struct ao2_container *contacts;<br>-<br>-   if (aor->permanent_contacts) {<br>-            contacts = ast_sip_location_retrieve_aor_contacts(aor);<br>-              if (contacts) {<br>-                      ao2_callback(contacts, OBJ_NODATA, qualify_and_schedule_cb_with_aor, aor);<br>-                   ao2_ref(contacts, -1);<br>-               }<br>-    }<br>-<br>- return 0;<br>-}<br>-<br>-static void qualify_and_schedule_all(void)<br>-{<br>-    struct ao2_container *aors;<br>-  struct ao2_container *contacts;<br>-<br>-   /*<br>-    * It's possible that the AOR had some of it's fields updated prior to a<br>-      * reload. For instance qualifying could have been turned on or off by<br>-        * setting the qualify_frequency. Due to this we have to iterate through<br>-      * all contacts (static and dynamic), and not just ones where the frequency<br>-   * is greater than zero, updating any contact fields with the AOR's values.<br>-       */<br>-<br>-       aors = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(),<br>-         "aor", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);<br>-<br>-       if (aors) {<br>-          ao2_callback(aors, OBJ_NODATA, qualify_and_schedule_all_cb, NULL);<br>-           ao2_ref(aors, -1);<br>-   }<br>-<br>- contacts = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(),<br>-             "contact", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);<br>-      if (contacts) {<br>-              ao2_callback(contacts, OBJ_NODATA, qualify_and_schedule_cb_without_aor, NULL);<br>-               ao2_ref(contacts, -1);<br>-       }<br>-}<br> <br> int ast_sip_format_contact_ami(void *obj, void *arg, int flags)<br> {<br>@@ -1245,9 +2422,7 @@<br>                 return -1;<br>    }<br> <br>- status = ast_sorcery_retrieve_by_id(<br>-         ast_sip_get_sorcery(), CONTACT_STATUS,<br>-               ast_sorcery_object_get_id(contact));<br>+ status = ast_sip_get_contact_status(contact);<br> <br>      ast_str_append(&buf, 0, "AOR: %s\r\n", wrapper->aor_id);<br>     ast_str_append(&buf, 0, "URI: %s\r\n", contact->uri);<br>@@ -1263,14 +2438,15 @@<br>       if (!ast_strlen_zero(contact->call_id)) {<br>          ast_str_append(&buf, 0, "CallID: %s\r\n", contact->call_id);<br>         }<br>-    ast_str_append(&buf, 0, "Status: %s\r\n", ast_sip_get_contact_status_label(status ? status->status : UNKNOWN));<br>+     ast_str_append(&buf, 0, "Status: %s\r\n",<br>+              ast_sip_get_contact_status_label(status ? status->status : UNKNOWN));<br>      if (!status || status->status == UNKNOWN) {<br>                ast_str_append(&buf, 0, "RoundtripUsec: N/A\r\n");<br>      } else {<br>              ast_str_append(&buf, 0, "RoundtripUsec: %" PRId64 "\r\n", status->rtt);<br>    }<br>     ast_str_append(&buf, 0, "EndpointName: %s\r\n",<br>-                        endpoint ? ast_sorcery_object_get_id(endpoint) : S_OR(contact->endpoint_name, ""));<br>+             endpoint ? ast_sorcery_object_get_id(endpoint) : S_OR(contact->endpoint_name, ""));<br> <br>   ast_str_append(&buf, 0, "ID: %s\r\n", ast_sorcery_object_get_id(contact));<br>      ast_str_append(&buf, 0, "AuthenticateQualify: %d\r\n", contact->authenticate_qualify);<br>@@ -1305,218 +2481,202 @@<br>    .format_ami = format_ami_contact_status<br> };<br> <br>-static void aor_observer_modified(const void *obj)<br>+/*!<br>+ * \brief Management task to clean up an AOR<br>+ * \note Run by aor_options->serializer<br>+ */<br>+static int sip_options_cleanup_aor_task(void *obj)<br> {<br>-        struct ast_sip_aor *aor = (void *)obj;<br>-       struct ao2_container *contacts;<br>+      struct sip_options_aor *aor_options = obj;<br> <br>-        contacts = ast_sip_location_retrieve_aor_contacts(aor);<br>-      if (contacts) {<br>-              ao2_callback(contacts, OBJ_NODATA, qualify_and_schedule_cb_with_aor, aor);<br>-           ao2_ref(contacts, -1);<br>+       ast_debug(2, "Cleaning up AOR '%s' for shutdown\n", aor_options->name);<br>+<br>+      aor_options->qualify_frequency = 0;<br>+       if (aor_options->sched_task) {<br>+            ast_sip_sched_task_cancel(aor_options->sched_task);<br>+               ao2_ref(aor_options->sched_task, -1);<br>+             aor_options->sched_task = NULL;<br>    }<br>+    AST_VECTOR_RESET(&aor_options->compositors, ao2_cleanup);<br>+<br>+  return 0;<br> }<br> <br>-static int unschedule_contact_cb(void *obj, void *arg, int flags)<br>+/*!<br>+ * \brief Management task to clean up the environment<br>+ * \note Run by management_serializer<br>+ */<br>+static int sip_options_cleanup_task(void *obj)<br> {<br>-        unschedule_qualify(obj);<br>+     struct ao2_iterator it_aor;<br>+  struct sip_options_aor *aor_options;<br> <br>-      return CMP_MATCH;<br>-}<br>-<br>-static void aor_observer_deleted(const void *obj)<br>-{<br>-     const struct ast_sip_aor *aor = obj;<br>- struct ao2_container *contacts;<br>-<br>-   contacts = ast_sip_location_retrieve_aor_contacts(aor);<br>-      if (contacts) {<br>-              ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, unschedule_contact_cb, NULL);<br>-              ao2_ref(contacts, -1);<br>-       }<br>-}<br>-<br>-static const struct ast_sorcery_observer observer_callbacks_options = {<br>-   .created = aor_observer_modified,<br>-    .updated = aor_observer_modified,<br>-    .deleted = aor_observer_deleted<br>-};<br>-<br>-static int aor_update_endpoint_state(void *obj, void *arg, int flags)<br>-{<br>-  struct ast_sip_endpoint *endpoint = obj;<br>-     const char *endpoint_name = ast_sorcery_object_get_id(endpoint);<br>-     char *aor = arg;<br>-     char *endpoint_aor;<br>-  char *endpoint_aors;<br>-<br>-      if (ast_strlen_zero(aor) || ast_strlen_zero(endpoint->aors)) {<br>+    if (!sip_options_aors) {<br>+             /* Nothing to do */<br>           return 0;<br>     }<br> <br>- endpoint_aors = ast_strdupa(endpoint->aors);<br>-      while ((endpoint_aor = ast_strip(strsep(&endpoint_aors, ",")))) {<br>-              if (!strcmp(aor, endpoint_aor)) {<br>-                    if (ast_sip_persistent_endpoint_update_state(endpoint_name, AST_ENDPOINT_ONLINE) == -1) {<br>-                            ast_log(LOG_WARNING, "Unable to find persistent endpoint '%s' for aor '%s'\n",<br>-                                     endpoint_name, aor);<br>-                 }<br>-            }<br>+    it_aor = ao2_iterator_init(sip_options_aors, AO2_ITERATOR_UNLINK);<br>+   for (; (aor_options = ao2_iterator_next(&it_aor)); ao2_ref(aor_options, -1)) {<br>+           ast_sip_push_task_wait_serializer(aor_options->serializer,<br>+                        sip_options_cleanup_aor_task, aor_options);<br>   }<br>-<br>- return 0;<br>-}<br>-<br>-static int on_aor_update_endpoint_state(void *obj, void *arg, int flags)<br>-{<br>-      struct ast_sip_aor *aor = obj;<br>-       struct ao2_container *endpoints;<br>-     RAII_VAR(struct ast_variable *, var, NULL, ast_variables_destroy);<br>-   const char *aor_name = ast_sorcery_object_get_id(aor);<br>-       char *aor_like;<br>-<br>-   if (ast_strlen_zero(aor_name)) {<br>-             return -1;<br>-   }<br>-<br>- if (aor->permanent_contacts && ((int)(aor->qualify_frequency * 1000)) <= 0) {<br>-               aor_like = ast_alloca(strlen(aor_name) + 3);<br>-         sprintf(aor_like, "%%%s%%", aor_name);<br>-             var = ast_variable_new("aors LIKE", aor_like, "");<br>-               if (!var) {<br>-                  return -1;<br>-           }<br>-            endpoints = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(),<br>-                    "endpoint", AST_RETRIEVE_FLAG_MULTIPLE, var);<br>-<br>-           if (endpoints) {<br>-                 /*<br>-                    * Because aors are a string list, we have to use a pattern match but since a simple<br>-                  * pattern match could return an endpoint that has an aor of "aaabccc" when searching<br>-               * for "abc", we still have to iterate over them to find an exact aor match.<br>-                */<br>-                  ao2_callback(endpoints, 0, aor_update_endpoint_state, (char *)aor_name);<br>-             ao2_ref(endpoints, -1);<br>-          }<br>-    }<br>-<br>- return 0;<br>-}<br>-<br>-static int contact_update_endpoint_state(void *obj, void *arg, int flags)<br>-{<br>-     const struct ast_sip_contact *contact = obj;<br>- struct timeval tv = ast_tvnow();<br>-<br>-  if (!ast_strlen_zero(contact->endpoint_name) && ((int)(contact->qualify_frequency * 1000)) <= 0 &&<br>-          contact->expiration_time.tv_sec > tv.tv_sec) {<br>-<br>-              if (ast_sip_persistent_endpoint_update_state(contact->endpoint_name, AST_ENDPOINT_ONLINE) == -1) {<br>-                        ast_log(LOG_WARNING, "Unable to find persistent endpoint '%s' for contact '%s/%s'\n",<br>-                              contact->endpoint_name, contact->aor, contact->uri);<br>-                        return -1;<br>-           }<br>-    }<br>-<br>- return 0;<br>-}<br>-<br>-static void update_all_unqualified_endpoints(void)<br>-{<br>-    struct ao2_container *aors;<br>-  struct ao2_container *contacts;<br>-      RAII_VAR(struct ast_variable *, var_aor, NULL, ast_variables_destroy);<br>-       RAII_VAR(struct ast_variable *, var_contact, NULL, ast_variables_destroy);<br>-   RAII_VAR(char *, time_now, NULL, ast_free);<br>-  struct timeval tv = ast_tvnow();<br>-<br>-  if (!(var_aor = ast_variable_new("contact !=", "", ""))) {<br>-             return;<br>-      }<br>-    if (!(var_aor->next = ast_variable_new("qualify_frequency <=", "0", ""))) {<br>-              return;<br>-      }<br>-<br>- if (ast_asprintf(&time_now, "%ld", tv.tv_sec) == -1) {<br>-         return;<br>-      }<br>-    if (!(var_contact = ast_variable_new("expiration_time >", time_now, ""))) {<br>-           return;<br>-      }<br>-    if (!(var_contact->next = ast_variable_new("qualify_frequency <=", "0", ""))) {<br>-          return;<br>-      }<br>-<br>- aors = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(),<br>-         "aor", AST_RETRIEVE_FLAG_MULTIPLE, var_aor);<br>-       if (aors) {<br>-          ao2_callback(aors, OBJ_NODATA, on_aor_update_endpoint_state, NULL);<br>-          ao2_ref(aors, -1);<br>-   }<br>-<br>- contacts = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(),<br>-             "contact", AST_RETRIEVE_FLAG_MULTIPLE, var_contact);<br>-       if (contacts) {<br>-              ao2_callback(contacts, OBJ_NODATA, contact_update_endpoint_state, NULL);<br>-             ao2_ref(contacts, -1);<br>-       }<br>-}<br>-<br>-int ast_res_pjsip_init_options_handling(int reload)<br>-{<br>-   static const pj_str_t STR_OPTIONS = { "OPTIONS", 7 };<br>-<br>-   if (reload) {<br>-                qualify_and_schedule_all();<br>-          return 0;<br>-    }<br>-<br>- sched_qualifies = ao2_t_container_alloc(QUALIFIED_BUCKETS,<br>-           sched_qualifies_hash_fn, sched_qualifies_cmp_fn,<br>-             "Create container for scheduled qualifies");<br>-       if (!sched_qualifies) {<br>-              return -1;<br>-   }<br>-<br>- if (pjsip_endpt_register_module(ast_sip_get_pjsip_endpoint(), &options_module) != PJ_SUCCESS) {<br>-          ao2_cleanup(sched_qualifies);<br>-                sched_qualifies = NULL;<br>-              return -1;<br>-   }<br>-<br>- if (pjsip_endpt_add_capability(ast_sip_get_pjsip_endpoint(), NULL, PJSIP_H_ALLOW,<br>-            NULL, 1, &STR_OPTIONS) != PJ_SUCCESS) {<br>-          pjsip_endpt_unregister_module(ast_sip_get_pjsip_endpoint(), &options_module);<br>-            ao2_cleanup(sched_qualifies);<br>-                sched_qualifies = NULL;<br>-              return -1;<br>-   }<br>-<br>- if (ast_sorcery_observer_add(ast_sip_get_sorcery(), "aor", &observer_callbacks_options)) {<br>-             pjsip_endpt_unregister_module(ast_sip_get_pjsip_endpoint(), &options_module);<br>-            ao2_cleanup(sched_qualifies);<br>-                sched_qualifies = NULL;<br>-              return -1;<br>-   }<br>-<br>- internal_sip_register_endpoint_formatter(&contact_status_formatter);<br>-     ast_manager_register2("PJSIPQualify", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, ami_sip_qualify, NULL, NULL, NULL);<br>-        ast_cli_register_multiple(cli_options, ARRAY_LEN(cli_options));<br>-<br>-   update_all_unqualified_endpoints();<br>-  qualify_and_schedule_all();<br>+  ao2_iterator_destroy(&it_aor);<br> <br>         return 0;<br> }<br> <br> void ast_res_pjsip_cleanup_options_handling(void)<br> {<br>+     int remaining;<br>+       struct ast_taskprocessor *mgmt_serializer;<br>+<br>         ast_cli_unregister_multiple(cli_options, ARRAY_LEN(cli_options));<br>     ast_manager_unregister("PJSIPQualify");<br>     internal_sip_unregister_endpoint_formatter(&contact_status_formatter);<br> <br>-        ast_sorcery_observer_remove(ast_sip_get_sorcery(), "aor", &observer_callbacks_options);<br>+        ast_sorcery_observer_remove(ast_sip_get_sorcery(), "contact",<br>+              &contact_observer_callbacks);<br>+    ast_sorcery_observer_remove(ast_sip_get_sorcery(), "aor",<br>+          &aor_observer_callbacks);<br>+        ast_sorcery_observer_remove(ast_sip_get_sorcery(), "endpoint",<br>+             &endpoint_observer_callbacks);<br>+<br>+        mgmt_serializer = management_serializer;<br>+     management_serializer = NULL;<br>+        if (mgmt_serializer) {<br>+               ast_sip_push_task_wait_serializer(mgmt_serializer, sip_options_cleanup_task, NULL);<br>+  }<br>+<br>+ remaining = ast_serializer_shutdown_group_join(shutdown_group,<br>+               MAX_UNLOAD_TIMEOUT_TIME);<br>+    if (remaining) {<br>+             ast_log(LOG_WARNING, "Cleanup incomplete. Could not stop %d AORs.\n",<br>+                      remaining);<br>+  }<br>+    ao2_cleanup(shutdown_group);<br>+ shutdown_group = NULL;<br>+<br>+    if (mgmt_serializer) {<br>+               ast_taskprocessor_unreference(mgmt_serializer);<br>+      }<br>+<br>+ ao2_cleanup(sip_options_aors);<br>+       sip_options_aors = NULL;<br>+     ao2_cleanup(sip_options_contact_statuses);<br>+   sip_options_contact_statuses = NULL;<br>+ ao2_cleanup(sip_options_endpoint_state_compositors);<br>+ sip_options_endpoint_state_compositors = NULL;<br>+<br>     pjsip_endpt_unregister_module(ast_sip_get_pjsip_endpoint(), &options_module);<br>-    ao2_cleanup(sched_qualifies);<br>-        sched_qualifies = NULL;<br>+}<br>+<br>+/*!<br>+ * \brief Management task to finish setting up the environment.<br>+ * \note Run by management_serializer<br>+ */<br>+static int sip_options_init_task(void *mgmt_serializer)<br>+{<br>+   management_serializer = mgmt_serializer;<br>+<br>+  shutdown_group = ast_serializer_shutdown_group_alloc();<br>+      if (!shutdown_group) {<br>+               return -1;<br>+   }<br>+<br>+ if (ast_sorcery_observer_add(ast_sip_get_sorcery(), "endpoint",<br>+            &endpoint_observer_callbacks)) {<br>+         return -1;<br>+   }<br>+    if (ast_sorcery_observer_add(ast_sip_get_sorcery(), "aor",<br>+         &aor_observer_callbacks)) {<br>+              return -1;<br>+   }<br>+    if (ast_sorcery_observer_add(ast_sip_get_sorcery(), "contact",<br>+             &contact_observer_callbacks)) {<br>+          return -1;<br>+   }<br>+<br>+ sip_options_synchronize(0);<br>+<br>+       return 0;<br>+}<br>+<br>+int ast_res_pjsip_preinit_options_handling(void)<br>+{<br>+      sip_options_contact_statuses = sip_options_contact_statuses_alloc();<br>+ return sip_options_contact_statuses ? 0 : -1;<br>+}<br>+<br>+int ast_res_pjsip_init_options_handling(int reload)<br>+{<br>+       struct ast_taskprocessor *mgmt_serializer;<br>+<br>+        static const pj_str_t STR_OPTIONS = { "OPTIONS", 7 };<br>+<br>+   if (reload) {<br>+                sip_options_synchronize(1);<br>+          return 0;<br>+    }<br>+<br>+ if (pjsip_endpt_register_module(ast_sip_get_pjsip_endpoint(), &options_module)<br>+           != PJ_SUCCESS) {<br>+             return -1;<br>+   }<br>+<br>+ if (pjsip_endpt_add_capability(ast_sip_get_pjsip_endpoint(), NULL, PJSIP_H_ALLOW,<br>+            NULL, 1, &STR_OPTIONS) != PJ_SUCCESS) {<br>+          ast_res_pjsip_cleanup_options_handling();<br>+            return -1;<br>+   }<br>+<br>+ sip_options_aors = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_RWLOCK, 0, AOR_BUCKETS,<br>+               sip_options_aor_hash_fn, NULL, sip_options_aor_cmp_fn);<br>+      if (!sip_options_aors) {<br>+             ast_res_pjsip_cleanup_options_handling();<br>+            return -1;<br>+   }<br>+    sip_options_endpoint_state_compositors =<br>+             ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_RWLOCK, 0,<br>+                       ENDPOINT_STATE_COMPOSITOR_BUCKETS,<br>+                   sip_options_endpoint_state_compositor_hash_fn, NULL,<br>+                 sip_options_endpoint_state_compositor_cmp_fn);<br>+       if (!sip_options_endpoint_state_compositors) {<br>+               ast_res_pjsip_cleanup_options_handling();<br>+            return -1;<br>+   }<br>+<br>+ mgmt_serializer = ast_sip_create_serializer_named("pjsip/options/manage");<br>+ if (!mgmt_serializer) {<br>+              ast_res_pjsip_cleanup_options_handling();<br>+            return -1;<br>+   }<br>+<br>+ /*<br>+    * Set the water mark levels high because we can get a flood of<br>+       * contact status updates from sip_options_synchronize() that<br>+         * quickly clears on initial load or reload.<br>+  */<br>+  ast_taskprocessor_alert_set_levels(mgmt_serializer, -1,<br>+              10 * AST_TASKPROCESSOR_HIGH_WATER_LEVEL);<br>+<br>+ /*<br>+    * We make sure that the environment is completely setup before we allow<br>+      * any other threads to post contact_status updates to the<br>+    * management_serializer.<br>+     */<br>+  if (ast_sip_push_task_wait_serializer(mgmt_serializer, sip_options_init_task,<br>+                mgmt_serializer)) {<br>+          /* Set management_serializer in case pushing the task actually failed. */<br>+            management_serializer = mgmt_serializer;<br>+             ast_res_pjsip_cleanup_options_handling();<br>+            return -1;<br>+   }<br>+<br>+ internal_sip_register_endpoint_formatter(&contact_status_formatter);<br>+     ast_manager_register2("PJSIPQualify", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING,<br>+            ami_sip_qualify, NULL, NULL, NULL);<br>+  ast_cli_register_multiple(cli_options, ARRAY_LEN(cli_options));<br>+<br>+   return 0;<br> }<br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/7710">change 7710</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/7710"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: 13 </div>
<div style="display:none"> Gerrit-MessageType: merged </div>
<div style="display:none"> Gerrit-Change-Id: I6a5ebbfca9001dfe933eaeac4d3babd8d2e6f082 </div>
<div style="display:none"> Gerrit-Change-Number: 7710 </div>
<div style="display:none"> Gerrit-PatchSet: 7 </div>
<div style="display:none"> Gerrit-Owner: Joshua Colp <jcolp@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Corey Farrell <git@cfware.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: Kevin Harwell <kharwell@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Richard Mudgett <rmudgett@digium.com> </div>