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
40 * \brief Create a ast_sip_contact_status object.
42 static void *contact_status_alloc(const char *name)
44 struct ast_sip_contact_status *status = ast_sorcery_generic_alloc(sizeof(*status), NULL);
47 ast_log(LOG_ERROR, "Unable to allocate ast_sip_contact_status\n");
51 status->status = UNAVAILABLE;
58 * \brief Retrieve a ast_sip_contact_status object from sorcery creating
61 static struct ast_sip_contact_status *find_or_create_contact_status(const struct ast_sip_contact *contact)
63 struct ast_sip_contact_status *status;
65 status = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), CONTACT_STATUS,
66 ast_sorcery_object_get_id(contact));
71 status = ast_sorcery_alloc(ast_sip_get_sorcery(), CONTACT_STATUS,
72 ast_sorcery_object_get_id(contact));
74 ast_log(LOG_ERROR, "Unable to create ast_sip_contact_status for contact %s\n",
79 if (ast_sorcery_create(ast_sip_get_sorcery(), status)) {
80 ast_log(LOG_ERROR, "Unable to persist ast_sip_contact_status for contact %s\n",
89 static void delete_contact_status(const struct ast_sip_contact *contact)
91 struct ast_sip_contact_status *status = ast_sorcery_retrieve_by_id(
92 ast_sip_get_sorcery(), CONTACT_STATUS, ast_sorcery_object_get_id(contact));
98 ast_sorcery_delete(ast_sip_get_sorcery(), status);
104 * \brief Update an ast_sip_contact_status's elements.
106 static void update_contact_status(const struct ast_sip_contact *contact,
107 enum ast_sip_contact_status_type value)
109 struct ast_sip_contact_status *status;
110 struct ast_sip_contact_status *update;
112 status = find_or_create_contact_status(contact);
114 ast_log(LOG_ERROR, "Unable to find ast_sip_contact_status for contact %s\n",
119 update = ast_sorcery_alloc(ast_sip_get_sorcery(), CONTACT_STATUS,
120 ast_sorcery_object_get_id(status));
122 ast_log(LOG_ERROR, "Unable to allocate ast_sip_contact_status for contact %s\n",
127 update->last_status = status->status;
128 update->status = value;
130 /* if the contact is available calculate the rtt as
131 the diff between the last start time and "now" */
132 update->rtt = update->status == AVAILABLE ?
133 ast_tvdiff_us(ast_tvnow(), status->rtt_start) : 0;
135 update->rtt_start = ast_tv(0, 0);
137 ast_test_suite_event_notify("AOR_CONTACT_QUALIFY_RESULT",
141 ast_sorcery_object_get_id(update),
142 (update->status == AVAILABLE ? "Available" : "Unavailable"),
145 if (ast_sorcery_update(ast_sip_get_sorcery(), update)) {
146 ast_log(LOG_ERROR, "Unable to update ast_sip_contact_status for contact %s\n",
156 * \brief Initialize the start time on a contact status so the round
157 * trip time can be calculated upon a valid response.
159 static void init_start_time(const struct ast_sip_contact *contact)
161 struct ast_sip_contact_status *status;
162 struct ast_sip_contact_status *update;
164 status = find_or_create_contact_status(contact);
166 ast_log(LOG_ERROR, "Unable to find ast_sip_contact_status for contact %s\n",
171 update = ast_sorcery_alloc(ast_sip_get_sorcery(), CONTACT_STATUS,
172 ast_sorcery_object_get_id(status));
174 ast_log(LOG_ERROR, "Unable to copy ast_sip_contact_status for contact %s\n",
179 update->status = status->status;
180 update->last_status = status->last_status;
181 update->rtt = status->rtt;
182 update->rtt_start = ast_tvnow();
184 if (ast_sorcery_update(ast_sip_get_sorcery(), update)) {
185 ast_log(LOG_ERROR, "Unable to update ast_sip_contact_status for contact %s\n",
195 * \brief Match a container contact object with the contact sorcery id looking for.
197 * \param obj pointer to the (user-defined part) of an object.
198 * \param arg callback argument from ao2_callback()
199 * \param flags flags from ao2_callback()
201 * \return Values are a combination of enum _cb_results.
203 static int match_contact_id(void *obj, void *arg, int flags)
205 struct ast_sip_contact *contact = obj;
206 const char *looking_for = arg;
208 return strcmp(ast_sorcery_object_get_id(contact), looking_for) ? 0 : CMP_MATCH;
213 * \brief For an endpoint try to match the given contact sorcery id.
215 static int on_endpoint(void *obj, void *arg, int flags)
217 struct ast_sip_endpoint *endpoint = obj;
218 struct ast_sip_contact *contact;
219 char *looking_for = arg;
223 if (!arg || ast_strlen_zero(endpoint->aors)) {
227 aors = ast_strdupa(endpoint->aors);
228 while ((aor_name = strsep(&aors, ","))) {
229 struct ast_sip_aor *aor;
230 struct ao2_container *contacts;
232 aor = ast_sip_location_retrieve_aor(aor_name);
237 contacts = ast_sip_location_retrieve_aor_contacts(aor);
243 contact = ao2_callback(contacts, 0, match_contact_id, looking_for);
244 ao2_ref(contacts, -1);
246 ao2_ref(contact, -1);
256 * \brief Find an endpoint associated with the given contact.
258 static struct ast_sip_endpoint *find_an_endpoint(struct ast_sip_contact *contact)
260 char *looking_for = (char *) ast_sorcery_object_get_id(contact);
261 struct ao2_container *endpoints = ast_sip_get_endpoints();
262 struct ast_sip_endpoint *endpoint;
264 endpoint = ao2_callback(endpoints, 0, on_endpoint, looking_for);
265 ao2_ref(endpoints, -1);
271 * \brief Receive a response to the qualify contact request.
273 static void qualify_contact_cb(void *token, pjsip_event *e)
275 struct ast_sip_contact *contact = token;
277 switch(e->body.tsx_state.type) {
279 ast_log(LOG_ERROR, "Unexpected PJSIP event %u\n", e->body.tsx_state.type);
281 case PJSIP_EVENT_TRANSPORT_ERROR:
282 case PJSIP_EVENT_TIMER:
283 update_contact_status(contact, UNAVAILABLE);
285 case PJSIP_EVENT_RX_MSG:
286 update_contact_status(contact, AVAILABLE);
289 ao2_cleanup(contact);
294 * \brief Attempt to qualify the contact
296 * \details Sends a SIP OPTIONS request to the given contact in order to make
297 * sure that contact is available.
299 static int qualify_contact(struct ast_sip_endpoint *endpoint, struct ast_sip_contact *contact)
301 pjsip_tx_data *tdata;
302 RAII_VAR(struct ast_sip_endpoint *, endpoint_local, NULL, ao2_cleanup);
304 if (contact->authenticate_qualify) {
305 endpoint_local = ao2_bump(endpoint);
306 if (!endpoint_local) {
308 * Find the "first" endpoint to completely qualify the contact - any
309 * endpoint that is associated with the contact should do.
311 endpoint_local = find_an_endpoint(contact);
312 if (!endpoint_local) {
313 ast_log(LOG_ERROR, "Unable to find an endpoint to qualify contact %s\n",
320 if (ast_sip_create_request("OPTIONS", NULL, NULL, NULL, contact, &tdata)) {
321 ast_log(LOG_ERROR, "Unable to create request to qualify contact %s\n",
326 /* If an outbound proxy is specified set it on this request */
327 if (!ast_strlen_zero(contact->outbound_proxy) &&
328 ast_sip_set_outbound_proxy(tdata, contact->outbound_proxy)) {
329 pjsip_tx_data_dec_ref(tdata);
330 ast_log(LOG_ERROR, "Unable to apply outbound proxy on request to qualify contact %s\n",
335 init_start_time(contact);
337 ao2_ref(contact, +1);
338 if (ast_sip_send_out_of_dialog_request(tdata, endpoint_local, (int)(contact->qualify_timeout * 1000), contact, qualify_contact_cb)
340 ast_log(LOG_ERROR, "Unable to send request to qualify contact %s\n",
342 update_contact_status(contact, UNAVAILABLE);
343 ao2_ref(contact, -1);
352 * \brief Scheduling context for sending QUALIFY request at specified intervals.
354 static struct ast_sched_context *sched;
358 * \brief Container to hold all actively scheduled qualifies.
360 static struct ao2_container *sched_qualifies;
364 * \brief Structure to hold qualify contact scheduling information.
367 /*! The scheduling id */
369 /*! The the contact being checked */
370 struct ast_sip_contact *contact;
375 * \brief Destroy the scheduled data and remove from scheduler.
377 static void sched_data_destructor(void *obj)
379 struct sched_data *data = obj;
381 ao2_cleanup(data->contact);
385 * \brief Create the scheduling data object.
387 static struct sched_data *sched_data_create(struct ast_sip_contact *contact)
389 struct sched_data *data;
391 data = ao2_t_alloc(sizeof(*data), sched_data_destructor, contact->uri);
393 ast_log(LOG_ERROR, "Unable to create schedule qualify data for contact %s\n",
398 data->contact = contact;
399 ao2_ref(data->contact, +1);
406 * \brief Send a qualify contact request within a threaded task.
408 static int qualify_contact_task(void *obj)
410 struct ast_sip_contact *contact = obj;
413 res = qualify_contact(NULL, contact);
414 ao2_ref(contact, -1);
420 * \brief Send a scheduled qualify contact request.
422 static int qualify_contact_sched(const void *obj)
424 struct sched_data *data = (struct sched_data *) obj;
426 ao2_ref(data->contact, +1);
427 if (ast_sip_push_task(NULL, qualify_contact_task, data->contact)) {
428 ao2_ref(data->contact, -1);
432 * Always reschedule rather than have a potential race cleaning
433 * up the data object ref between self deletion and an external
436 return data->contact->qualify_frequency * 1000;
441 * \brief Set up a scheduled qualify contact check.
443 static void schedule_qualify(struct ast_sip_contact *contact, int initial_interval)
445 struct sched_data *data;
447 data = sched_data_create(contact);
452 ast_assert(contact->qualify_frequency != 0);
454 ao2_t_ref(data, +1, "Ref for qualify_contact_sched() scheduler entry");
455 data->id = ast_sched_add_variable(sched, initial_interval,
456 qualify_contact_sched, data, 1);
458 ao2_t_ref(data, -1, "Cleanup failed scheduler add");
459 ast_log(LOG_ERROR, "Unable to schedule qualify for contact %s\n",
461 } else if (!ao2_link(sched_qualifies, data)) {
462 AST_SCHED_DEL_UNREF(sched, data->id,
463 ao2_t_ref(data, -1, "Cleanup scheduler for failed ao2_link"));
465 ao2_t_ref(data, -1, "Done setting up scheduler entry");
470 * \brief Remove the contact from the scheduler.
472 static void unschedule_qualify(struct ast_sip_contact *contact)
474 struct sched_data *data;
476 data = ao2_find(sched_qualifies, contact, OBJ_UNLINK | OBJ_SEARCH_KEY);
481 AST_SCHED_DEL_UNREF(sched, data->id,
482 ao2_t_ref(data, -1, "Delete scheduler entry ref"));
483 ao2_t_ref(data, -1, "Done with ao2_find ref");
488 * \brief Qualify the given contact and set up scheduling if configured.
490 static void qualify_and_schedule(struct ast_sip_contact *contact)
492 unschedule_qualify(contact);
494 if (contact->qualify_frequency) {
495 ao2_ref(contact, +1);
496 if (ast_sip_push_task(NULL, qualify_contact_task, contact)) {
497 ao2_ref(contact, -1);
500 schedule_qualify(contact, contact->qualify_frequency * 1000);
502 delete_contact_status(contact);
508 * \brief A new contact has been created make sure it is available.
510 static void contact_created(const void *obj)
512 qualify_and_schedule((struct ast_sip_contact *) obj);
517 * \brief A contact has been deleted remove status tracking.
519 static void contact_deleted(const void *obj)
521 struct ast_sip_contact *contact = (struct ast_sip_contact *) obj;
522 struct ast_sip_contact_status *status;
524 unschedule_qualify(contact);
526 status = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), CONTACT_STATUS,
527 ast_sorcery_object_get_id(contact));
532 if (ast_sorcery_delete(ast_sip_get_sorcery(), status)) {
533 ast_log(LOG_ERROR, "Unable to delete ast_sip_contact_status for contact %s\n",
539 static const struct ast_sorcery_observer contact_observer = {
540 .created = contact_created,
541 .deleted = contact_deleted
544 static pj_bool_t options_start(void)
546 sched = ast_sched_context_create();
550 if (ast_sched_start_thread(sched)) {
551 ast_sched_context_destroy(sched);
556 if (ast_sorcery_observer_add(ast_sip_get_sorcery(), "contact", &contact_observer)) {
557 ast_log(LOG_WARNING, "Unable to add contact observer\n");
558 ast_sched_context_destroy(sched);
566 static int sched_qualifies_empty(void *obj, void *arg, int flags)
568 ao2_t_ref(obj, -1, "Release ref held by destroyed scheduler context.");
572 static pj_bool_t options_stop(void)
574 ast_sorcery_observer_remove(ast_sip_get_sorcery(), "contact", &contact_observer);
577 ast_sched_context_destroy(sched);
581 /* Empty the container of scheduling data refs. */
582 ao2_callback(sched_qualifies, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE,
583 sched_qualifies_empty, NULL);
588 static pj_status_t send_options_response(pjsip_rx_data *rdata, int code)
590 pjsip_endpoint *endpt = ast_sip_get_pjsip_endpoint();
591 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
592 pjsip_transaction *trans = pjsip_rdata_get_tsx(rdata);
593 pjsip_tx_data *tdata;
594 const pjsip_hdr *hdr;
597 /* Make the response object */
598 if ((status = ast_sip_create_response(rdata, code, NULL, &tdata) != PJ_SUCCESS)) {
599 ast_log(LOG_ERROR, "Unable to create response (%d)\n", status);
603 /* Add appropriate headers */
604 if ((hdr = pjsip_endpt_get_capability(endpt, PJSIP_H_ACCEPT, NULL))) {
605 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, hdr));
607 if ((hdr = pjsip_endpt_get_capability(endpt, PJSIP_H_ALLOW, NULL))) {
608 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, hdr));
610 if ((hdr = pjsip_endpt_get_capability(endpt, PJSIP_H_SUPPORTED, NULL))) {
611 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, hdr));
615 * XXX TODO: pjsip doesn't care a lot about either of these headers -
616 * while it provides specific methods to create them, they are defined
617 * to be the standard string header creation. We never did add them
618 * in chan_sip, although RFC 3261 says they SHOULD. Hard coded here.
620 ast_sip_add_header(tdata, "Accept-Encoding", DEFAULT_ENCODING);
621 ast_sip_add_header(tdata, "Accept-Language", DEFAULT_LANGUAGE);
624 status = pjsip_dlg_send_response(dlg, trans, tdata);
626 struct ast_sip_endpoint *endpoint;
628 endpoint = ast_pjsip_rdata_get_endpoint(rdata);
629 status = ast_sip_send_stateful_response(rdata, tdata, endpoint);
630 ao2_cleanup(endpoint);
633 if (status != PJ_SUCCESS) {
634 ast_log(LOG_ERROR, "Unable to send response (%d)\n", status);
640 static pj_bool_t options_on_rx_request(pjsip_rx_data *rdata)
642 RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
644 pjsip_sip_uri *sip_ruri;
645 char exten[AST_MAX_EXTENSION];
647 if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
648 &pjsip_options_method)) {
652 if (!(endpoint = ast_pjsip_rdata_get_endpoint(rdata))) {
656 ruri = rdata->msg_info.msg->line.req.uri;
657 if (!PJSIP_URI_SCHEME_IS_SIP(ruri) && !PJSIP_URI_SCHEME_IS_SIPS(ruri)) {
658 send_options_response(rdata, 416);
662 sip_ruri = pjsip_uri_get_uri(ruri);
663 ast_copy_pj_str(exten, &sip_ruri->user, sizeof(exten));
665 if (ast_shutting_down()) {
667 * Not taking any new calls at this time.
668 * Likely a server availability OPTIONS poll.
670 send_options_response(rdata, 503);
671 } else if (!ast_strlen_zero(exten) && !ast_exists_extension(NULL, endpoint->context, exten, 1, NULL)) {
672 send_options_response(rdata, 404);
674 send_options_response(rdata, 200);
679 static pjsip_module options_module = {
680 .name = {"Options Module", 14},
682 .priority = PJSIP_MOD_PRIORITY_APPLICATION,
683 .start = options_start,
684 .stop = options_stop,
685 .on_rx_request = options_on_rx_request,
690 * \brief Send qualify request to the given contact.
692 static int cli_on_contact(void *obj, void *arg, void *data, int flags)
694 struct ast_sip_contact *contact = obj;
695 struct ast_sip_endpoint *endpoint = data;
698 ast_cli(*cli_fd, " contact %s\n", contact->uri);
699 qualify_contact(endpoint, contact);
704 * \brief Data pushed to threadpool to qualify endpoints from the CLI
706 struct qualify_data {
707 /*! Endpoint that is being qualified */
708 struct ast_sip_endpoint *endpoint;
709 /*! CLI File descriptor for printing messages */
713 static struct qualify_data *qualify_data_alloc(struct ast_sip_endpoint *endpoint, int cli_fd)
715 struct qualify_data *qual_data;
717 qual_data = ast_malloc(sizeof(*qual_data));
722 qual_data->endpoint = ao2_bump(endpoint);
723 qual_data->cli_fd = cli_fd;
727 static void qualify_data_destroy(struct qualify_data *qual_data)
729 ao2_cleanup(qual_data->endpoint);
735 * \brief For an endpoint iterate over and qualify all aors/contacts
737 static int cli_qualify_contacts(void *data)
741 RAII_VAR(struct qualify_data *, qual_data, data, qualify_data_destroy);
742 struct ast_sip_endpoint *endpoint = qual_data->endpoint;
743 int cli_fd = qual_data->cli_fd;
744 const char *endpoint_name = ast_sorcery_object_get_id(endpoint);
746 if (ast_strlen_zero(endpoint->aors)) {
747 ast_cli(cli_fd, "Endpoint %s has no AoR's configured\n",
752 aors = ast_strdupa(endpoint->aors);
753 while ((aor_name = strsep(&aors, ","))) {
754 struct ast_sip_aor *aor;
755 struct ao2_container *contacts;
757 aor = ast_sip_location_retrieve_aor(aor_name);
762 contacts = ast_sip_location_retrieve_aor_contacts(aor);
764 ast_cli(cli_fd, "Sending qualify to endpoint %s\n", endpoint_name);
765 ao2_callback_data(contacts, OBJ_NODATA, cli_on_contact, &cli_fd, endpoint);
766 ao2_ref(contacts, -1);
774 static char *cli_qualify(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
776 RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
777 const char *endpoint_name;
778 struct qualify_data *qual_data;
782 e->command = "pjsip qualify";
784 "Usage: pjsip qualify <endpoint>\n"
785 " Send a SIP OPTIONS request to all contacts on the endpoint.\n";
792 return CLI_SHOWUSAGE;
795 endpoint_name = a->argv[2];
797 if (!(endpoint = ast_sorcery_retrieve_by_id(
798 ast_sip_get_sorcery(), "endpoint", endpoint_name))) {
799 ast_cli(a->fd, "Unable to retrieve endpoint %s\n", endpoint_name);
803 qual_data = qualify_data_alloc(endpoint, a->fd);
808 if (ast_sip_push_task(NULL, cli_qualify_contacts, qual_data)) {
809 qualify_data_destroy(qual_data);
818 * \brief Send qualify request to the given contact.
820 static int ami_contact_cb(void *obj, void *arg, int flags)
822 struct ast_sip_contact *contact = obj;
824 ao2_ref(contact, +1);
825 if (ast_sip_push_task(NULL, qualify_contact_task, contact)) {
826 ao2_ref(contact, -1);
831 static int ami_sip_qualify(struct mansession *s, const struct message *m)
833 const char *endpoint_name = astman_get_header(m, "Endpoint");
834 RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
838 if (ast_strlen_zero(endpoint_name)) {
839 astman_send_error(s, m, "Endpoint parameter missing.");
843 endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint",
846 astman_send_error(s, m, "Unable to retrieve endpoint\n");
850 /* send a qualify for all contacts registered with the endpoint */
851 if (ast_strlen_zero(endpoint->aors)) {
852 astman_send_error(s, m, "No AoRs configured for endpoint\n");
856 aors = ast_strdupa(endpoint->aors);
857 while ((aor_name = strsep(&aors, ","))) {
858 struct ast_sip_aor *aor;
859 struct ao2_container *contacts;
861 aor = ast_sip_location_retrieve_aor(aor_name);
866 contacts = ast_sip_location_retrieve_aor_contacts(aor);
868 ao2_callback(contacts, OBJ_NODATA, ami_contact_cb, NULL);
869 ao2_ref(contacts, -1);
875 astman_send_ack(s, m, "Endpoint found, will qualify");
879 static struct ast_cli_entry cli_options[] = {
880 AST_CLI_DEFINE(cli_qualify, "Send an OPTIONS request to a PJSIP endpoint")
883 static int sched_qualifies_hash_fn(const void *obj, int flags)
885 const struct sched_data *object;
886 const struct ast_sip_contact *key;
888 switch (flags & OBJ_SEARCH_MASK) {
892 case OBJ_SEARCH_OBJECT:
894 key = object->contact;
897 /* Hash can only work on something with a full key. */
901 return ast_str_hash(ast_sorcery_object_get_id(key));
904 static int sched_qualifies_cmp_fn(void *obj, void *arg, int flags)
906 const struct sched_data *object_left = obj;
907 const struct sched_data *object_right = arg;
908 struct ast_sip_contact *right_key = arg;
911 switch (flags & OBJ_SEARCH_MASK) {
912 case OBJ_SEARCH_OBJECT:
913 right_key = object_right->contact;
916 cmp = strcmp(ast_sorcery_object_get_id(object_left->contact),
917 ast_sorcery_object_get_id(right_key));
919 case OBJ_SEARCH_PARTIAL_KEY:
920 /* Not supported by container. */
925 * What arg points to is specific to this traversal callback
926 * and has no special meaning to astobj2.
935 * At this point the traversal callback is identical to a sorted
941 static int rtt_start_handler(const struct aco_option *opt,
942 struct ast_variable *var, void *obj)
944 struct ast_sip_contact_status *status = obj;
947 if (sscanf(var->value, "%ld.%06ld", &sec, &usec) != 2) {
951 status->rtt_start = ast_tv(sec, usec);
956 static int rtt_start_to_str(const void *obj, const intptr_t *args, char **buf)
958 const struct ast_sip_contact_status *status = obj;
960 if (ast_asprintf(buf, "%ld.%06ld", status->rtt_start.tv_sec, status->rtt_start.tv_usec) == -1) {
967 int ast_sip_initialize_sorcery_qualify(void)
969 struct ast_sorcery *sorcery = ast_sip_get_sorcery();
971 /* initialize sorcery ast_sip_contact_status resource */
972 ast_sorcery_apply_default(sorcery, CONTACT_STATUS, "memory", NULL);
974 if (ast_sorcery_internal_object_register(sorcery, CONTACT_STATUS,
975 contact_status_alloc, NULL, NULL)) {
976 ast_log(LOG_ERROR, "Unable to register ast_sip_contact_status in sorcery\n");
980 ast_sorcery_object_field_register_nodoc(sorcery, CONTACT_STATUS, "last_status",
981 "0", OPT_UINT_T, 1, FLDSET(struct ast_sip_contact_status, last_status));
982 ast_sorcery_object_field_register_nodoc(sorcery, CONTACT_STATUS, "status",
983 "0", OPT_UINT_T, 1, FLDSET(struct ast_sip_contact_status, status));
984 ast_sorcery_object_field_register_custom_nodoc(sorcery, CONTACT_STATUS, "rtt_start",
985 "0.0", rtt_start_handler, rtt_start_to_str, NULL, 0, 0);
986 ast_sorcery_object_field_register_nodoc(sorcery, CONTACT_STATUS, "rtt",
987 "0", OPT_UINT_T, 1, FLDSET(struct ast_sip_contact_status, rtt));
992 static int qualify_and_schedule_cb(void *obj, void *arg, int flags)
994 struct ast_sip_contact *contact = obj;
995 struct ast_sip_aor *aor = arg;
996 int initial_interval;
997 int max_time = ast_sip_get_max_initial_qualify_time();
999 contact->qualify_frequency = aor->qualify_frequency;
1000 contact->qualify_timeout = aor->qualify_timeout;
1001 contact->authenticate_qualify = aor->authenticate_qualify;
1003 /* Delay initial qualification by a random fraction of the specified interval */
1004 if (max_time && max_time < contact->qualify_frequency) {
1005 initial_interval = max_time;
1007 initial_interval = contact->qualify_frequency;
1010 initial_interval = (int)((initial_interval * 1000) * ast_random_double());
1012 if (contact->qualify_frequency) {
1013 schedule_qualify(contact, initial_interval);
1021 * \brief Qualify and schedule an endpoint's contacts
1023 * \details For the given endpoint retrieve its list of aors, qualify all
1024 * contacts, and schedule for checks if configured.
1026 static int qualify_and_schedule_all_cb(void *obj, void *arg, int flags)
1028 struct ast_sip_endpoint *endpoint = obj;
1032 if (ast_strlen_zero(endpoint->aors)) {
1036 aors = ast_strdupa(endpoint->aors);
1037 while ((aor_name = strsep(&aors, ","))) {
1038 struct ast_sip_aor *aor;
1039 struct ao2_container *contacts;
1041 aor = ast_sip_location_retrieve_aor(aor_name);
1046 contacts = ast_sip_location_retrieve_aor_contacts(aor);
1048 ao2_callback(contacts, OBJ_NODATA, qualify_and_schedule_cb, aor);
1049 ao2_ref(contacts, -1);
1060 * \brief Unschedule all existing contacts
1062 static int unschedule_all_cb(void *obj, void *arg, int flags)
1064 struct sched_data *data = obj;
1066 AST_SCHED_DEL_UNREF(sched, data->id, ao2_ref(data, -1));
1071 static void qualify_and_schedule_all(void)
1073 struct ao2_container *endpoints = ast_sip_get_endpoints();
1075 ao2_callback(sched_qualifies, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, unschedule_all_cb, NULL);
1081 ao2_callback(endpoints, OBJ_NODATA, qualify_and_schedule_all_cb, NULL);
1082 ao2_ref(endpoints, -1);
1085 static const char *status_map [] = {
1086 [UNAVAILABLE] = "Unreachable",
1087 [AVAILABLE] = "Reachable",
1090 static int format_contact_status(void *obj, void *arg, int flags)
1092 struct ast_sip_contact_wrapper *wrapper = obj;
1093 struct ast_sip_contact *contact = wrapper->contact;
1094 struct ast_sip_ami *ami = arg;
1095 struct ast_sip_contact_status *status;
1096 struct ast_str *buf;
1097 const struct ast_sip_endpoint *endpoint = ami->arg;
1099 buf = ast_sip_create_ami_event("ContactStatusDetail", ami);
1104 status = ast_sorcery_retrieve_by_id(
1105 ast_sip_get_sorcery(), CONTACT_STATUS,
1106 ast_sorcery_object_get_id(contact));
1108 ast_str_append(&buf, 0, "AOR: %s\r\n", wrapper->aor_id);
1109 ast_str_append(&buf, 0, "URI: %s\r\n", contact->uri);
1111 ast_str_append(&buf, 0, "Status: %s\r\n", status_map[status->status]);
1112 ast_str_append(&buf, 0, "RoundtripUsec: %" PRId64 "\r\n", status->rtt);
1114 ast_str_append(&buf, 0, "Status: Unknown\r\n");
1115 ast_str_append(&buf, 0, "RoundtripUsec: N/A\r\n");
1117 ast_str_append(&buf, 0, "EndpointName: %s\r\n",
1118 ast_sorcery_object_get_id(endpoint));
1119 astman_append(ami->s, "%s\r\n", ast_str_buffer(buf));
1123 ao2_cleanup(status);
1127 static int format_contact_status_for_aor(void *obj, void *arg, int flags)
1129 struct ast_sip_aor *aor = obj;
1131 return ast_sip_for_each_contact(aor, format_contact_status, arg);
1134 static int format_ami_contact_status(const struct ast_sip_endpoint *endpoint,
1135 struct ast_sip_ami *ami)
1137 ami->arg = (void *)endpoint;
1138 return ast_sip_for_each_aor(endpoint->aors, format_contact_status_for_aor, ami);
1141 static struct ast_sip_endpoint_formatter contact_status_formatter = {
1142 .format_ami = format_ami_contact_status
1145 int ast_res_pjsip_init_options_handling(int reload)
1147 static const pj_str_t STR_OPTIONS = { "OPTIONS", 7 };
1150 qualify_and_schedule_all();
1154 sched_qualifies = ao2_t_container_alloc(QUALIFIED_BUCKETS,
1155 sched_qualifies_hash_fn, sched_qualifies_cmp_fn,
1156 "Create container for scheduled qualifies");
1157 if (!sched_qualifies) {
1161 if (pjsip_endpt_register_module(ast_sip_get_pjsip_endpoint(), &options_module) != PJ_SUCCESS) {
1162 ao2_cleanup(sched_qualifies);
1163 sched_qualifies = NULL;
1167 if (pjsip_endpt_add_capability(ast_sip_get_pjsip_endpoint(), NULL, PJSIP_H_ALLOW,
1168 NULL, 1, &STR_OPTIONS) != PJ_SUCCESS) {
1169 pjsip_endpt_unregister_module(ast_sip_get_pjsip_endpoint(), &options_module);
1170 ao2_cleanup(sched_qualifies);
1171 sched_qualifies = NULL;
1175 internal_sip_register_endpoint_formatter(&contact_status_formatter);
1176 ast_manager_register2("PJSIPQualify", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, ami_sip_qualify, NULL, NULL, NULL);
1177 ast_cli_register_multiple(cli_options, ARRAY_LEN(cli_options));
1179 qualify_and_schedule_all();
1184 void ast_res_pjsip_cleanup_options_handling(void)
1186 ast_cli_unregister_multiple(cli_options, ARRAY_LEN(cli_options));
1187 ast_manager_unregister("PJSIPQualify");
1188 internal_sip_unregister_endpoint_formatter(&contact_status_formatter);
1190 pjsip_endpt_unregister_module(ast_sip_get_pjsip_endpoint(), &options_module);
1191 ao2_cleanup(sched_qualifies);
1192 sched_qualifies = NULL;