diff --git a/res/res_pjsip_outbound_registration.c b/res/res_pjsip_outbound_registration.c index af4a3694db..552d61e285 100644 --- a/res/res_pjsip_outbound_registration.c +++ b/res/res_pjsip_outbound_registration.c @@ -166,6 +166,9 @@ /*! \brief Amount of buffer time (in seconds) before expiration that we re-register at */ #define REREGISTER_BUFFER_TIME 10 +/*! \brief Size of the buffer for creating a unique string for the line */ +#define LINE_PARAMETER_SIZE 8 + /*! \brief Various states that an outbound registration may be in */ enum sip_outbound_registration_status { /*! \brief Currently unregistered */ @@ -188,6 +191,43 @@ static const char *sip_outbound_registration_status_str[] = { [SIP_REGISTRATION_STOPPED] = "Stopped", }; +/*! \brief Outbound registration information */ +struct sip_outbound_registration { + /*! \brief Sorcery object details */ + SORCERY_OBJECT(details); + /*! \brief Stringfields */ + AST_DECLARE_STRING_FIELDS( + /*! \brief URI for the registrar */ + AST_STRING_FIELD(server_uri); + /*! \brief URI for the AOR */ + AST_STRING_FIELD(client_uri); + /*! \brief Optional user for contact header */ + AST_STRING_FIELD(contact_user); + /*! \brief Explicit transport to use for registration */ + AST_STRING_FIELD(transport); + /*! \brief Outbound proxy to use */ + AST_STRING_FIELD(outbound_proxy); + /*! \brief Endpoint to use for related incoming calls */ + AST_STRING_FIELD(endpoint); + ); + /*! \brief Requested expiration time */ + unsigned int expiration; + /*! \brief Interval at which retries should occur for temporal responses */ + unsigned int retry_interval; + /*! \brief Interval at which retries should occur for permanent responses */ + unsigned int forbidden_retry_interval; + /*! \brief Treat authentication challenges that we cannot handle as permanent failures */ + unsigned int auth_rejection_permanent; + /*! \brief Maximum number of retries permitted */ + unsigned int max_retries; + /*! \brief Whether to add a line parameter to the outbound Contact or not */ + unsigned int line; + /*! \brief Configured authentication credentials */ + struct ast_sip_auth_vector outbound_auths; + /*! \brief Whether Path support is enabled */ + unsigned int support_path; +}; + /*! \brief Outbound registration client state information (persists for lifetime of regc) */ struct sip_outbound_registration_client_state { /*! \brief Current status of this registration */ @@ -196,6 +236,8 @@ struct sip_outbound_registration_client_state { pjsip_regc *client; /*! \brief Timer entry for retrying on temporal responses */ pj_timer_entry timer; + /*! \brief Optional line parameter placed into Contact */ + char line[LINE_PARAMETER_SIZE]; /*! \brief Current number of retries */ unsigned int retries; /*! \brief Maximum number of retries permitted */ @@ -218,44 +260,106 @@ struct sip_outbound_registration_client_state { /*! \brief Outbound registration state information (persists for lifetime that registration should exist) */ struct sip_outbound_registration_state { + /*! \brief Outbound registration configuration object */ + struct sip_outbound_registration *registration; /*! \brief Client state information */ struct sip_outbound_registration_client_state *client_state; }; -/*! \brief Outbound registration information */ -struct sip_outbound_registration { - /*! \brief Sorcery object details */ - SORCERY_OBJECT(details); - /*! \brief Stringfields */ - AST_DECLARE_STRING_FIELDS( - /*! \brief URI for the registrar */ - AST_STRING_FIELD(server_uri); - /*! \brief URI for the AOR */ - AST_STRING_FIELD(client_uri); - /*! \brief Optional user for contact header */ - AST_STRING_FIELD(contact_user); - /*! \brief Explicit transport to use for registration */ - AST_STRING_FIELD(transport); - /*! \brief Outbound proxy to use */ - AST_STRING_FIELD(outbound_proxy); - ); - /*! \brief Requested expiration time */ - unsigned int expiration; - /*! \brief Interval at which retries should occur for temporal responses */ - unsigned int retry_interval; - /*! \brief Interval at which retries should occur for permanent responses */ - unsigned int forbidden_retry_interval; - /*! \brief Treat authentication challenges that we cannot handle as permanent failures */ - unsigned int auth_rejection_permanent; - /*! \brief Maximum number of retries permitted */ - unsigned int max_retries; - /*! \brief Outbound registration state */ - struct sip_outbound_registration_state *state; - /*! \brief Configured authentication credentials */ - struct ast_sip_auth_vector outbound_auths; - /*! \brief Whether Path support is enabled */ - unsigned int support_path; -}; +/*! \brief Default number of state container buckets */ +#define DEFAULT_STATE_BUCKETS 53 +static AO2_GLOBAL_OBJ_STATIC(current_states); + +/*! \brief hashing function for state objects */ +static int registration_state_hash(const void *obj, const int flags) +{ + const struct sip_outbound_registration_state *object; + const char *key; + + switch (flags & OBJ_SEARCH_MASK) { + case OBJ_SEARCH_KEY: + key = obj; + break; + case OBJ_SEARCH_OBJECT: + object = obj; + key = ast_sorcery_object_get_id(object->registration); + break; + default: + ast_assert(0); + return 0; + } + return ast_str_hash(key); +} + +/*! \brief comparator function for state objects */ +static int registration_state_cmp(void *obj, void *arg, int flags) +{ + const struct sip_outbound_registration_state *object_left = obj; + const struct sip_outbound_registration_state *object_right = arg; + const char *right_key = arg; + int cmp; + + switch (flags & OBJ_SEARCH_MASK) { + case OBJ_SEARCH_OBJECT: + right_key = ast_sorcery_object_get_id(object_right->registration); + /* Fall through */ + case OBJ_SEARCH_KEY: + cmp = strcmp(ast_sorcery_object_get_id(object_left->registration), right_key); + break; + case OBJ_SEARCH_PARTIAL_KEY: + /* Not supported by container. */ + ast_assert(0); + return 0; + default: + cmp = 0; + break; + } + if (cmp) { + return 0; + } + return CMP_MATCH; +} + +static struct sip_outbound_registration_state *get_state(const char *id) +{ + RAII_VAR(struct ao2_container *, states, + ao2_global_obj_ref(current_states), ao2_cleanup); + return states ? ao2_find(states, id, OBJ_SEARCH_KEY) : NULL; +} + +static int registration_state_add(void *obj, void *arg, int flags) +{ + struct sip_outbound_registration_state *state = + get_state(ast_sorcery_object_get_id(obj)); + + if (state) { + ao2_link(arg, state); + ao2_ref(state, -1); + } + + return 0; +} + +static struct ao2_container *get_registrations(void) +{ + RAII_VAR(struct ao2_container *, new_states, NULL, ao2_cleanup); + struct ao2_container *registrations = ast_sorcery_retrieve_by_fields( + ast_sip_get_sorcery(), "registration", + AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL); + + if (!(new_states = ao2_container_alloc(DEFAULT_STATE_BUCKETS, + registration_state_hash, registration_state_cmp))) { + ast_log(LOG_ERROR, "Unable to allocate registration states container\n"); + return NULL; + } + + if (registrations && ao2_container_count(registrations)) { + ao2_callback(registrations, OBJ_NODATA, registration_state_add, new_states); + } + + ao2_global_obj_replace_unref(current_states, new_states); + return registrations; +} /*! \brief Helper function which cancels the timer on a client */ static void cancel_registration(struct sip_outbound_registration_client_state *client_state) @@ -560,6 +664,8 @@ static void sip_outbound_registration_state_destroy(void *obj) { struct sip_outbound_registration_state *state = obj; + ao2_cleanup(state->registration); + if (!state->client_state) { return; } @@ -579,7 +685,7 @@ static void sip_outbound_registration_client_state_destroy(void *obj) } /*! \brief Allocator function for registration state */ -static struct sip_outbound_registration_state *sip_outbound_registration_state_alloc(void) +static struct sip_outbound_registration_state *sip_outbound_registration_state_alloc(struct sip_outbound_registration *registration) { struct sip_outbound_registration_state *state = ao2_alloc(sizeof(*state), sip_outbound_registration_state_destroy); @@ -598,6 +704,7 @@ static struct sip_outbound_registration_state *sip_outbound_registration_state_a state->client_state->timer.user_data = state->client_state; state->client_state->timer.cb = sip_outbound_registration_timer_cb; + state->registration = ao2_bump(registration); return state; } @@ -606,7 +713,6 @@ static void sip_outbound_registration_destroy(void *obj) { struct sip_outbound_registration *registration = obj; - ao2_cleanup(registration->state); ast_sip_auth_vector_destroy(®istration->outbound_auths); ast_string_field_free_memory(registration); @@ -616,7 +722,6 @@ static void sip_outbound_registration_destroy(void *obj) static void *sip_outbound_registration_alloc(const char *name) { struct sip_outbound_registration *registration = ast_sorcery_generic_alloc(sizeof(*registration), sip_outbound_registration_destroy); - if (!registration || ast_string_field_init(registration, 256)) { ao2_cleanup(registration); return NULL; @@ -718,7 +823,9 @@ static int can_reuse_registration(struct sip_outbound_registration *existing, st /*! \brief Helper function that allocates a pjsip registration client and configures it */ static int sip_outbound_registration_regc_alloc(void *data) { - struct sip_outbound_registration *registration = data; + struct sip_outbound_registration_state *state = data; + RAII_VAR(struct sip_outbound_registration *, registration, + ao2_bump(state->registration), ao2_cleanup); pj_pool_t *pool; pj_str_t tmp; pjsip_uri *uri; @@ -772,13 +879,13 @@ static int sip_outbound_registration_regc_alloc(void *data) } } - if (!registration->state->client_state->client && - pjsip_regc_create(ast_sip_get_pjsip_endpoint(), registration->state->client_state, sip_outbound_registration_response_cb, - ®istration->state->client_state->client) != PJ_SUCCESS) { + if (!state->client_state->client && + pjsip_regc_create(ast_sip_get_pjsip_endpoint(), state->client_state, sip_outbound_registration_response_cb, + &state->client_state->client) != PJ_SUCCESS) { return -1; } - pjsip_regc_set_transport(registration->state->client_state->client, &selector); + pjsip_regc_set_transport(state->client_state->client, &selector); if (!ast_strlen_zero(registration->outbound_proxy)) { pjsip_route_hdr route_set, *route; @@ -787,34 +894,65 @@ static int sip_outbound_registration_regc_alloc(void *data) pj_list_init(&route_set); - pj_strdup2_with_null(pjsip_regc_get_pool(registration->state->client_state->client), &tmp, registration->outbound_proxy); - if (!(route = pjsip_parse_hdr(pjsip_regc_get_pool(registration->state->client_state->client), &ROUTE_HNAME, tmp.ptr, tmp.slen, NULL))) { + pj_strdup2_with_null(pjsip_regc_get_pool(state->client_state->client), &tmp, registration->outbound_proxy); + if (!(route = pjsip_parse_hdr(pjsip_regc_get_pool(state->client_state->client), &ROUTE_HNAME, tmp.ptr, tmp.slen, NULL))) { return -1; } pj_list_insert_nodes_before(&route_set, route); - pjsip_regc_set_route_set(registration->state->client_state->client, &route_set); + pjsip_regc_set_route_set(state->client_state->client, &route_set); } pj_cstr(&server_uri, registration->server_uri); - if (sip_dialog_create_contact(pjsip_regc_get_pool(registration->state->client_state->client), &contact_uri, S_OR(registration->contact_user, "s"), &server_uri, &selector)) { + if (sip_dialog_create_contact(pjsip_regc_get_pool(state->client_state->client), &contact_uri, S_OR(registration->contact_user, "s"), &server_uri, &selector)) { return -1; } pj_cstr(&client_uri, registration->client_uri); - - if (pjsip_regc_init(registration->state->client_state->client, &server_uri, &client_uri, &client_uri, 1, &contact_uri, registration->expiration) != PJ_SUCCESS) { + if (pjsip_regc_init(state->client_state->client, &server_uri, &client_uri, &client_uri, 1, &contact_uri, registration->expiration) != PJ_SUCCESS) { return -1; } return 0; } +/*! \brief Helper function which performs a single registration */ +static int sip_outbound_registration_perform(void *data) +{ + RAII_VAR(struct sip_outbound_registration_state *, state, data, ao2_cleanup); + RAII_VAR(struct sip_outbound_registration *, registration, ao2_bump(state->registration), ao2_cleanup); + size_t i; + + /* Just in case the client state is being reused for this registration, free the auth information */ + ast_sip_auth_vector_destroy(&state->client_state->outbound_auths); + + AST_VECTOR_INIT(&state->client_state->outbound_auths, AST_VECTOR_SIZE(®istration->outbound_auths)); + for (i = 0; i < AST_VECTOR_SIZE(®istration->outbound_auths); ++i) { + const char *name = ast_strdup(AST_VECTOR_GET(®istration->outbound_auths, i)); + AST_VECTOR_APPEND(&state->client_state->outbound_auths, name); + } + state->client_state->retry_interval = registration->retry_interval; + state->client_state->forbidden_retry_interval = registration->forbidden_retry_interval; + state->client_state->max_retries = registration->max_retries; + state->client_state->retries = 0; + state->client_state->support_path = registration->support_path; + state->client_state->auth_rejection_permanent = registration->auth_rejection_permanent; + + pjsip_regc_update_expires(state->client_state->client, registration->expiration); + + schedule_registration(state->client_state, (ast_random() % 10) + 1); + + return 0; +} + /*! \brief Apply function which finds or allocates a state structure */ static int sip_outbound_registration_apply(const struct ast_sorcery *sorcery, void *obj) { - RAII_VAR(struct sip_outbound_registration *, existing, ast_sorcery_retrieve_by_id(sorcery, "registration", ast_sorcery_object_get_id(obj)), ao2_cleanup); + RAII_VAR(struct ao2_container *, states, ao2_global_obj_ref(current_states), ao2_cleanup); + RAII_VAR(struct sip_outbound_registration_state *, state, + ao2_find(states, ast_sorcery_object_get_id(obj), OBJ_SEARCH_KEY), ao2_cleanup); + RAII_VAR(struct sip_outbound_registration_state *, new_state, NULL, ao2_cleanup); struct sip_outbound_registration *applied = obj; if (ast_strlen_zero(applied->server_uri)) { @@ -827,75 +965,39 @@ static int sip_outbound_registration_apply(const struct ast_sorcery *sorcery, vo return -1; } - if (!existing) { - /* If no existing registration exists we can just start fresh easily */ - applied->state = sip_outbound_registration_state_alloc(); - } else { - /* If there is an existing registration things are more complicated, we can immediately reuse this state if most stuff remains unchanged */ - if (can_reuse_registration(existing, applied)) { - applied->state = existing->state; - ao2_ref(applied->state, +1); - return 0; - } - applied->state = sip_outbound_registration_state_alloc(); + if (state && can_reuse_registration(state->registration, applied)) { + ao2_replace(state->registration, applied); + return 0; } - if (!applied->state) { + if (!(new_state = sip_outbound_registration_state_alloc(applied))) { return -1; } - return ast_sip_push_task_synchronous(NULL, sip_outbound_registration_regc_alloc, applied); -} + if (ast_sip_push_task_synchronous(NULL, sip_outbound_registration_regc_alloc, new_state)) { + return -1; + } -/*! \brief Helper function which performs a single registration */ -static int sip_outbound_registration_perform(void *data) -{ - RAII_VAR(struct sip_outbound_registration *, registration, data, ao2_cleanup); - size_t i; + if (ast_sip_push_task(new_state->client_state->serializer, + sip_outbound_registration_perform, ao2_bump(new_state))) { + ast_log(LOG_ERROR, "Failed to perform outbound registration on '%s'\n", + ast_sorcery_object_get_id(new_state->registration)); + ao2_ref(new_state, -1); + return -1; + } - /* Just in case the client state is being reused for this registration, free the auth information */ - ast_sip_auth_vector_destroy(®istration->state->client_state->outbound_auths); + ao2_lock(states); - AST_VECTOR_INIT(®istration->state->client_state->outbound_auths, AST_VECTOR_SIZE(®istration->outbound_auths)); - for (i = 0; i < AST_VECTOR_SIZE(®istration->outbound_auths); ++i) { - const char *name = ast_strdup(AST_VECTOR_GET(®istration->outbound_auths, i)); - AST_VECTOR_APPEND(®istration->state->client_state->outbound_auths, name); + if (state) { + ao2_unlink(states, state); } - registration->state->client_state->retry_interval = registration->retry_interval; - registration->state->client_state->forbidden_retry_interval = registration->forbidden_retry_interval; - registration->state->client_state->max_retries = registration->max_retries; - registration->state->client_state->retries = 0; - registration->state->client_state->support_path = registration->support_path; - registration->state->client_state->auth_rejection_permanent = registration->auth_rejection_permanent; - - pjsip_regc_update_expires(registration->state->client_state->client, registration->expiration); - schedule_registration(registration->state->client_state, (ast_random() % 10) + 1); + ao2_link(states, new_state); + ao2_unlock(states); return 0; } -/*! \brief Helper function which performs all registrations */ -static void sip_outbound_registration_perform_all(void) -{ - RAII_VAR(struct ao2_container *, registrations, ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "registration", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL), ao2_cleanup); - struct ao2_iterator i; - struct sip_outbound_registration *registration; - - if (!registrations) { - return; - } - - i = ao2_iterator_init(registrations, 0); - while ((registration = ao2_iterator_next(&i))) { - if (ast_sip_push_task(registration->state->client_state->serializer, sip_outbound_registration_perform, registration)) { - ast_log(LOG_ERROR, "Failed to perform outbound registration on '%s'\n", ast_sorcery_object_get_id(registration)); - ao2_ref(registration, -1); - } - } - ao2_iterator_destroy(&i); -} - static int outbound_auth_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) { struct sip_outbound_registration *registration = obj; @@ -928,44 +1030,36 @@ static int outbound_auths_to_var_list(const void *obj, struct ast_variable **fie return 0; } -static struct sip_outbound_registration *retrieve_registration(const char *registration_name) -{ - return ast_sorcery_retrieve_by_id( - ast_sip_get_sorcery(), - "registration", - registration_name); -} - static int unregister_task(void *obj) { - RAII_VAR(struct sip_outbound_registration*, registration, obj, ao2_cleanup); - struct pjsip_regc *client = registration->state->client_state->client; + RAII_VAR(struct sip_outbound_registration_state*, state, obj, ao2_cleanup); + struct pjsip_regc *client = state->client_state->client; pjsip_tx_data *tdata; if (pjsip_regc_unregister(client, &tdata) != PJ_SUCCESS) { return 0; } - ao2_ref(registration->state->client_state, +1); + ao2_ref(state->client_state, +1); if (pjsip_regc_send(client, tdata) != PJ_SUCCESS) { - ao2_cleanup(registration->state->client_state); + ao2_cleanup(state->client_state); } return 0; } -static int queue_unregister(struct sip_outbound_registration *registration) +static int queue_unregister(struct sip_outbound_registration_state *state) { - ao2_ref(registration, +1); - if (ast_sip_push_task(registration->state->client_state->serializer, unregister_task, registration)) { - ao2_cleanup(registration); + ao2_ref(state, +1); + if (ast_sip_push_task(state->client_state->serializer, unregister_task, state)) { + ao2_ref(state, -1); return -1; } return 0; } static char *cli_complete_registration(const char *line, const char *word, -int pos, int state) + int pos, int state) { char *result = NULL; int wordlen; @@ -1003,7 +1097,7 @@ int pos, int state) static char *cli_unregister(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { - RAII_VAR(struct sip_outbound_registration *, registration, NULL, ao2_cleanup); + RAII_VAR(struct sip_outbound_registration_state *, state, NULL, ao2_cleanup); const char *registration_name; switch (cmd) { @@ -1027,13 +1121,13 @@ static char *cli_unregister(struct ast_cli_entry *e, int cmd, struct ast_cli_arg registration_name = a->argv[3]; - registration = retrieve_registration(registration_name); - if (!registration) { + state = get_state(registration_name); + if (!state) { ast_cli(a->fd, "Unable to retrieve registration %s\n", registration_name); return CLI_FAILURE; } - if (queue_unregister(registration)) { + if (queue_unregister(state)) { ast_cli(a->fd, "Failed to queue unregistration"); return 0; } @@ -1044,21 +1138,20 @@ static char *cli_unregister(struct ast_cli_entry *e, int cmd, struct ast_cli_arg static int ami_unregister(struct mansession *s, const struct message *m) { const char *registration_name = astman_get_header(m, "Registration"); - RAII_VAR(struct sip_outbound_registration *, registration, NULL, ao2_cleanup); + RAII_VAR(struct sip_outbound_registration_state *, state, NULL, ao2_cleanup); if (ast_strlen_zero(registration_name)) { astman_send_error(s, m, "Registration parameter missing."); return 0; } - registration = retrieve_registration(registration_name); - if (!registration) { + state = get_state(registration_name); + if (!state) { astman_send_error(s, m, "Unable to retrieve registration entry\n"); return 0; } - - if (queue_unregister(registration)) { + if (queue_unregister(state)) { astman_send_ack(s, m, "Failed to queue unregistration"); return 0; } @@ -1079,6 +1172,7 @@ static int ami_outbound_registration_task(void *obj) struct sip_ami_outbound *ami = obj; RAII_VAR(struct ast_str *, buf, ast_sip_create_ami_event("OutboundRegistrationDetail", ami->ami), ast_free); + struct sip_outbound_registration_state *state; if (!buf) { return -1; @@ -1086,10 +1180,9 @@ static int ami_outbound_registration_task(void *obj) ast_sip_sorcery_object_to_ami(ami->registration, &buf); - if (ami->registration->state) { + if ((state = get_state(ast_sorcery_object_get_id(ami->registration)))) { pjsip_regc_info info; - if (ami->registration->state->client_state->status == - SIP_REGISTRATION_REGISTERED) { + if (state->client_state->status == SIP_REGISTRATION_REGISTERED) { ++ami->registered; } else { ++ami->not_registered; @@ -1097,10 +1190,11 @@ static int ami_outbound_registration_task(void *obj) ast_str_append(&buf, 0, "Status: %s%s", sip_outbound_registration_status_str[ - ami->registration->state->client_state->status], "\r\n"); + state->client_state->status], "\r\n"); - pjsip_regc_get_info(ami->registration->state->client_state->client, &info); + pjsip_regc_get_info(state->client_state->client, &info); ast_str_append(&buf, 0, "NextReg: %d%s", info.next_reg, "\r\n"); + ao2_ref(state, -1); } astman_append(ami->ami->s, "%s\r\n", ast_str_buffer(buf)); @@ -1121,9 +1215,7 @@ static int ami_show_outbound_registrations(struct mansession *s, { struct ast_sip_ami ami = { .s = s, .m = m, .action_id = astman_get_header(m, "ActionID"), }; struct sip_ami_outbound ami_outbound = { .ami = &ami }; - RAII_VAR(struct ao2_container *, regs, ast_sorcery_retrieve_by_fields( - ast_sip_get_sorcery(), "registration", AST_RETRIEVE_FLAG_MULTIPLE | - AST_RETRIEVE_FLAG_ALL, NULL), ao2_cleanup); + RAII_VAR(struct ao2_container *, regs, get_registrations(), ao2_cleanup); if (!regs) { astman_send_error(s, m, "Unable to retreive " @@ -1150,11 +1242,9 @@ static int ami_show_outbound_registrations(struct mansession *s, static struct ao2_container *cli_get_container(void) { - RAII_VAR(struct ao2_container *, container, NULL, ao2_cleanup); + RAII_VAR(struct ao2_container *, container, get_registrations(), ao2_cleanup); struct ao2_container *s_container; - container = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "registration", - AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL); if (!container) { return NULL; } @@ -1182,7 +1272,17 @@ static int cli_iterator(void *container, ao2_callback_fn callback, void *args) static void *cli_retrieve_by_id(const char *id) { - return ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "registration", id); + struct ao2_container *states; + void *obj = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "registration", id); + + if (!obj) { + /* if the object no longer exists then remove its state */ + ao2_find((states = ao2_global_obj_ref(current_states)), + id, OBJ_SEARCH_KEY | OBJ_UNLINK | OBJ_NODATA); + ao2_ref(states, -1); + } + + return obj; } static int cli_print_header(void *obj, void *arg, int flags) @@ -1202,6 +1302,7 @@ static int cli_print_body(void *obj, void *arg, int flags) struct sip_outbound_registration *registration = obj; struct ast_sip_cli_context *context = arg; const char *id = ast_sorcery_object_get_id(registration); + struct sip_outbound_registration_state *state = get_state(id); #define REGISTRATION_URI_FIELD_LEN 53 ast_assert(context->output_buffer != NULL); @@ -1214,7 +1315,8 @@ static int cli_print_body(void *obj, void *arg, int flags) AST_VECTOR_SIZE(®istration->outbound_auths) ? AST_VECTOR_GET(®istration->outbound_auths, 0) : "n/a", - sip_outbound_registration_status_str[registration->state->client_state->status]); + sip_outbound_registration_status_str[state->client_state->status]); + ao2_ref(state, -1); if (context->show_details || (context->show_details_only_level_0 && context->indent_level == 0)) { @@ -1253,10 +1355,24 @@ static struct ast_cli_entry cli_outbound_registration[] = { static struct ast_sip_cli_formatter_entry *cli_formatter; +static int unload_module(void) +{ + ast_cli_unregister_multiple(cli_outbound_registration, ARRAY_LEN(cli_outbound_registration)); + ast_sip_unregister_cli_formatter(cli_formatter); + ast_manager_unregister("PJSIPShowRegistrationsOutbound"); + ast_manager_unregister("PJSIPUnregister"); + + ao2_global_obj_release(current_states); + + return 0; +} + static int load_module(void) { + struct ao2_container *registrations, *new_states; CHECK_PJSIP_MODULE_LOADED(); + ast_sorcery_apply_config(ast_sip_get_sorcery(), "res_pjsip_outbound_registration"); ast_sorcery_apply_default(ast_sip_get_sorcery(), "registration", "config", "pjsip.conf,criteria=type=registration"); if (ast_sorcery_object_register(ast_sip_get_sorcery(), "registration", sip_outbound_registration_alloc, NULL, sip_outbound_registration_apply)) { @@ -1276,8 +1392,6 @@ static int load_module(void) ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "auth_rejection_permanent", "yes", OPT_BOOL_T, 1, FLDSET(struct sip_outbound_registration, auth_rejection_permanent)); ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "registration", "outbound_auth", "", outbound_auth_handler, outbound_auths_to_str, outbound_auths_to_var_list, 0, 0); ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "support_path", "no", OPT_BOOL_T, 1, FLDSET(struct sip_outbound_registration, support_path)); - ast_sorcery_reload_object(ast_sip_get_sorcery(), "registration"); - sip_outbound_registration_perform_all(); ast_manager_register_xml("PJSIPUnregister", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, ami_unregister); ast_manager_register_xml("PJSIPShowRegistrationsOutbound", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, ami_show_outbound_registrations); @@ -1285,6 +1399,7 @@ static int load_module(void) cli_formatter = ao2_alloc(sizeof(struct ast_sip_cli_formatter_entry), NULL); if (!cli_formatter) { ast_log(LOG_ERROR, "Unable to allocate memory for cli formatter\n"); + unload_module(); return -1; } cli_formatter->name = "registration"; @@ -1298,23 +1413,29 @@ static int load_module(void) ast_sip_register_cli_formatter(cli_formatter); ast_cli_register_multiple(cli_outbound_registration, ARRAY_LEN(cli_outbound_registration)); + if (!(new_states = ao2_container_alloc( + DEFAULT_STATE_BUCKETS, registration_state_hash, registration_state_cmp))) { + ast_log(LOG_ERROR, "Unable to allocate registration states container\n"); + unload_module(); + return AST_MODULE_LOAD_FAILURE; + } + ao2_global_obj_replace_unref(current_states, new_states); + ao2_ref(new_states, -1); + + ast_sorcery_reload_object(ast_sip_get_sorcery(), "registration"); + if (!(registrations = get_registrations())) { + unload_module(); + return AST_MODULE_LOAD_FAILURE; + } + ao2_ref(registrations, -1); + return AST_MODULE_LOAD_SUCCESS; } static int reload_module(void) { ast_sorcery_reload_object(ast_sip_get_sorcery(), "registration"); - sip_outbound_registration_perform_all(); - return 0; -} - -static int unload_module(void) -{ - ast_cli_unregister_multiple(cli_outbound_registration, ARRAY_LEN(cli_outbound_registration)); - ast_sip_unregister_cli_formatter(cli_formatter); - ast_manager_unregister("PJSIPShowRegistrationsOutbound"); - ast_manager_unregister("PJSIPUnregister"); - + ao2_cleanup(get_registrations()); return 0; }