[asterisk-commits] kharwell: branch 13 r427815 - in /branches/13: ./ res/

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Thu Nov 13 16:03:05 CST 2014


Author: kharwell
Date: Thu Nov 13 16:03:00 2014
New Revision: 427815

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=427815
Log:
res_pjsip_exten_state: PJSIPShowSubscriptionsInbound causes crash

When using a non-default sorcery wizard (in this instance realtime) for
outbound registrations and after adding in an appropriate call to
ast_sorcery_apply_config() (since it is missing) Asterisk will crash after
a stack overflow occurs due to the code infinitely recursing.  The fix entails
removing the outbound registration state dependency from the outbound
registration sorcery object and instead keeping an in memory container that
can be used to lookup the state when needed.

ASTERISK-24514
Reported by: Mark Michelson
Review: https://reviewboard.asterisk.org/r/4164/
........

Merged revisions 427814 from http://svn.asterisk.org/svn/asterisk/branches/12

Modified:
    branches/13/   (props changed)
    branches/13/res/res_pjsip_outbound_registration.c

Propchange: branches/13/
------------------------------------------------------------------------------
Binary property 'branch-12-merged' - no diff available.

Modified: branches/13/res/res_pjsip_outbound_registration.c
URL: http://svnview.digium.com/svn/asterisk/branches/13/res/res_pjsip_outbound_registration.c?view=diff&rev=427815&r1=427814&r2=427815
==============================================================================
--- branches/13/res/res_pjsip_outbound_registration.c (original)
+++ branches/13/res/res_pjsip_outbound_registration.c Thu Nov 13 16:03:00 2014
@@ -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 @@
 	[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 @@
 	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 @@
 
 /*! \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 @@
 {
 	struct sip_outbound_registration_state *state = obj;
 
+	ao2_cleanup(state->registration);
+
 	if (!state->client_state) {
 		return;
 	}
@@ -579,7 +685,7 @@
 }
 
 /*! \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 @@
 	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 @@
 {
 	struct sip_outbound_registration *registration = obj;
 
-	ao2_cleanup(registration->state);
 	ast_sip_auth_vector_destroy(&registration->outbound_auths);
 
 	ast_string_field_free_memory(registration);
@@ -616,7 +722,6 @@
 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 @@
 /*! \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 @@
 		}
 	}
 
-	if (!registration->state->client_state->client &&
-		pjsip_regc_create(ast_sip_get_pjsip_endpoint(), registration->state->client_state, sip_outbound_registration_response_cb,
-		&registration->state->client_state->client) != PJ_SUCCESS) {
-		return -1;
-	}
-
-	pjsip_regc_set_transport(registration->state->client_state->client, &selector);
+	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(state->client_state->client, &selector);
 
 	if (!ast_strlen_zero(registration->outbound_proxy)) {
 		pjsip_route_hdr route_set, *route;
@@ -787,26 +894,54 @@
 
 		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) {
-		return -1;
-	}
+	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(&registration->outbound_auths));
+	for (i = 0; i < AST_VECTOR_SIZE(&registration->outbound_auths); ++i) {
+		const char *name = ast_strdup(AST_VECTOR_GET(&registration->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;
 }
@@ -814,7 +949,10 @@
 /*! \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,73 +965,37 @@
 		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 (!applied->state) {
-		return -1;
-	}
-
-	return ast_sip_push_task_synchronous(NULL, sip_outbound_registration_regc_alloc, applied);
-}
-
-/*! \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;
-
-	/* Just in case the client state is being reused for this registration, free the auth information */
-	ast_sip_auth_vector_destroy(&registration->state->client_state->outbound_auths);
-
-	AST_VECTOR_INIT(&registration->state->client_state->outbound_auths, AST_VECTOR_SIZE(&registration->outbound_auths));
-	for (i = 0; i < AST_VECTOR_SIZE(&registration->outbound_auths); ++i) {
-		const char *name = ast_strdup(AST_VECTOR_GET(&registration->outbound_auths, i));
-		AST_VECTOR_APPEND(&registration->state->client_state->outbound_auths, name);
-	}
-	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);
-
-	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);
+	if (state && can_reuse_registration(state->registration, applied)) {
+		ao2_replace(state->registration, applied);
+		return 0;
+	}
+
+	if (!(new_state = sip_outbound_registration_state_alloc(applied))) {
+		return -1;
+	}
+
+	if (ast_sip_push_task_synchronous(NULL, sip_outbound_registration_regc_alloc, new_state)) {
+		return -1;
+	}
+
+	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;
+	}
+
+	ao2_lock(states);
+
+	if (state) {
+		ao2_unlink(states, state);
+	}
+
+	ao2_link(states, new_state);
+	ao2_unlock(states);
+
+	return 0;
 }
 
 static int outbound_auth_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
@@ -928,44 +1030,36 @@
 	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);
-	}
-
-	return 0;
-}
-
-static int queue_unregister(struct sip_outbound_registration *registration)
-{
-	ao2_ref(registration, +1);
-	if (ast_sip_push_task(registration->state->client_state->serializer, unregister_task, registration)) {
-		ao2_cleanup(registration);
+		ao2_cleanup(state->client_state);
+	}
+
+	return 0;
+}
+
+static int queue_unregister(struct sip_outbound_registration_state *state)
+{
+	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 @@
 
 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 @@
 
 	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 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 @@
 	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 @@
 
 	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 @@
 
 		ast_str_append(&buf, 0, "Status: %s%s",
 			       sip_outbound_registration_status_str[
-				       ami->registration->state->client_state->status], "\r\n");
-
-		pjsip_regc_get_info(ami->registration->state->client_state->client, &info);
+				       state->client_state->status], "\r\n");
+
+		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 @@
 {
 	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 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 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 @@
 	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 @@
 		AST_VECTOR_SIZE(&registration->outbound_auths)
 			? AST_VECTOR_GET(&registration->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_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 @@
 	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 @@
 	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 @@
 	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;
 }
 




More information about the asterisk-commits mailing list