Merge "res_pjsip_outbound_publish: state potential dropped on reloads/realtime fetches"

This commit is contained in:
Joshua Colp
2016-05-11 12:57:34 -05:00
committed by Gerrit Code Review

View File

@@ -406,22 +406,30 @@ static void sip_outbound_publish_synchronize(struct ast_sip_event_publisher_hand
ao2_ref(states, -1); ao2_ref(states, -1);
} }
struct ast_sip_outbound_publish_client *ast_sip_publish_client_get(const char *name) static struct ast_sip_outbound_publish_state *sip_publish_state_get(const char *id)
{ {
RAII_VAR(struct ao2_container *, states, struct ao2_container *states = ao2_global_obj_ref(current_states);
ao2_global_obj_ref(current_states), ao2_cleanup); struct ast_sip_outbound_publish_state *res;
RAII_VAR(struct ast_sip_outbound_publish_state *, state, NULL, ao2_cleanup);
if (!states) { if (!states) {
return NULL; return NULL;
} }
state = ao2_find(states, name, OBJ_SEARCH_KEY); res = ao2_find(states, id, OBJ_SEARCH_KEY);
ao2_ref(states, -1);
return res;
}
struct ast_sip_outbound_publish_client *ast_sip_publish_client_get(const char *name)
{
struct ast_sip_outbound_publish_state *state = sip_publish_state_get(name);
if (!state) { if (!state) {
return NULL; return NULL;
} }
ao2_ref(state->client, +1); ao2_ref(state->client, +1);
ao2_ref(state, -1);
return state->client; return state->client;
} }
@@ -1086,25 +1094,89 @@ static struct ast_sip_outbound_publish_state *sip_outbound_publish_state_alloc(
return state; return state;
} }
/*! \brief Apply function which finds or allocates a state structure */ static int initialize_publish_client(struct ast_sip_outbound_publish *publish,
static int sip_outbound_publish_apply(const struct ast_sorcery *sorcery, void *obj) struct ast_sip_outbound_publish_state *state)
{ {
RAII_VAR(struct ao2_container *, states, ao2_global_obj_ref(current_states), ao2_cleanup); if (ast_sip_push_task_synchronous(NULL, sip_outbound_publish_client_alloc, state->client)) {
RAII_VAR(struct ast_sip_outbound_publish_state *, state, NULL, ao2_cleanup); ast_log(LOG_ERROR, "Unable to create client for outbound publish '%s'\n",
struct ast_sip_outbound_publish *applied = obj; ast_sorcery_object_get_id(publish));
if (ast_strlen_zero(applied->server_uri)) {
ast_log(LOG_ERROR, "No server URI specified on outbound publish '%s'\n",
ast_sorcery_object_get_id(applied));
return -1;
} else if (ast_strlen_zero(applied->event)) {
ast_log(LOG_ERROR, "No event type specified for outbound publish '%s'\n",
ast_sorcery_object_get_id(applied));
return -1; return -1;
} }
return 0;
}
static int validate_publish_config(struct ast_sip_outbound_publish *publish)
{
if (ast_strlen_zero(publish->server_uri)) {
ast_log(LOG_ERROR, "No server URI specified on outbound publish '%s'\n",
ast_sorcery_object_get_id(publish));
return -1;
} else if (ast_strlen_zero(publish->event)) {
ast_log(LOG_ERROR, "No event type specified for outbound publish '%s'\n",
ast_sorcery_object_get_id(publish));
return -1;
}
return 0;
}
static int current_state_reusable(struct ast_sip_outbound_publish *publish,
struct ast_sip_outbound_publish_state *current_state)
{
struct ast_sip_outbound_publish *old_publish;
if (!can_reuse_publish(current_state->client->publish, publish)) {
/*
* Something significant has changed in the configuration, so we are
* unable to use the old state object. The current state needs to go
* away and a new one needs to be created.
*/
return 0;
}
/*
* We can reuse the current state object so keep it, but swap out the
* underlying publish object with the new one.
*/
old_publish = current_state->client->publish;
current_state->client->publish = publish;
if (initialize_publish_client(publish, current_state)) {
/*
* If the state object fails to re-initialize then swap
* the old publish info back in.
*/
current_state->client->publish = publish;
return -1;
}
/*
* Since we swapped out the publish object the new one needs a ref
* while the old one needs to go away.
*/
ao2_ref(current_state->client->publish, +1);
ao2_cleanup(old_publish);
/* Tell the caller that the current state object should be used */
return 1;
}
/*! \brief Apply function which finds or allocates a state structure */
static int sip_outbound_publish_apply(const struct ast_sorcery *sorcery, void *obj)
{
#define ADD_TO_NEW_STATES(__obj) \
do { if (__obj) { \
ao2_link(new_states, __obj); \
ao2_ref(__obj, -1); } } while (0)
struct ast_sip_outbound_publish *applied = obj;
struct ast_sip_outbound_publish_state *current_state, *new_state;
int res;
/*
* New states are being loaded or reloaded. We'll need to add the new
* object if created/updated, or keep the old object if an error occurs.
*/
if (!new_states) { if (!new_states) {
/* make sure new_states has been allocated as we will be adding to it */
new_states = ao2_container_alloc_options( new_states = ao2_container_alloc_options(
AO2_ALLOC_OPT_LOCK_NOLOCK, DEFAULT_STATE_BUCKETS, AO2_ALLOC_OPT_LOCK_NOLOCK, DEFAULT_STATE_BUCKETS,
outbound_publish_state_hash, outbound_publish_state_cmp); outbound_publish_state_hash, outbound_publish_state_cmp);
@@ -1115,35 +1187,44 @@ static int sip_outbound_publish_apply(const struct ast_sorcery *sorcery, void *o
} }
} }
if (states) { /* If there is current state we'll want to maintain it if any errors occur */
state = ao2_find(states, ast_sorcery_object_get_id(obj), OBJ_SEARCH_KEY); current_state = sip_publish_state_get(ast_sorcery_object_get_id(applied));
if (state) {
if (can_reuse_publish(state->client->publish, applied)) { if ((res = validate_publish_config(applied))) {
ao2_replace(state->client->publish, applied); ADD_TO_NEW_STATES(current_state);
} else { return res;
ao2_ref(state, -1);
state = NULL;
}
}
} }
if (!state) { if (current_state && (res = current_state_reusable(applied, current_state))) {
state = sip_outbound_publish_state_alloc(applied); /*
if (!state) { * The current state object was able to be reused, or an error
ast_log(LOG_ERROR, "Unable to create state for outbound publish '%s'\n", * occurred. Either way we keep the current state and be done.
ast_sorcery_object_get_id(applied)); */
return -1; ADD_TO_NEW_STATES(current_state);
}; return res == 1 ? 0 : -1;
} }
if (ast_sip_push_task_synchronous(NULL, sip_outbound_publish_client_alloc, state->client)) { /*
ast_log(LOG_ERROR, "Unable to create client for outbound publish '%s'\n", * No current state was found or it was unable to be reused. Either way
* we'll need to create a new state object.
*/
new_state = sip_outbound_publish_state_alloc(applied);
if (!new_state) {
ast_log(LOG_ERROR, "Unable to create state for outbound publish '%s'\n",
ast_sorcery_object_get_id(applied)); ast_sorcery_object_get_id(applied));
ADD_TO_NEW_STATES(current_state);
return -1;
};
if (initialize_publish_client(applied, new_state)) {
ADD_TO_NEW_STATES(current_state);
ao2_ref(new_state, -1);
return -1; return -1;
} }
ao2_link(new_states, state); ADD_TO_NEW_STATES(new_state);
return 0; ao2_cleanup(current_state);
return res;
} }
static int outbound_auth_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) static int outbound_auth_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)