diff --git a/CHANGES b/CHANGES index 12bbea4871..281d059e46 100644 --- a/CHANGES +++ b/CHANGES @@ -177,6 +177,14 @@ cdr_adaptive_odbc names. This setting is configurable for cdr_adaptive_odbc via the quoted_identifiers in configuration file cdr_adaptive_odbc.conf. +------------------------------------------------------------------------------ +--- Functionality changes from Asterisk 13.4.0 to Asterisk 13.5.0 ------------ +------------------------------------------------------------------------------ + +AMI +------------------ + * A new ContactStatus event has been added that reflects res_pjsip contact + lifecycle changes: Created, Removed, Reachable, Unreachable, Unknown. ------------------------------------------------------------------------------ --- Functionality changes from Asterisk 13.3.0 to Asterisk 13.4.0 ------------ diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h index 4023014dc0..bd56c19524 100644 --- a/include/asterisk/res_pjsip.h +++ b/include/asterisk/res_pjsip.h @@ -178,7 +178,9 @@ struct ast_sip_contact { enum ast_sip_contact_status_type { UNAVAILABLE, AVAILABLE, - UNKNOWN + UNKNOWN, + CREATED, + REMOVED, }; /*! diff --git a/include/asterisk/stasis_endpoints.h b/include/asterisk/stasis_endpoints.h index 1d56a8feae..539f270cf2 100644 --- a/include/asterisk/stasis_endpoints.h +++ b/include/asterisk/stasis_endpoints.h @@ -118,6 +118,12 @@ void ast_endpoint_blob_publish(struct ast_endpoint *endpoint, struct stasis_mess */ struct stasis_message_type *ast_endpoint_state_type(void); +/*! + * \brief Message type for endpoint contact state changes. + * \since 13.5 + */ +struct stasis_message_type *ast_endpoint_contact_state_type(void); + /*! * \brief Message type for \ref ast_endpoint_snapshot. * \since 12 diff --git a/main/manager_endpoints.c b/main/manager_endpoints.c index 424e321085..ffcdef06f9 100644 --- a/main/manager_endpoints.c +++ b/main/manager_endpoints.c @@ -75,6 +75,7 @@ int manager_endpoints_init(void) } ret |= stasis_message_router_add(endpoint_router, ast_endpoint_state_type(), endpoint_state_cb, NULL); + ret |= stasis_message_router_add(endpoint_router, ast_endpoint_contact_state_type(), endpoint_state_cb, NULL); /* If somehow we failed to add any routes, just shut down the whole * thing and fail it. diff --git a/main/stasis_endpoints.c b/main/stasis_endpoints.c index f19bb91fe3..da65053551 100644 --- a/main/stasis_endpoints.c +++ b/main/stasis_endpoints.c @@ -71,6 +71,35 @@ ASTERISK_REGISTER_FILE() + + + Raised when the state of a contact changes. + + + This contact's URI. + + + New status of the contact. + + + + + + + + + + The name of the associated aor. + + + The name of the associated endpoint. + + + The RTT measured during the last qualify. + + + + ***/ static struct stasis_cp_all *endpoint_cache_all; @@ -137,6 +166,46 @@ static struct ast_manager_event_blob *peerstatus_to_ami(struct stasis_message *m ast_str_buffer(peerstatus_event_string)); } +static struct ast_manager_event_blob *contactstatus_to_ami(struct stasis_message *msg); + +STASIS_MESSAGE_TYPE_DEFN(ast_endpoint_contact_state_type, + .to_ami = contactstatus_to_ami, +); + +static struct ast_manager_event_blob *contactstatus_to_ami(struct stasis_message *msg) +{ + struct ast_endpoint_blob *obj = stasis_message_data(msg); + RAII_VAR(struct ast_str *, contactstatus_event_string, ast_str_create(64), ast_free); + const char *value; + + if (!(value = ast_json_string_get(ast_json_object_get(obj->blob, "uri")))) { + return NULL; + } + ast_str_append(&contactstatus_event_string, 0, "URI: %s\r\n", value); + + if (!(value = ast_json_string_get(ast_json_object_get(obj->blob, "contact_status")))) { + return NULL; + } + ast_str_append(&contactstatus_event_string, 0, "ContactStatus: %s\r\n", value); + + if (!(value = ast_json_string_get(ast_json_object_get(obj->blob, "aor")))) { + return NULL; + } + ast_str_append(&contactstatus_event_string, 0, "AOR: %s\r\n", value); + + if (!(value = ast_json_string_get(ast_json_object_get(obj->blob, "endpoint_name")))) { + return NULL; + } + ast_str_append(&contactstatus_event_string, 0, "EndpointName: %s\r\n", value); + + if ((value = ast_json_string_get(ast_json_object_get(obj->blob, "roundtrip_usec")))) { + ast_str_append(&contactstatus_event_string, 0, "RoundtripUsec: %s\r\n", value); + } + + return ast_manager_event_blob_create(EVENT_FLAG_SYSTEM, "ContactStatus", + "%s", ast_str_buffer(contactstatus_event_string)); +} + static void endpoint_blob_dtor(void *obj) { struct ast_endpoint_blob *event = obj; @@ -294,6 +363,7 @@ static void endpoints_stasis_cleanup(void) { STASIS_MESSAGE_TYPE_CLEANUP(ast_endpoint_snapshot_type); STASIS_MESSAGE_TYPE_CLEANUP(ast_endpoint_state_type); + STASIS_MESSAGE_TYPE_CLEANUP(ast_endpoint_contact_state_type); ao2_cleanup(endpoint_cache_all); endpoint_cache_all = NULL; @@ -312,6 +382,7 @@ int ast_endpoint_stasis_init(void) res |= STASIS_MESSAGE_TYPE_INIT(ast_endpoint_snapshot_type); res |= STASIS_MESSAGE_TYPE_INIT(ast_endpoint_state_type); + res |= STASIS_MESSAGE_TYPE_INIT(ast_endpoint_contact_state_type); return res; } diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c index 9fa18c7603..59598ecdf6 100644 --- a/res/res_pjsip/pjsip_configuration.c +++ b/res/res_pjsip/pjsip_configuration.c @@ -56,22 +56,47 @@ 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 Structure for communicating contact status to + * persistent_endpoint_update_state from the contact/contact_status + * observers. + */ +struct sip_contact_status { + char *uri; + enum ast_sip_contact_status_type status; + int64_t rtt; +}; + /*! \brief Callback function for changing the state of an endpoint */ -static int persistent_endpoint_update_state(void *obj, void *arg, int flags) +static int persistent_endpoint_update_state(void *obj, void *arg, void *data, int flags) { struct sip_persistent_endpoint *persistent = obj; struct ast_endpoint *endpoint = persistent->endpoint; char *aor = arg; + struct sip_contact_status *status = data; 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; - if (!ast_strlen_zero(aor) && !strstr(persistent->aors, aor)) { - return 0; - } + if (!ast_strlen_zero(aor)) { + if (!strstr(persistent->aors, aor)) { + return 0; + } + if (status) { + char rtt[32]; + snprintf(rtt, 31, "%" 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", 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. */ @@ -121,22 +146,28 @@ static int persistent_endpoint_update_state(void *obj, void *arg, int flags) /*! \brief Function called when stuff relating to a contact happens (created/deleted) */ static void persistent_endpoint_contact_created_observer(const void *object) { - char *id = ast_strdupa(ast_sorcery_object_get_id(object)); + const struct ast_sip_contact *contact = object; + char *id = ast_strdupa(ast_sorcery_object_get_id(contact)); char *aor = NULL; - char *contact = NULL; + char *contact_uri = NULL; + struct sip_contact_status status; aor = id; /* Dynamic contacts are delimited with ";@" and static ones with "@@" */ - if ((contact = strstr(id, ";@")) || (contact = strstr(id, "@@"))) { - *contact = '\0'; - contact += 2; + if ((contact_uri = strstr(id, ";@")) || (contact_uri = strstr(id, "@@"))) { + *contact_uri = '\0'; + contact_uri += 2; } else { - contact = id; + contact_uri = id; } - ast_verb(1, "Contact %s/%s has been created\n", aor, contact); + status.uri = contact_uri; + status.status = CREATED; + status.rtt = 0; - ao2_callback(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, aor); + ast_verb(1, "Contact %s/%s has been created\n", aor, contact_uri); + + ao2_callback_data(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, aor, &status); } /*! \brief Function called when stuff relating to a contact happens (created/deleted) */ @@ -144,20 +175,25 @@ static void persistent_endpoint_contact_deleted_observer(const void *object) { char *id = ast_strdupa(ast_sorcery_object_get_id(object)); char *aor = NULL; - char *contact = NULL; + char *contact_uri = NULL; + struct sip_contact_status status; aor = id; /* Dynamic contacts are delimited with ";@" and static ones with "@@" */ - if ((contact = strstr(id, ";@")) || (contact = strstr(id, "@@"))) { - *contact = '\0'; - contact += 2; + if ((contact_uri = strstr(id, ";@")) || (contact_uri = strstr(id, "@@"))) { + *contact_uri = '\0'; + contact_uri += 2; } else { - contact = id; + contact_uri = id; } - ast_verb(1, "Contact %s/%s has been deleted\n", aor, contact); + ast_verb(1, "Contact %s/%s has been deleted\n", aor, contact_uri); + + status.uri = contact_uri; + status.status = REMOVED; + status.rtt = 0; - ao2_callback(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, aor); + ao2_callback_data(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, aor, &status); } /*! \brief Observer for contacts so state can be updated on respective endpoints */ @@ -172,23 +208,32 @@ static void persistent_endpoint_contact_status_observer(const void *object) const struct ast_sip_contact_status *contact_status = object; char *id = ast_strdupa(ast_sorcery_object_get_id(object)); char *aor = NULL; - char *contact = NULL; + char *contact_uri = NULL; + struct sip_contact_status status; - /* If rtt_start is set (this is the outgoing OPTIONS) or - * there's no status change, ignore. - */ - if (contact_status->rtt_start.tv_sec > 0 - || contact_status->status == contact_status->last_status) { + /* If rtt_start is set (this is the outgoing OPTIONS), ignore. */ + if (contact_status->rtt_start.tv_sec > 0) { return; } aor = id; /* Dynamic contacts are delimited with ";@" and static ones with "@@" */ - if ((contact = strstr(id, ";@")) || (contact = strstr(id, "@@"))) { - *contact = '\0'; - contact += 2; + if ((contact_uri = strstr(id, ";@")) || (contact_uri = strstr(id, "@@"))) { + *contact_uri = '\0'; + contact_uri += 2; + } else { + contact_uri = id; + } + + if (contact_status->status == contact_status->last_status) { + ast_debug(3, "Contact %s status didn't change: %s, RTT: %.3f msec\n", + contact_uri, ast_sip_get_contact_status_label(contact_status->status), + contact_status->rtt / 1000.0); + return; } else { - contact = id; + ast_verb(1, "Contact %s/%s is now %s. RTT: %.3f msec\n", aor, contact_uri, + ast_sip_get_contact_status_label(contact_status->status), + contact_status->rtt / 1000.0); } ast_test_suite_event_notify("AOR_CONTACT_UPDATE", @@ -197,10 +242,11 @@ static void persistent_endpoint_contact_status_observer(const void *object) ast_sorcery_object_get_id(contact_status), ast_sip_get_contact_status_label(contact_status->status)); - ast_verb(1, "Contact %s/%s is now %s\n", aor, contact, - ast_sip_get_contact_status_label(contact_status->status)); + status.uri = contact_uri; + status.status = contact_status->status; + status.rtt = contact_status->rtt; - ao2_callback(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, aor); + ao2_callback_data(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, aor, &status); } /*! \brief Observer for contacts so state can be updated on respective endpoints */ @@ -1025,7 +1071,7 @@ static struct ast_endpoint *persistent_endpoint_find_or_create(const struct ast_ if (ast_strlen_zero(persistent->aors)) { ast_endpoint_set_state(persistent->endpoint, AST_ENDPOINT_UNKNOWN); } else { - persistent_endpoint_update_state(persistent, NULL, 0); + persistent_endpoint_update_state(persistent, NULL, NULL, 0); } ao2_link_flags(persistent_endpoints, persistent, OBJ_NOLOCK); diff --git a/res/res_pjsip/pjsip_options.c b/res/res_pjsip/pjsip_options.c index 87c67fae35..e3e8f18082 100644 --- a/res/res_pjsip/pjsip_options.c +++ b/res/res_pjsip/pjsip_options.c @@ -39,12 +39,17 @@ static const char *status_map [] = { [UNAVAILABLE] = "Unreachable", [AVAILABLE] = "Reachable", [UNKNOWN] = "Unknown", + [CREATED] = "Created", + [REMOVED] = "Removed", + }; static const char *short_status_map [] = { [UNAVAILABLE] = "Unavail", [AVAILABLE] = "Avail", [UNKNOWN] = "Unknown", + [CREATED] = "Created", + [REMOVED] = "Removed", }; const char *ast_sip_get_contact_status_label(const enum ast_sip_contact_status_type status)