2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2013, Digium, Inc.
6 * Matt Jordan <mjordan@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
25 #include "asterisk/res_pjsip.h"
26 #include "asterisk/channel.h"
27 #include "asterisk/pbx.h"
28 #include "asterisk/astobj2.h"
29 #include "asterisk/cli.h"
30 #include "asterisk/time.h"
31 #include "asterisk/test.h"
32 #include "include/res_pjsip_private.h"
34 #define DEFAULT_LANGUAGE "en"
35 #define DEFAULT_ENCODING "text/plain"
36 #define QUALIFIED_BUCKETS 211
38 static const char *status_map [] = {
39 [UNAVAILABLE] = "Unreachable",
40 [AVAILABLE] = "Reachable",
41 [UNKNOWN] = "Unknown",
44 static const char *short_status_map [] = {
45 [UNAVAILABLE] = "Unavail",
46 [AVAILABLE] = "Avail",
47 [UNKNOWN] = "Unknown",
50 const char *ast_sip_get_contact_status_label(const enum ast_sip_contact_status_type status)
52 return status_map[status];
55 const char *ast_sip_get_contact_short_status_label(const enum ast_sip_contact_status_type status)
57 return short_status_map[status];
62 * \brief Create a ast_sip_contact_status object.
64 static void *contact_status_alloc(const char *name)
66 struct ast_sip_contact_status *status = ast_sorcery_generic_alloc(sizeof(*status), NULL);
69 ast_log(LOG_ERROR, "Unable to allocate ast_sip_contact_status\n");
73 status->status = UNKNOWN;
80 * \brief Retrieve a ast_sip_contact_status object from sorcery creating
83 static struct ast_sip_contact_status *find_or_create_contact_status(const struct ast_sip_contact *contact)
85 struct ast_sip_contact_status *status;
87 status = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), CONTACT_STATUS,
88 ast_sorcery_object_get_id(contact));
93 status = ast_sorcery_alloc(ast_sip_get_sorcery(), CONTACT_STATUS,
94 ast_sorcery_object_get_id(contact));
96 ast_log(LOG_ERROR, "Unable to create ast_sip_contact_status for contact %s\n",
101 if (ast_sorcery_create(ast_sip_get_sorcery(), status)) {
102 ast_log(LOG_ERROR, "Unable to persist ast_sip_contact_status for contact %s\n",
113 * \brief Update an ast_sip_contact_status's elements.
115 static void update_contact_status(const struct ast_sip_contact *contact,
116 enum ast_sip_contact_status_type value)
118 struct ast_sip_contact_status *status;
119 struct ast_sip_contact_status *update;
121 status = find_or_create_contact_status(contact);
123 ast_log(LOG_ERROR, "Unable to find ast_sip_contact_status for contact %s\n",
128 update = ast_sorcery_alloc(ast_sip_get_sorcery(), CONTACT_STATUS,
129 ast_sorcery_object_get_id(status));
131 ast_log(LOG_ERROR, "Unable to allocate ast_sip_contact_status for contact %s\n",
136 update->last_status = status->status;
137 update->status = value;
139 /* if the contact is available calculate the rtt as
140 the diff between the last start time and "now" */
141 update->rtt = update->status == AVAILABLE && status->rtt_start.tv_sec > 0 ?
142 ast_tvdiff_us(ast_tvnow(), status->rtt_start) : 0;
144 update->rtt_start = ast_tv(0, 0);
148 ast_test_suite_event_notify("AOR_CONTACT_QUALIFY_RESULT",
152 ast_sorcery_object_get_id(update),
153 ast_sip_get_contact_status_label(update->status),
156 if (ast_sorcery_update(ast_sip_get_sorcery(), update)) {
157 ast_log(LOG_ERROR, "Unable to update ast_sip_contact_status for contact %s\n",
167 * \brief Initialize the start time on a contact status so the round
168 * trip time can be calculated upon a valid response.
170 static void init_start_time(const struct ast_sip_contact *contact)
172 struct ast_sip_contact_status *status;
173 struct ast_sip_contact_status *update;
175 status = find_or_create_contact_status(contact);
177 ast_log(LOG_ERROR, "Unable to find ast_sip_contact_status for contact %s\n",
182 update = ast_sorcery_alloc(ast_sip_get_sorcery(), CONTACT_STATUS,
183 ast_sorcery_object_get_id(status));
185 ast_log(LOG_ERROR, "Unable to copy ast_sip_contact_status for contact %s\n",
190 update->status = status->status;
191 update->last_status = status->last_status;
192 update->rtt = status->rtt;
193 update->rtt_start = ast_tvnow();
195 if (ast_sorcery_update(ast_sip_get_sorcery(), update)) {
196 ast_log(LOG_ERROR, "Unable to update ast_sip_contact_status for contact %s\n",
206 * \brief Match a container contact object with the contact sorcery id looking for.
208 * \param obj pointer to the (user-defined part) of an object.
209 * \param arg callback argument from ao2_callback()
210 * \param flags flags from ao2_callback()
212 * \return Values are a combination of enum _cb_results.
214 static int match_contact_id(void *obj, void *arg, int flags)
216 struct ast_sip_contact *contact = obj;
217 const char *looking_for = arg;
219 return strcmp(ast_sorcery_object_get_id(contact), looking_for) ? 0 : CMP_MATCH;
224 * \brief For an endpoint try to match the given contact sorcery id.
226 static int on_endpoint(void *obj, void *arg, int flags)
228 struct ast_sip_endpoint *endpoint = obj;
229 struct ast_sip_contact *contact;
230 char *looking_for = arg;
234 if (!arg || ast_strlen_zero(endpoint->aors)) {
238 aors = ast_strdupa(endpoint->aors);
239 while ((aor_name = strsep(&aors, ","))) {
240 struct ast_sip_aor *aor;
241 struct ao2_container *contacts;
243 aor = ast_sip_location_retrieve_aor(aor_name);
248 contacts = ast_sip_location_retrieve_aor_contacts(aor);
254 contact = ao2_callback(contacts, 0, match_contact_id, looking_for);
255 ao2_ref(contacts, -1);
257 ao2_ref(contact, -1);
267 * \brief Find an endpoint associated with the given contact.
269 static struct ast_sip_endpoint *find_an_endpoint(struct ast_sip_contact *contact)
271 char *looking_for = (char *) ast_sorcery_object_get_id(contact);
272 struct ao2_container *endpoints = ast_sip_get_endpoints();
273 struct ast_sip_endpoint *endpoint;
275 endpoint = ao2_callback(endpoints, 0, on_endpoint, looking_for);
276 ao2_ref(endpoints, -1);
282 * \brief Receive a response to the qualify contact request.
284 static void qualify_contact_cb(void *token, pjsip_event *e)
286 struct ast_sip_contact *contact = token;
288 switch(e->body.tsx_state.type) {
290 ast_log(LOG_ERROR, "Unexpected PJSIP event %u\n", e->body.tsx_state.type);
292 case PJSIP_EVENT_TRANSPORT_ERROR:
293 case PJSIP_EVENT_TIMER:
294 update_contact_status(contact, UNAVAILABLE);
296 case PJSIP_EVENT_RX_MSG:
297 update_contact_status(contact, AVAILABLE);
300 ao2_cleanup(contact);
305 * \brief Attempt to qualify the contact
307 * \details Sends a SIP OPTIONS request to the given contact in order to make
308 * sure that contact is available.
310 static int qualify_contact(struct ast_sip_endpoint *endpoint, struct ast_sip_contact *contact)
312 pjsip_tx_data *tdata;
313 RAII_VAR(struct ast_sip_endpoint *, endpoint_local, NULL, ao2_cleanup);
315 if (contact->authenticate_qualify) {
316 endpoint_local = ao2_bump(endpoint);
317 if (!endpoint_local) {
319 * Find the "first" endpoint to completely qualify the contact - any
320 * endpoint that is associated with the contact should do.
322 endpoint_local = find_an_endpoint(contact);
323 if (!endpoint_local) {
324 ast_log(LOG_ERROR, "Unable to find an endpoint to qualify contact %s\n",
331 if (ast_sip_create_request("OPTIONS", NULL, NULL, NULL, contact, &tdata)) {
332 ast_log(LOG_ERROR, "Unable to create request to qualify contact %s\n",
337 /* If an outbound proxy is specified set it on this request */
338 if (!ast_strlen_zero(contact->outbound_proxy) &&
339 ast_sip_set_outbound_proxy(tdata, contact->outbound_proxy)) {
340 pjsip_tx_data_dec_ref(tdata);
341 ast_log(LOG_ERROR, "Unable to apply outbound proxy on request to qualify contact %s\n",
346 init_start_time(contact);
348 ao2_ref(contact, +1);
349 if (ast_sip_send_out_of_dialog_request(tdata, endpoint_local, (int)(contact->qualify_timeout * 1000), contact, qualify_contact_cb)
351 ast_log(LOG_ERROR, "Unable to send request to qualify contact %s\n",
353 update_contact_status(contact, UNAVAILABLE);
354 ao2_ref(contact, -1);
363 * \brief Scheduling context for sending QUALIFY request at specified intervals.
365 static struct ast_sched_context *sched;
369 * \brief Container to hold all actively scheduled qualifies.
371 static struct ao2_container *sched_qualifies;
375 * \brief Structure to hold qualify contact scheduling information.
378 /*! The scheduling id */
380 /*! The the contact being checked */
381 struct ast_sip_contact *contact;
386 * \brief Destroy the scheduled data and remove from scheduler.
388 static void sched_data_destructor(void *obj)
390 struct sched_data *data = obj;
392 ao2_cleanup(data->contact);
396 * \brief Create the scheduling data object.
398 static struct sched_data *sched_data_create(struct ast_sip_contact *contact)
400 struct sched_data *data;
402 data = ao2_t_alloc(sizeof(*data), sched_data_destructor, contact->uri);
404 ast_log(LOG_ERROR, "Unable to create schedule qualify data for contact %s\n",
409 data->contact = contact;
410 ao2_ref(data->contact, +1);
417 * \brief Send a qualify contact request within a threaded task.
419 static int qualify_contact_task(void *obj)
421 struct ast_sip_contact *contact = obj;
424 res = qualify_contact(NULL, contact);
425 ao2_ref(contact, -1);
431 * \brief Send a scheduled qualify contact request.
433 static int qualify_contact_sched(const void *obj)
435 struct sched_data *data = (struct sched_data *) obj;
437 ao2_ref(data->contact, +1);
438 if (ast_sip_push_task(NULL, qualify_contact_task, data->contact)) {
439 ao2_ref(data->contact, -1);
443 * Always reschedule rather than have a potential race cleaning
444 * up the data object ref between self deletion and an external
447 return data->contact->qualify_frequency * 1000;
452 * \brief Set up a scheduled qualify contact check.
454 static void schedule_qualify(struct ast_sip_contact *contact, int initial_interval)
456 struct sched_data *data;
458 data = sched_data_create(contact);
463 ast_assert(contact->qualify_frequency != 0);
465 ao2_t_ref(data, +1, "Ref for qualify_contact_sched() scheduler entry");
466 data->id = ast_sched_add_variable(sched, initial_interval,
467 qualify_contact_sched, data, 1);
469 ao2_t_ref(data, -1, "Cleanup failed scheduler add");
470 ast_log(LOG_ERROR, "Unable to schedule qualify for contact %s\n",
472 } else if (!ao2_link(sched_qualifies, data)) {
473 AST_SCHED_DEL_UNREF(sched, data->id,
474 ao2_t_ref(data, -1, "Cleanup scheduler for failed ao2_link"));
476 ao2_t_ref(data, -1, "Done setting up scheduler entry");
481 * \brief Remove the contact from the scheduler.
483 static void unschedule_qualify(struct ast_sip_contact *contact)
485 struct sched_data *data;
487 data = ao2_find(sched_qualifies, contact, OBJ_UNLINK | OBJ_SEARCH_KEY);
492 AST_SCHED_DEL_UNREF(sched, data->id,
493 ao2_t_ref(data, -1, "Delete scheduler entry ref"));
494 ao2_t_ref(data, -1, "Done with ao2_find ref");
499 * \brief Qualify the given contact and set up scheduling if configured.
501 static void qualify_and_schedule(struct ast_sip_contact *contact)
503 unschedule_qualify(contact);
505 if (contact->qualify_frequency) {
506 ao2_ref(contact, +1);
507 if (ast_sip_push_task(NULL, qualify_contact_task, contact)) {
508 ao2_ref(contact, -1);
511 schedule_qualify(contact, contact->qualify_frequency * 1000);
513 update_contact_status(contact, UNKNOWN);
519 * \brief A new contact has been created make sure it is available.
521 static void contact_created(const void *obj)
523 qualify_and_schedule((struct ast_sip_contact *) obj);
528 * \brief A contact has been deleted remove status tracking.
530 static void contact_deleted(const void *obj)
532 struct ast_sip_contact *contact = (struct ast_sip_contact *) obj;
533 struct ast_sip_contact_status *status;
535 unschedule_qualify(contact);
537 status = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), CONTACT_STATUS,
538 ast_sorcery_object_get_id(contact));
543 if (ast_sorcery_delete(ast_sip_get_sorcery(), status)) {
544 ast_log(LOG_ERROR, "Unable to delete ast_sip_contact_status for contact %s\n",
550 static const struct ast_sorcery_observer contact_observer = {
551 .created = contact_created,
552 .deleted = contact_deleted
555 static pj_bool_t options_start(void)
557 sched = ast_sched_context_create();
561 if (ast_sched_start_thread(sched)) {
562 ast_sched_context_destroy(sched);
567 if (ast_sorcery_observer_add(ast_sip_get_sorcery(), "contact", &contact_observer)) {
568 ast_log(LOG_WARNING, "Unable to add contact observer\n");
569 ast_sched_context_destroy(sched);
577 static int sched_qualifies_empty(void *obj, void *arg, int flags)
579 ao2_t_ref(obj, -1, "Release ref held by destroyed scheduler context.");
583 static pj_bool_t options_stop(void)
585 ast_sorcery_observer_remove(ast_sip_get_sorcery(), "contact", &contact_observer);
588 ast_sched_context_destroy(sched);
592 /* Empty the container of scheduling data refs. */
593 ao2_callback(sched_qualifies, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE,
594 sched_qualifies_empty, NULL);
599 static pj_status_t send_options_response(pjsip_rx_data *rdata, int code)
601 pjsip_endpoint *endpt = ast_sip_get_pjsip_endpoint();
602 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
603 pjsip_transaction *trans = pjsip_rdata_get_tsx(rdata);
604 pjsip_tx_data *tdata;
605 const pjsip_hdr *hdr;
608 /* Make the response object */
609 if ((status = ast_sip_create_response(rdata, code, NULL, &tdata) != PJ_SUCCESS)) {
610 ast_log(LOG_ERROR, "Unable to create response (%d)\n", status);
614 /* Add appropriate headers */
615 if ((hdr = pjsip_endpt_get_capability(endpt, PJSIP_H_ACCEPT, NULL))) {
616 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, hdr));
618 if ((hdr = pjsip_endpt_get_capability(endpt, PJSIP_H_ALLOW, NULL))) {
619 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, hdr));
621 if ((hdr = pjsip_endpt_get_capability(endpt, PJSIP_H_SUPPORTED, NULL))) {
622 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, hdr));
626 * XXX TODO: pjsip doesn't care a lot about either of these headers -
627 * while it provides specific methods to create them, they are defined
628 * to be the standard string header creation. We never did add them
629 * in chan_sip, although RFC 3261 says they SHOULD. Hard coded here.
631 ast_sip_add_header(tdata, "Accept-Encoding", DEFAULT_ENCODING);
632 ast_sip_add_header(tdata, "Accept-Language", DEFAULT_LANGUAGE);
635 status = pjsip_dlg_send_response(dlg, trans, tdata);
637 struct ast_sip_endpoint *endpoint;
639 endpoint = ast_pjsip_rdata_get_endpoint(rdata);
640 status = ast_sip_send_stateful_response(rdata, tdata, endpoint);
641 ao2_cleanup(endpoint);
644 if (status != PJ_SUCCESS) {
645 ast_log(LOG_ERROR, "Unable to send response (%d)\n", status);
651 static pj_bool_t options_on_rx_request(pjsip_rx_data *rdata)
653 RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
655 pjsip_sip_uri *sip_ruri;
656 char exten[AST_MAX_EXTENSION];
658 if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
659 &pjsip_options_method)) {
663 if (!(endpoint = ast_pjsip_rdata_get_endpoint(rdata))) {
667 ruri = rdata->msg_info.msg->line.req.uri;
668 if (!PJSIP_URI_SCHEME_IS_SIP(ruri) && !PJSIP_URI_SCHEME_IS_SIPS(ruri)) {
669 send_options_response(rdata, 416);
673 sip_ruri = pjsip_uri_get_uri(ruri);
674 ast_copy_pj_str(exten, &sip_ruri->user, sizeof(exten));
676 if (ast_shutting_down()) {
678 * Not taking any new calls at this time.
679 * Likely a server availability OPTIONS poll.
681 send_options_response(rdata, 503);
682 } else if (!ast_strlen_zero(exten) && !ast_exists_extension(NULL, endpoint->context, exten, 1, NULL)) {
683 send_options_response(rdata, 404);
685 send_options_response(rdata, 200);
690 static pjsip_module options_module = {
691 .name = {"Options Module", 14},
693 .priority = PJSIP_MOD_PRIORITY_APPLICATION,
694 .start = options_start,
695 .stop = options_stop,
696 .on_rx_request = options_on_rx_request,
701 * \brief Send qualify request to the given contact.
703 static int cli_on_contact(void *obj, void *arg, void *data, int flags)
705 struct ast_sip_contact *contact = obj;
706 struct ast_sip_endpoint *endpoint = data;
709 ast_cli(*cli_fd, " contact %s\n", contact->uri);
710 qualify_contact(endpoint, contact);
715 * \brief Data pushed to threadpool to qualify endpoints from the CLI
717 struct qualify_data {
718 /*! Endpoint that is being qualified */
719 struct ast_sip_endpoint *endpoint;
720 /*! CLI File descriptor for printing messages */
724 static struct qualify_data *qualify_data_alloc(struct ast_sip_endpoint *endpoint, int cli_fd)
726 struct qualify_data *qual_data;
728 qual_data = ast_malloc(sizeof(*qual_data));
733 qual_data->endpoint = ao2_bump(endpoint);
734 qual_data->cli_fd = cli_fd;
738 static void qualify_data_destroy(struct qualify_data *qual_data)
740 ao2_cleanup(qual_data->endpoint);
746 * \brief For an endpoint iterate over and qualify all aors/contacts
748 static int cli_qualify_contacts(void *data)
752 RAII_VAR(struct qualify_data *, qual_data, data, qualify_data_destroy);
753 struct ast_sip_endpoint *endpoint = qual_data->endpoint;
754 int cli_fd = qual_data->cli_fd;
755 const char *endpoint_name = ast_sorcery_object_get_id(endpoint);
757 if (ast_strlen_zero(endpoint->aors)) {
758 ast_cli(cli_fd, "Endpoint %s has no AoR's configured\n",
763 aors = ast_strdupa(endpoint->aors);
764 while ((aor_name = strsep(&aors, ","))) {
765 struct ast_sip_aor *aor;
766 struct ao2_container *contacts;
768 aor = ast_sip_location_retrieve_aor(aor_name);
773 contacts = ast_sip_location_retrieve_aor_contacts(aor);
775 ast_cli(cli_fd, "Sending qualify to endpoint %s\n", endpoint_name);
776 ao2_callback_data(contacts, OBJ_NODATA, cli_on_contact, &cli_fd, endpoint);
777 ao2_ref(contacts, -1);
785 static char *cli_qualify(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
787 RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
788 const char *endpoint_name;
789 struct qualify_data *qual_data;
793 e->command = "pjsip qualify";
795 "Usage: pjsip qualify <endpoint>\n"
796 " Send a SIP OPTIONS request to all contacts on the endpoint.\n";
803 return CLI_SHOWUSAGE;
806 endpoint_name = a->argv[2];
808 if (!(endpoint = ast_sorcery_retrieve_by_id(
809 ast_sip_get_sorcery(), "endpoint", endpoint_name))) {
810 ast_cli(a->fd, "Unable to retrieve endpoint %s\n", endpoint_name);
814 qual_data = qualify_data_alloc(endpoint, a->fd);
819 if (ast_sip_push_task(NULL, cli_qualify_contacts, qual_data)) {
820 qualify_data_destroy(qual_data);
829 * \brief Send qualify request to the given contact.
831 static int ami_contact_cb(void *obj, void *arg, int flags)
833 struct ast_sip_contact *contact = obj;
835 ao2_ref(contact, +1);
836 if (ast_sip_push_task(NULL, qualify_contact_task, contact)) {
837 ao2_ref(contact, -1);
842 static int ami_sip_qualify(struct mansession *s, const struct message *m)
844 const char *endpoint_name = astman_get_header(m, "Endpoint");
845 RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
849 if (ast_strlen_zero(endpoint_name)) {
850 astman_send_error(s, m, "Endpoint parameter missing.");
854 endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint",
857 astman_send_error(s, m, "Unable to retrieve endpoint\n");
861 /* send a qualify for all contacts registered with the endpoint */
862 if (ast_strlen_zero(endpoint->aors)) {
863 astman_send_error(s, m, "No AoRs configured for endpoint\n");
867 aors = ast_strdupa(endpoint->aors);
868 while ((aor_name = strsep(&aors, ","))) {
869 struct ast_sip_aor *aor;
870 struct ao2_container *contacts;
872 aor = ast_sip_location_retrieve_aor(aor_name);
877 contacts = ast_sip_location_retrieve_aor_contacts(aor);
879 ao2_callback(contacts, OBJ_NODATA, ami_contact_cb, NULL);
880 ao2_ref(contacts, -1);
886 astman_send_ack(s, m, "Endpoint found, will qualify");
890 static struct ast_cli_entry cli_options[] = {
891 AST_CLI_DEFINE(cli_qualify, "Send an OPTIONS request to a PJSIP endpoint")
894 static int sched_qualifies_hash_fn(const void *obj, int flags)
896 const struct sched_data *object;
897 const struct ast_sip_contact *key;
899 switch (flags & OBJ_SEARCH_MASK) {
903 case OBJ_SEARCH_OBJECT:
905 key = object->contact;
908 /* Hash can only work on something with a full key. */
912 return ast_str_hash(ast_sorcery_object_get_id(key));
915 static int sched_qualifies_cmp_fn(void *obj, void *arg, int flags)
917 const struct sched_data *object_left = obj;
918 const struct sched_data *object_right = arg;
919 struct ast_sip_contact *right_key = arg;
922 switch (flags & OBJ_SEARCH_MASK) {
923 case OBJ_SEARCH_OBJECT:
924 right_key = object_right->contact;
927 cmp = strcmp(ast_sorcery_object_get_id(object_left->contact),
928 ast_sorcery_object_get_id(right_key));
930 case OBJ_SEARCH_PARTIAL_KEY:
931 /* Not supported by container. */
936 * What arg points to is specific to this traversal callback
937 * and has no special meaning to astobj2.
946 * At this point the traversal callback is identical to a sorted
952 static int rtt_start_handler(const struct aco_option *opt,
953 struct ast_variable *var, void *obj)
955 struct ast_sip_contact_status *status = obj;
958 if (sscanf(var->value, "%ld.%06ld", &sec, &usec) != 2) {
962 status->rtt_start = ast_tv(sec, usec);
967 static int rtt_start_to_str(const void *obj, const intptr_t *args, char **buf)
969 const struct ast_sip_contact_status *status = obj;
971 if (ast_asprintf(buf, "%ld.%06ld", status->rtt_start.tv_sec, status->rtt_start.tv_usec) == -1) {
978 int ast_sip_initialize_sorcery_qualify(void)
980 struct ast_sorcery *sorcery = ast_sip_get_sorcery();
982 /* initialize sorcery ast_sip_contact_status resource */
983 ast_sorcery_apply_default(sorcery, CONTACT_STATUS, "memory", NULL);
985 if (ast_sorcery_internal_object_register(sorcery, CONTACT_STATUS,
986 contact_status_alloc, NULL, NULL)) {
987 ast_log(LOG_ERROR, "Unable to register ast_sip_contact_status in sorcery\n");
991 ast_sorcery_object_field_register_nodoc(sorcery, CONTACT_STATUS, "last_status",
992 "0", OPT_UINT_T, 1, FLDSET(struct ast_sip_contact_status, last_status));
993 ast_sorcery_object_field_register_nodoc(sorcery, CONTACT_STATUS, "status",
994 "0", OPT_UINT_T, 1, FLDSET(struct ast_sip_contact_status, status));
995 ast_sorcery_object_field_register_custom_nodoc(sorcery, CONTACT_STATUS, "rtt_start",
996 "0.0", rtt_start_handler, rtt_start_to_str, NULL, 0, 0);
997 ast_sorcery_object_field_register_nodoc(sorcery, CONTACT_STATUS, "rtt",
998 "0", OPT_UINT_T, 1, FLDSET(struct ast_sip_contact_status, rtt));
1003 static int qualify_and_schedule_cb(void *obj, void *arg, int flags)
1005 struct ast_sip_contact *contact = obj;
1006 struct ast_sip_aor *aor = arg;
1007 int initial_interval;
1008 int max_time = ast_sip_get_max_initial_qualify_time();
1010 contact->qualify_frequency = aor->qualify_frequency;
1011 contact->qualify_timeout = aor->qualify_timeout;
1012 contact->authenticate_qualify = aor->authenticate_qualify;
1014 /* Delay initial qualification by a random fraction of the specified interval */
1015 if (max_time && max_time < contact->qualify_frequency) {
1016 initial_interval = max_time;
1018 initial_interval = contact->qualify_frequency;
1021 initial_interval = (int)((initial_interval * 1000) * ast_random_double());
1023 if (contact->qualify_frequency) {
1024 schedule_qualify(contact, initial_interval);
1026 update_contact_status(contact, UNKNOWN);
1034 * \brief Qualify and schedule an endpoint's contacts
1036 * \details For the given endpoint retrieve its list of aors, qualify all
1037 * contacts, and schedule for checks if configured.
1039 static int qualify_and_schedule_all_cb(void *obj, void *arg, int flags)
1041 struct ast_sip_endpoint *endpoint = obj;
1045 if (ast_strlen_zero(endpoint->aors)) {
1049 aors = ast_strdupa(endpoint->aors);
1050 while ((aor_name = strsep(&aors, ","))) {
1051 struct ast_sip_aor *aor;
1052 struct ao2_container *contacts;
1054 aor = ast_sip_location_retrieve_aor(aor_name);
1059 contacts = ast_sip_location_retrieve_aor_contacts(aor);
1061 ao2_callback(contacts, OBJ_NODATA, qualify_and_schedule_cb, aor);
1062 ao2_ref(contacts, -1);
1073 * \brief Unschedule all existing contacts
1075 static int unschedule_all_cb(void *obj, void *arg, int flags)
1077 struct sched_data *data = obj;
1079 AST_SCHED_DEL_UNREF(sched, data->id, ao2_ref(data, -1));
1084 static void qualify_and_schedule_all(void)
1086 struct ao2_container *endpoints = ast_sip_get_endpoints();
1088 ao2_callback(sched_qualifies, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, unschedule_all_cb, NULL);
1094 ao2_callback(endpoints, OBJ_NODATA, qualify_and_schedule_all_cb, NULL);
1095 ao2_ref(endpoints, -1);
1098 static int format_contact_status(void *obj, void *arg, int flags)
1100 struct ast_sip_contact_wrapper *wrapper = obj;
1101 struct ast_sip_contact *contact = wrapper->contact;
1102 struct ast_sip_ami *ami = arg;
1103 struct ast_sip_contact_status *status;
1104 struct ast_str *buf;
1105 const struct ast_sip_endpoint *endpoint = ami->arg;
1107 buf = ast_sip_create_ami_event("ContactStatusDetail", ami);
1112 status = ast_sorcery_retrieve_by_id(
1113 ast_sip_get_sorcery(), CONTACT_STATUS,
1114 ast_sorcery_object_get_id(contact));
1116 ast_str_append(&buf, 0, "AOR: %s\r\n", wrapper->aor_id);
1117 ast_str_append(&buf, 0, "URI: %s\r\n", contact->uri);
1118 ast_str_append(&buf, 0, "Status: %s\r\n", ast_sip_get_contact_status_label(status->status));
1119 if (status->status == UNKNOWN) {
1120 ast_str_append(&buf, 0, "RoundtripUsec: N/A\r\n");
1122 ast_str_append(&buf, 0, "RoundtripUsec: %" PRId64 "\r\n", status->rtt);
1124 ast_str_append(&buf, 0, "EndpointName: %s\r\n",
1125 ast_sorcery_object_get_id(endpoint));
1126 astman_append(ami->s, "%s\r\n", ast_str_buffer(buf));
1130 ao2_cleanup(status);
1134 static int format_contact_status_for_aor(void *obj, void *arg, int flags)
1136 struct ast_sip_aor *aor = obj;
1138 return ast_sip_for_each_contact(aor, format_contact_status, arg);
1141 static int format_ami_contact_status(const struct ast_sip_endpoint *endpoint,
1142 struct ast_sip_ami *ami)
1144 ami->arg = (void *)endpoint;
1145 return ast_sip_for_each_aor(endpoint->aors, format_contact_status_for_aor, ami);
1148 static struct ast_sip_endpoint_formatter contact_status_formatter = {
1149 .format_ami = format_ami_contact_status
1152 int ast_res_pjsip_init_options_handling(int reload)
1154 static const pj_str_t STR_OPTIONS = { "OPTIONS", 7 };
1157 qualify_and_schedule_all();
1161 sched_qualifies = ao2_t_container_alloc(QUALIFIED_BUCKETS,
1162 sched_qualifies_hash_fn, sched_qualifies_cmp_fn,
1163 "Create container for scheduled qualifies");
1164 if (!sched_qualifies) {
1168 if (pjsip_endpt_register_module(ast_sip_get_pjsip_endpoint(), &options_module) != PJ_SUCCESS) {
1169 ao2_cleanup(sched_qualifies);
1170 sched_qualifies = NULL;
1174 if (pjsip_endpt_add_capability(ast_sip_get_pjsip_endpoint(), NULL, PJSIP_H_ALLOW,
1175 NULL, 1, &STR_OPTIONS) != PJ_SUCCESS) {
1176 pjsip_endpt_unregister_module(ast_sip_get_pjsip_endpoint(), &options_module);
1177 ao2_cleanup(sched_qualifies);
1178 sched_qualifies = NULL;
1182 internal_sip_register_endpoint_formatter(&contact_status_formatter);
1183 ast_manager_register2("PJSIPQualify", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, ami_sip_qualify, NULL, NULL, NULL);
1184 ast_cli_register_multiple(cli_options, ARRAY_LEN(cli_options));
1186 qualify_and_schedule_all();
1191 void ast_res_pjsip_cleanup_options_handling(void)
1193 ast_cli_unregister_multiple(cli_options, ARRAY_LEN(cli_options));
1194 ast_manager_unregister("PJSIPQualify");
1195 internal_sip_unregister_endpoint_formatter(&contact_status_formatter);
1197 pjsip_endpt_unregister_module(ast_sip_get_pjsip_endpoint(), &options_module);
1198 ao2_cleanup(sched_qualifies);
1199 sched_qualifies = NULL;