diff options
Diffstat (limited to 'res/res_pjsip/pjsip_configuration.c')
-rw-r--r-- | res/res_pjsip/pjsip_configuration.c | 221 |
1 files changed, 152 insertions, 69 deletions
diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c index 368f0d8a2..9e757e230 100644 --- a/res/res_pjsip/pjsip_configuration.c +++ b/res/res_pjsip/pjsip_configuration.c @@ -58,64 +58,15 @@ static int persistent_endpoint_cmp(void *obj, void *arg, int flags) return !strcmp(ast_endpoint_get_resource(persistent1->endpoint), id) ? CMP_MATCH | CMP_STOP : 0; } -/*! \brief Callback function for changing the state of an endpoint */ -static int persistent_endpoint_update_state(void *obj, void *arg, int flags) +/*! \brief Internal function for changing the state of an endpoint */ +static void endpoint_update_state(struct ast_endpoint *endpoint, enum ast_endpoint_state state) { - struct sip_persistent_endpoint *persistent = obj; - struct ast_endpoint *endpoint = persistent->endpoint; - struct ast_sip_contact_status *status = arg; - struct ao2_container *contacts; struct ast_json *blob; - struct ao2_iterator i; - struct ast_sip_contact *contact; - enum ast_endpoint_state state = AST_ENDPOINT_OFFLINE; char *regcontext; - if (status) { - char rtt[32]; - - /* If the status' aor isn't one of the endpoint's, we skip */ - if (!strstr(persistent->aors, status->aor)) { - return 0; - } - - snprintf(rtt, sizeof(rtt), "%" PRId64, status->rtt); - blob = ast_json_pack("{s: s, s: s, s: s, s: s, s: s}", - "contact_status", ast_sip_get_contact_status_label(status->status), - "aor", status->aor, - "uri", status->uri, - "roundtrip_usec", rtt, - "endpoint_name", ast_endpoint_get_resource(endpoint)); - ast_endpoint_blob_publish(endpoint, ast_endpoint_contact_state_type(), blob); - ast_json_unref(blob); - } - - /* Find all the contacts for this endpoint. If ANY are available, - * mark the endpoint as ONLINE. - */ - contacts = ast_sip_location_retrieve_contacts_from_aor_list(persistent->aors); - if (contacts) { - i = ao2_iterator_init(contacts, 0); - while (state == AST_ENDPOINT_OFFLINE && (contact = ao2_iterator_next(&i))) { - struct ast_sip_contact_status *contact_status; - const char *contact_id = ast_sorcery_object_get_id(contact); - - contact_status = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), - CONTACT_STATUS, contact_id); - - if (contact_status && contact_status->status != UNAVAILABLE) { - state = AST_ENDPOINT_ONLINE; - } - ao2_cleanup(contact_status); - ao2_ref(contact, -1); - } - ao2_iterator_destroy(&i); - ao2_ref(contacts, -1); - } - /* If there was no state change, don't publish anything. */ if (ast_endpoint_get_state(endpoint) == state) { - return 0; + return; } regcontext = ast_sip_get_regcontext(); @@ -152,6 +103,83 @@ static int persistent_endpoint_update_state(void *obj, void *arg, int flags) ast_endpoint_blob_publish(endpoint, ast_endpoint_state_type(), blob); ast_json_unref(blob); ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "PJSIP/%s", ast_endpoint_get_resource(endpoint)); +} + +static void endpoint_publish_contact_status(struct ast_endpoint *endpoint, struct ast_sip_contact_status *contact) +{ + struct ast_json *blob; + char rtt[32]; + + snprintf(rtt, sizeof(rtt), "%" PRId64, contact->rtt); + blob = ast_json_pack("{s: s, s: s, s: s, s: s, s: s}", + "contact_status", ast_sip_get_contact_status_label(contact->status), + "aor", contact->aor, + "uri", contact->uri, + "roundtrip_usec", rtt, + "endpoint_name", ast_endpoint_get_resource(endpoint)); + if (blob) { + ast_endpoint_blob_publish(endpoint, ast_endpoint_contact_state_type(), blob); + ast_json_unref(blob); + } +} + +/*! \brief Callback function for publishing the status of an endpoint */ +static int persistent_endpoint_publish_status(void *obj, void *arg, int flags) +{ + struct sip_persistent_endpoint *persistent = obj; + struct ast_endpoint *endpoint = persistent->endpoint; + struct ast_sip_contact_status *status = arg; + + /* If the status' aor isn't one of the endpoint's, we skip */ + if (!strstr(persistent->aors, status->aor)) { + return 0; + } + + endpoint_publish_contact_status(endpoint, status); + return 0; +} + +/*! \brief Callback function for changing the state of an endpoint */ +static int persistent_endpoint_update_state(void *obj, void *arg, int flags) +{ + struct sip_persistent_endpoint *persistent = obj; + struct ast_endpoint *endpoint = persistent->endpoint; + struct ast_sip_contact_status *status = arg; + struct ao2_container *contacts; + struct ao2_iterator iter; + struct ast_sip_contact *contact; + enum ast_endpoint_state state = AST_ENDPOINT_OFFLINE; + + /* If the status' aor isn't one of the endpoint's, we skip */ + if (!strstr(persistent->aors, status->aor)) { + return 0; + } + + endpoint_publish_contact_status(endpoint, status); + + /* Find all the contacts for this endpoint. If ANY are available, + * mark the endpoint as ONLINE. + */ + contacts = ast_sip_location_retrieve_contacts_from_aor_list(persistent->aors); + if (contacts) { + iter = ao2_iterator_init(contacts, 0); + while (state == AST_ENDPOINT_OFFLINE && (contact = ao2_iterator_next(&iter))) { + struct ast_sip_contact_status *contact_status; + const char *contact_id = ast_sorcery_object_get_id(contact); + + contact_status = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), + CONTACT_STATUS, contact_id); + if (contact_status && contact_status->status != UNAVAILABLE) { + state = AST_ENDPOINT_ONLINE; + } + ao2_cleanup(contact_status); + ao2_ref(contact, -1); + } + ao2_iterator_destroy(&iter); + ao2_ref(contacts, -1); + } + + endpoint_update_state(endpoint, state); return 0; } @@ -176,7 +204,7 @@ static void persistent_endpoint_contact_created_observer(const void *object) } contact_status->status = CREATED; - ast_verb(2, "Contact %s/%s has been created\n",contact->aor, contact->uri); + ast_verb(2, "Contact %s/%s has been created\n", contact->aor, contact->uri); ao2_callback(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, contact_status); ao2_cleanup(contact_status); @@ -190,7 +218,7 @@ static void persistent_endpoint_contact_deleted_observer(const void *object) contact_status = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), CONTACT_STATUS, ast_sorcery_object_get_id(contact)); if (!contact_status) { - ast_log(LOG_ERROR, "Unable to create ast_sip_contact_status for contact %s/%s\n", + ast_log(LOG_ERROR, "Unable to find ast_sip_contact_status for contact %s/%s\n", contact->aor, contact->uri); return; } @@ -217,13 +245,21 @@ static void persistent_endpoint_contact_status_observer(const void *object) { struct ast_sip_contact_status *contact_status = (struct ast_sip_contact_status *)object; + if (contact_status->refresh) { + /* We are only re-publishing the contact status. */ + ao2_callback(persistent_endpoints, OBJ_NODATA, + persistent_endpoint_publish_status, contact_status); + return; + } + /* If rtt_start is set (this is the outgoing OPTIONS), ignore. */ if (contact_status->rtt_start.tv_sec > 0) { return; } if (contact_status->status != contact_status->last_status) { - ast_verb(3, "Contact %s/%s is now %s. RTT: %.3f msec\n", contact_status->aor, contact_status->uri, + ast_verb(3, "Contact %s/%s is now %s. RTT: %.3f msec\n", + contact_status->aor, contact_status->uri, ast_sip_get_contact_status_label(contact_status->status), contact_status->rtt / 1000.0); @@ -234,19 +270,23 @@ static void persistent_endpoint_contact_status_observer(const void *object) ast_test_suite_event_notify("AOR_CONTACT_UPDATE", "Contact: %s\r\n" - "Status: %s", + "Status: %s", ast_sorcery_object_get_id(contact_status), ast_sip_get_contact_status_label(contact_status->status)); - ao2_callback(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, contact_status); + ao2_callback(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, + contact_status); } else { ast_debug(3, "Contact %s/%s status didn't change: %s, RTT: %.3f msec\n", - contact_status->aor, contact_status->uri, ast_sip_get_contact_status_label(contact_status->status), + contact_status->aor, contact_status->uri, + ast_sip_get_contact_status_label(contact_status->status), contact_status->rtt / 1000.0); } ast_statsd_log_full_va("PJSIP.contacts.%s.rtt", AST_STATSD_TIMER, - contact_status->status != AVAILABLE ? -1 : contact_status->rtt / 1000, 1.0, ast_sorcery_object_get_id(contact_status)); + contact_status->status != AVAILABLE ? -1 : contact_status->rtt / 1000, + 1.0, + ast_sorcery_object_get_id(contact_status)); } /*! \brief Observer for contacts so state can be updated on respective endpoints */ @@ -258,7 +298,8 @@ static void endpoint_deleted_observer(const void *object) { const struct ast_sip_endpoint *endpoint = object; - ao2_find(persistent_endpoints, ast_endpoint_get_resource(endpoint->persistent), OBJ_SEARCH_KEY | OBJ_UNLINK | OBJ_NODATA); + ao2_find(persistent_endpoints, ast_endpoint_get_resource(endpoint->persistent), + OBJ_SEARCH_KEY | OBJ_UNLINK | OBJ_NODATA); } static const struct ast_sorcery_observer endpoint_observers = { @@ -1184,29 +1225,48 @@ static void persistent_endpoint_destroy(void *obj) ast_free(persistent->aors); } +int ast_sip_persistent_endpoint_update_state(const char *endpoint_name, enum ast_endpoint_state state) +{ + struct sip_persistent_endpoint *persistent; + + ao2_lock(persistent_endpoints); + persistent = ao2_find(persistent_endpoints, endpoint_name, OBJ_SEARCH_KEY | OBJ_NOLOCK); + if (persistent) { + endpoint_update_state(persistent->endpoint, state); + ao2_ref(persistent, -1); + } + ao2_unlock(persistent_endpoints); + return persistent ? 0 : -1; +} + /*! \brief Internal function which finds (or creates) persistent endpoint information */ static struct ast_endpoint *persistent_endpoint_find_or_create(const struct ast_sip_endpoint *endpoint) { RAII_VAR(struct sip_persistent_endpoint *, persistent, NULL, ao2_cleanup); SCOPED_AO2LOCK(lock, persistent_endpoints); - if (!(persistent = ao2_find(persistent_endpoints, ast_sorcery_object_get_id(endpoint), OBJ_KEY | OBJ_NOLOCK))) { - if (!(persistent = ao2_alloc(sizeof(*persistent), persistent_endpoint_destroy))) { + persistent = ao2_find(persistent_endpoints, ast_sorcery_object_get_id(endpoint), + OBJ_SEARCH_KEY | OBJ_NOLOCK); + if (!persistent) { + persistent = ao2_alloc_options(sizeof(*persistent), persistent_endpoint_destroy, + AO2_ALLOC_OPT_LOCK_NOLOCK); + if (!persistent) { return NULL; } - if (!(persistent->endpoint = ast_endpoint_create("PJSIP", ast_sorcery_object_get_id(endpoint)))) { + persistent->endpoint = ast_endpoint_create("PJSIP", + ast_sorcery_object_get_id(endpoint)); + if (!persistent->endpoint) { return NULL; } persistent->aors = ast_strdup(endpoint->aors); - - if (ast_strlen_zero(persistent->aors)) { - ast_endpoint_set_state(persistent->endpoint, AST_ENDPOINT_UNKNOWN); - } else { - persistent_endpoint_update_state(persistent, NULL, 0); + if (!persistent->aors) { + return NULL; } + ast_endpoint_set_state(persistent->endpoint, AST_ENDPOINT_UNKNOWN); + ao2_link_flags(persistent_endpoints, persistent, OBJ_NOLOCK); } @@ -1686,6 +1746,22 @@ static struct ast_cli_entry cli_commands[] = { struct ast_sip_cli_formatter_entry *channel_formatter; struct ast_sip_cli_formatter_entry *endpoint_formatter; +static int on_load_endpoint(void *obj, void *arg, int flags) +{ + return sip_endpoint_apply_handler(sip_sorcery, obj); +} + +static void load_all_endpoints(void) +{ + struct ao2_container *endpoints; + + endpoints = ast_sorcery_retrieve_by_fields(sip_sorcery, "endpoint", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL); + if (endpoints) { + ao2_callback(endpoints, OBJ_NODATA, on_load_endpoint, NULL); + ao2_ref(endpoints, -1); + } +} + int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_module_info) { if (ast_manager_register_xml(AMI_SHOW_ENDPOINTS, EVENT_FLAG_SYSTEM, ami_show_endpoints) || @@ -1693,7 +1769,9 @@ int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_mod return -1; } - if (!(persistent_endpoints = ao2_container_alloc(PERSISTENT_BUCKETS, persistent_endpoint_hash, persistent_endpoint_cmp))) { + persistent_endpoints = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, + PERSISTENT_BUCKETS, persistent_endpoint_hash, NULL, persistent_endpoint_cmp); + if (!persistent_endpoints) { return -1; } @@ -1785,6 +1863,7 @@ int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_mod ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "t38_udptl_ec", "none", t38udptl_ec_handler, t38udptl_ec_to_str, NULL, 0, 0); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "t38_udptl_maxdatagram", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, media.t38.maxdatagram)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "fax_detect", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, faxdetect)); + ast_sorcery_object_field_register(sip_sorcery, "endpoint", "fax_detect_timeout", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, faxdetect_timeout)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "t38_udptl_nat", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.t38.nat)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "t38_udptl_ipv6", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.t38.ipv6)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "tone_zone", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, zone)); @@ -1827,6 +1906,7 @@ int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_mod ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "contact_deny", "", endpoint_acl_handler, NULL, NULL, 0, 0); ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "contact_permit", "", endpoint_acl_handler, NULL, NULL, 0, 0); ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "contact_acl", "", endpoint_acl_handler, contact_acl_to_str, NULL, 0, 0); + ast_sorcery_object_field_register(sip_sorcery, "endpoint", "subscribe_context", "", OPT_CHAR_ARRAY_T, 0, CHARFLDSET(struct ast_sip_endpoint, subscription.context)); if (ast_sip_initialize_sorcery_transport()) { ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n"); @@ -1887,6 +1967,8 @@ int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_mod ast_sorcery_load(sip_sorcery); + load_all_endpoints(); + return 0; } @@ -1910,6 +1992,7 @@ void ast_res_pjsip_destroy_configuration(void) ast_sip_unregister_cli_formatter(endpoint_formatter); ast_sip_destroy_cli(); ao2_cleanup(persistent_endpoints); + persistent_endpoints = NULL; } int ast_res_pjsip_reload_configuration(void) |