Merge "res_pjsip_pubsub: Add ability to persist generator state information." into 16

This commit is contained in:
Friendly Automation 2020-01-09 16:25:17 -06:00 committed by Gerrit Code Review
commit 3746b1e09e
3 changed files with 143 additions and 47 deletions

View File

@ -545,6 +545,29 @@ void ast_sip_subscription_remove_datastore(struct ast_sip_subscription *subscrip
*/
struct ao2_container *ast_sip_subscription_get_datastores(const struct ast_sip_subscription *subscription);
/*!
* \since 13.31.0
* \since 16.8.0
* \since 17.2.0
* \brief Set persistence data for a subscription
*
* \param subscription The subscription to set persistence data on
* \param persistence_data The persistence data to set
*
* \note This steals the reference to persistence_data
*/
void ast_sip_subscription_set_persistence_data(struct ast_sip_subscription *subscription, struct ast_json *persistence_data);
/*!
* \since 13.31.0
* \since 16.8.0
* \since 17.2.0
* \brief Retrieve persistence data for a subscription
*
* \param subscription The subscription to retrieve persistence data from
*/
const struct ast_json *ast_sip_subscription_get_persistence_data(const struct ast_sip_subscription *subscription);
/*!
* \brief Register a subscription handler
*

View File

@ -60,51 +60,15 @@ static void *dialog_info_allocate_body(void *data)
return ast_sip_presence_xml_create_node(state_data->pool, NULL, "dialog-info");
}
static struct ast_datastore *dialog_info_xml_state_find_or_create(struct ao2_container *datastores)
{
struct ast_datastore *datastore = ast_datastores_find(datastores, "dialog-info+xml");
if (datastore) {
return datastore;
}
datastore = ast_datastores_alloc_datastore(&dialog_info_xml_datastore, "dialog-info+xml");
if (!datastore) {
return NULL;
}
datastore->data = ast_calloc(1, sizeof(struct dialog_info_xml_state));
if (!datastore->data || ast_datastores_add(datastores, datastore)) {
ao2_ref(datastore, -1);
return NULL;
}
return datastore;
}
static unsigned int dialog_info_xml_get_version(struct ao2_container *datastores, unsigned int *version)
{
struct ast_datastore *datastore = dialog_info_xml_state_find_or_create(datastores);
struct dialog_info_xml_state *state;
if (!datastore) {
return -1;
}
state = datastore->data;
*version = state->version++;
ao2_ref(datastore, -1);
return 0;
}
static int dialog_info_generate_body_content(void *body, void *data)
{
pj_xml_node *dialog_info = body, *dialog, *state;
struct ast_datastore *datastore;
struct dialog_info_xml_state *datastore_state;
struct ast_sip_exten_state_data *state_data = data;
char *local = ast_strdupa(state_data->local), *stripped, *statestring = NULL;
char *pidfstate = NULL, *pidfnote = NULL;
enum ast_sip_pidf_state local_state;
unsigned int version;
char version_str[32], sanitized[PJSIP_MAX_URL_SIZE];
struct ast_sip_endpoint *endpoint = NULL;
unsigned int notify_early_inuse_ringing = 0;
@ -113,9 +77,35 @@ static int dialog_info_generate_body_content(void *body, void *data)
return -1;
}
if (dialog_info_xml_get_version(state_data->datastores, &version)) {
ast_log(LOG_WARNING, "dialog-info+xml version could not be retrieved from datastore\n");
return -1;
datastore = ast_datastores_find(state_data->datastores, "dialog-info+xml");
if (!datastore) {
const struct ast_json *version_json = NULL;
datastore = ast_datastores_alloc_datastore(&dialog_info_xml_datastore, "dialog-info+xml");
if (!datastore) {
return -1;
}
datastore->data = ast_calloc(1, sizeof(struct dialog_info_xml_state));
if (!datastore->data || ast_datastores_add(state_data->datastores, datastore)) {
ao2_ref(datastore, -1);
return -1;
}
datastore_state = datastore->data;
if (state_data->sub) {
version_json = ast_sip_subscription_get_persistence_data(state_data->sub);
}
if (version_json) {
datastore_state->version = ast_json_integer_get(version_json);
datastore_state->version++;
} else {
datastore_state->version = 0;
}
} else {
datastore_state = datastore->data;
datastore_state->version++;
}
stripped = ast_strip_quoted(local, "<", ">");
@ -130,9 +120,13 @@ static int dialog_info_generate_body_content(void *body, void *data)
ast_sip_presence_xml_create_attr(state_data->pool, dialog_info, "xmlns", "urn:ietf:params:xml:ns:dialog-info");
snprintf(version_str, sizeof(version_str), "%u", version);
snprintf(version_str, sizeof(version_str), "%u", datastore_state->version);
ast_sip_presence_xml_create_attr(state_data->pool, dialog_info, "version", version_str);
if (state_data->sub) {
ast_sip_subscription_set_persistence_data(state_data->sub, ast_json_integer_create(datastore_state->version));
}
ast_sip_presence_xml_create_attr(state_data->pool, dialog_info, "state", "full");
ast_sip_presence_xml_create_attr(state_data->pool, dialog_info, "entity", sanitized);
@ -156,6 +150,8 @@ static int dialog_info_generate_body_content(void *body, void *data)
ast_sip_presence_xml_create_attr(state_data->pool, param, "pvalue", "no");
}
ao2_ref(datastore, -1);
return 0;
}

View File

@ -132,6 +132,11 @@
and therefore the subscription must be deleted after an asterisk restart.
</synopsis>
</configOption>
<configOption name="generator_data">
<synopsis>If set, contains persistence data for all generators of content
for the subscription.
</synopsis>
</configOption>
</configObject>
<configObject name="resource_list">
<synopsis>Resource list configuration parameters.</synopsis>
@ -389,6 +394,8 @@ struct subscription_persistence {
char contact_uri[PJSIP_MAX_URL_SIZE];
/*! Prune subscription on restart */
int prune_on_boot;
/*! Body generator specific persistence data */
struct ast_json *generator_data;
};
/*!
@ -490,6 +497,8 @@ struct ast_sip_subscription {
unsigned int full_state;
/*! URI associated with the subscription */
pjsip_sip_uri *uri;
/*! Data to be persisted with the subscription */
struct ast_json *persistence_data;
/*! Name of resource being subscribed to */
char resource[0];
};
@ -615,6 +624,7 @@ static void subscription_persistence_destroy(void *obj)
ast_free(persistence->endpoint);
ast_free(persistence->tag);
ast_json_unref(persistence->generator_data);
}
/*! \brief Allocator for subscription persistence */
@ -1198,6 +1208,7 @@ static void destroy_subscription(struct ast_sip_subscription *sub)
AST_VECTOR_FREE(&sub->children);
ao2_cleanup(sub->datastores);
ast_json_unref(sub->persistence_data);
ast_free(sub);
}
@ -1248,6 +1259,14 @@ static struct ast_sip_subscription *allocate_subscription(const struct ast_sip_s
pjsip_sip_uri_assign(tree->dlg->pool, sub->uri, contact_uri);
pj_strdup2(tree->dlg->pool, &sub->uri->user, resource);
/* If there is any persistence information available for this subscription that was persisted
* then make it available so that the NOTIFY has the correct state.
*/
if (tree->persistence && tree->persistence->generator_data) {
sub->persistence_data = ast_json_object_get(tree->persistence->generator_data, resource);
}
sub->handler = handler;
sub->subscription_state = PJSIP_EVSUB_STATE_ACTIVE;
sub->tree = ao2_bump(tree);
@ -1446,11 +1465,10 @@ static struct sip_subscription_tree *allocate_subscription_tree(struct ast_sip_e
static struct sip_subscription_tree *create_subscription_tree(const struct ast_sip_subscription_handler *handler,
struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata, const char *resource,
struct ast_sip_pubsub_body_generator *generator, struct resource_tree *tree,
pj_status_t *dlg_status)
pj_status_t *dlg_status, struct subscription_persistence *persistence)
{
struct sip_subscription_tree *sub_tree;
pjsip_dialog *dlg;
struct subscription_persistence *persistence;
sub_tree = allocate_subscription_tree(endpoint, rdata);
if (!sub_tree) {
@ -1491,6 +1509,9 @@ static struct sip_subscription_tree *create_subscription_tree(const struct ast_s
sub_tree->notification_batch_interval = tree->notification_batch_interval;
/* Persistence information needs to be available for all the subscriptions */
sub_tree->persistence = ao2_bump(persistence);
sub_tree->root = create_virtual_subscriptions(handler, resource, generator, sub_tree, tree->root);
if (AST_VECTOR_SIZE(&sub_tree->root->children) > 0) {
sub_tree->is_list = 1;
@ -1612,7 +1633,7 @@ static int sub_persistence_recreate(void *obj)
pj_status_t dlg_status;
sub_tree = create_subscription_tree(handler, endpoint, rdata, resource, generator,
&tree, &dlg_status);
&tree, &dlg_status, persistence);
if (!sub_tree) {
if (dlg_status != PJ_EEXISTS) {
ast_log(LOG_WARNING, "Failed recreating '%s' subscription: Could not create subscription tree.\n",
@ -1630,7 +1651,6 @@ static int sub_persistence_recreate(void *obj)
ind->sub_tree = ao2_bump(sub_tree);
ind->expires = expires_header->ivalue;
sub_tree->persistence = ao2_bump(persistence);
subscription_persistence_update(sub_tree, rdata, SUBSCRIPTION_PERSISTENCE_RECREATED);
if (ast_sip_push_task(sub_tree->serializer, initial_notify_task, ind)) {
/* Could not send initial subscribe NOTIFY */
@ -2644,6 +2664,28 @@ struct ao2_container *ast_sip_publication_get_datastores(const struct ast_sip_pu
return publication->datastores;
}
void ast_sip_subscription_set_persistence_data(struct ast_sip_subscription *subscription, struct ast_json *persistence_data)
{
ast_json_unref(subscription->persistence_data);
subscription->persistence_data = persistence_data;
if (subscription->tree->persistence) {
if (!subscription->tree->persistence->generator_data) {
subscription->tree->persistence->generator_data = ast_json_object_create();
if (!subscription->tree->persistence->generator_data) {
return;
}
}
ast_json_object_set(subscription->tree->persistence->generator_data, subscription->resource,
ast_json_ref(persistence_data));
}
}
const struct ast_json *ast_sip_subscription_get_persistence_data(const struct ast_sip_subscription *subscription)
{
return subscription->persistence_data;
}
AST_RWLIST_HEAD_STATIC(publish_handlers, ast_sip_publish_handler);
static int publication_hash_fn(const void *obj, const int flags)
@ -3005,7 +3047,7 @@ static pj_bool_t pubsub_on_rx_subscribe_request(pjsip_rx_data *rdata)
return PJ_TRUE;
}
sub_tree = create_subscription_tree(handler, endpoint, rdata, resource, generator, &tree, &dlg_status);
sub_tree = create_subscription_tree(handler, endpoint, rdata, resource, generator, &tree, &dlg_status, NULL);
if (!sub_tree) {
if (dlg_status != PJ_EEXISTS) {
pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL);
@ -4657,6 +4699,39 @@ static int persistence_tag_struct2str(const void *obj, const intptr_t *args, cha
return 0;
}
static int persistence_generator_data_str2struct(const struct aco_option *opt, struct ast_variable *var, void *obj)
{
struct subscription_persistence *persistence = obj;
struct ast_json_error error;
/* We tolerate a failure of the JSON to load and instead start fresh, since this field
* originates from the persistence code and not a user.
*/
persistence->generator_data = ast_json_load_string(var->value, &error);
return 0;
}
static int persistence_generator_data_struct2str(const void *obj, const intptr_t *args, char **buf)
{
const struct subscription_persistence *persistence = obj;
char *value;
if (!persistence->generator_data) {
return 0;
}
value = ast_json_dump_string(persistence->generator_data);
if (!value) {
return -1;
}
*buf = ast_strdup(value);
ast_json_free(value);
return 0;
}
static int persistence_expires_str2struct(const struct aco_option *opt, struct ast_variable *var, void *obj)
{
struct subscription_persistence *persistence = obj;
@ -5529,6 +5604,8 @@ static int load_module(void)
CHARFLDSET(struct subscription_persistence, contact_uri));
ast_sorcery_object_field_register(sorcery, "subscription_persistence", "prune_on_boot", "no", OPT_YESNO_T, 1,
FLDSET(struct subscription_persistence, prune_on_boot));
ast_sorcery_object_field_register_custom(sorcery, "subscription_persistence", "generator_data", "",
persistence_generator_data_str2struct, persistence_generator_data_struct2str, NULL, 0, 0);
if (apply_list_configuration(sorcery)) {
ast_sched_context_destroy(sched);