mirror of
https://github.com/asterisk/asterisk.git
synced 2025-10-12 15:45:18 +00:00
Merge "sorcery/res_pjsip: Refactor for realtime performance" into 13
This commit is contained in:
@@ -1279,6 +1279,9 @@
|
||||
<configOption name="keep_alive_interval" default="0">
|
||||
<synopsis>The interval (in seconds) to send keepalives to active connection-oriented transports.</synopsis>
|
||||
</configOption>
|
||||
<configOption name="contact_expiration_check_interval" default="30">
|
||||
<synopsis>The interval (in seconds) to check for expired contacts.</synopsis>
|
||||
</configOption>
|
||||
<configOption name="max_initial_qualify_time" default="0">
|
||||
<synopsis>The maximum amount of time from startup that qualifies should be attempted on all contacts.
|
||||
If greater than the qualify_frequency for an aor, qualify_frequency will be used instead.</synopsis>
|
||||
|
@@ -36,6 +36,7 @@
|
||||
#define DEFAULT_MAX_INITIAL_QUALIFY_TIME 0
|
||||
#define DEFAULT_FROM_USER "asterisk"
|
||||
#define DEFAULT_REGCONTEXT ""
|
||||
#define DEFAULT_CONTACT_EXPIRATION_CHECK_INTERVAL 30
|
||||
|
||||
static char default_useragent[256];
|
||||
|
||||
@@ -58,6 +59,8 @@ struct global_config {
|
||||
unsigned int keep_alive_interval;
|
||||
/* The maximum time for all contacts to be qualified at startup */
|
||||
unsigned int max_initial_qualify_time;
|
||||
/* The interval at which to check for expired contacts */
|
||||
unsigned int contact_expiration_check_interval;
|
||||
};
|
||||
|
||||
static void global_destructor(void *obj)
|
||||
@@ -186,6 +189,21 @@ unsigned int ast_sip_get_keep_alive_interval(void)
|
||||
return interval;
|
||||
}
|
||||
|
||||
unsigned int ast_sip_get_contact_expiration_check_interval(void)
|
||||
{
|
||||
unsigned int interval;
|
||||
struct global_config *cfg;
|
||||
|
||||
cfg = get_global_cfg();
|
||||
if (!cfg) {
|
||||
return DEFAULT_CONTACT_EXPIRATION_CHECK_INTERVAL;
|
||||
}
|
||||
|
||||
interval = cfg->contact_expiration_check_interval;
|
||||
ao2_ref(cfg, -1);
|
||||
return interval;
|
||||
}
|
||||
|
||||
unsigned int ast_sip_get_max_initial_qualify_time(void)
|
||||
{
|
||||
unsigned int time;
|
||||
@@ -331,6 +349,9 @@ int ast_sip_initialize_sorcery_global(void)
|
||||
OPT_STRINGFIELD_T, 0, STRFLDSET(struct global_config, default_from_user));
|
||||
ast_sorcery_object_field_register(sorcery, "global", "regcontext", DEFAULT_REGCONTEXT,
|
||||
OPT_STRINGFIELD_T, 0, STRFLDSET(struct global_config, regcontext));
|
||||
ast_sorcery_object_field_register(sorcery, "global", "contact_expiration_check_interval",
|
||||
__stringify(DEFAULT_CONTACT_EXPIRATION_CHECK_INTERVAL),
|
||||
OPT_UINT_T, 0, FLDSET(struct global_config, contact_expiration_check_interval));
|
||||
|
||||
|
||||
if (ast_sorcery_instance_observer_add(sorcery, &observer_callbacks_global)) {
|
||||
|
@@ -1089,31 +1089,13 @@ static int qualify_and_schedule_cb(void *obj, void *arg, int flags)
|
||||
*/
|
||||
static int qualify_and_schedule_all_cb(void *obj, void *arg, int flags)
|
||||
{
|
||||
struct ast_sip_endpoint *endpoint = obj;
|
||||
char *aors;
|
||||
char *aor_name;
|
||||
struct ast_sip_aor *aor = obj;
|
||||
struct ao2_container *contacts;
|
||||
|
||||
if (ast_strlen_zero(endpoint->aors)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
aors = ast_strdupa(endpoint->aors);
|
||||
while ((aor_name = ast_strip(strsep(&aors, ",")))) {
|
||||
struct ast_sip_aor *aor;
|
||||
struct ao2_container *contacts;
|
||||
|
||||
aor = ast_sip_location_retrieve_aor(aor_name);
|
||||
if (!aor) {
|
||||
continue;
|
||||
}
|
||||
|
||||
contacts = ast_sip_location_retrieve_aor_contacts(aor);
|
||||
if (contacts) {
|
||||
ao2_callback(contacts, OBJ_NODATA, qualify_and_schedule_cb, aor);
|
||||
ao2_ref(contacts, -1);
|
||||
}
|
||||
|
||||
ao2_ref(aor, -1);
|
||||
contacts = ast_sip_location_retrieve_aor_contacts(aor);
|
||||
if (contacts) {
|
||||
ao2_callback(contacts, OBJ_NODATA, qualify_and_schedule_cb, aor);
|
||||
ao2_ref(contacts, -1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -1134,16 +1116,25 @@ static int unschedule_all_cb(void *obj, void *arg, int flags)
|
||||
|
||||
static void qualify_and_schedule_all(void)
|
||||
{
|
||||
struct ao2_container *endpoints = ast_sip_get_endpoints();
|
||||
struct ast_variable *var = ast_variable_new("qualify_frequency >", "0", "");
|
||||
struct ao2_container *aors;
|
||||
|
||||
if (!var) {
|
||||
return;
|
||||
}
|
||||
aors = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(),
|
||||
"aor", AST_RETRIEVE_FLAG_MULTIPLE, var);
|
||||
|
||||
ast_variables_destroy(var);
|
||||
|
||||
ao2_callback(sched_qualifies, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, unschedule_all_cb, NULL);
|
||||
|
||||
if (!endpoints) {
|
||||
if (!aors) {
|
||||
return;
|
||||
}
|
||||
|
||||
ao2_callback(endpoints, OBJ_NODATA, qualify_and_schedule_all_cb, NULL);
|
||||
ao2_ref(endpoints, -1);
|
||||
ao2_callback(aors, OBJ_NODATA, qualify_and_schedule_all_cb, NULL);
|
||||
ao2_ref(aors, -1);
|
||||
}
|
||||
|
||||
static int format_contact_status(void *obj, void *arg, int flags)
|
||||
|
@@ -966,9 +966,14 @@ static int unsubscribe(void *obj, void *arg, int flags)
|
||||
static void create_mwi_subscriptions(void)
|
||||
{
|
||||
struct ao2_container *endpoints;
|
||||
struct ast_variable *var;
|
||||
|
||||
var = ast_variable_new("mailboxes !=", "", "");
|
||||
|
||||
endpoints = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "endpoint",
|
||||
AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
|
||||
AST_RETRIEVE_FLAG_MULTIPLE, var);
|
||||
|
||||
ast_variables_destroy(var);
|
||||
if (!endpoints) {
|
||||
return;
|
||||
}
|
||||
|
@@ -25,265 +25,102 @@
|
||||
#include "asterisk.h"
|
||||
|
||||
#include <pjsip.h>
|
||||
#include <sys/time.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "asterisk/res_pjsip.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/sched.h"
|
||||
|
||||
#define CONTACT_AUTOEXPIRE_BUCKETS 977
|
||||
/*! \brief Thread keeping things alive */
|
||||
static pthread_t check_thread = AST_PTHREADT_NULL;
|
||||
|
||||
static struct ao2_container *contact_autoexpire;
|
||||
/*! \brief The global interval at which to check for contact expiration */
|
||||
static unsigned int check_interval;
|
||||
|
||||
/*! \brief Scheduler used for automatically expiring contacts */
|
||||
static struct ast_sched_context *sched;
|
||||
|
||||
/*! \brief Structure used for contact auto-expiration */
|
||||
struct contact_expiration {
|
||||
/*! \brief Contact that is being auto-expired */
|
||||
struct ast_sip_contact *contact;
|
||||
|
||||
/*! \brief Scheduled item for performing expiration */
|
||||
int sched;
|
||||
};
|
||||
|
||||
/*! \brief Destructor function for contact auto-expiration */
|
||||
static void contact_expiration_destroy(void *obj)
|
||||
{
|
||||
struct contact_expiration *expiration = obj;
|
||||
|
||||
ao2_cleanup(expiration->contact);
|
||||
}
|
||||
|
||||
/*! \brief Hashing function for contact auto-expiration */
|
||||
static int contact_expiration_hash(const void *obj, const int flags)
|
||||
{
|
||||
const struct contact_expiration *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->contact);
|
||||
break;
|
||||
default:
|
||||
/* Hash can only work on something with a full key. */
|
||||
ast_assert(0);
|
||||
return 0;
|
||||
}
|
||||
return ast_str_hash(key);
|
||||
}
|
||||
|
||||
/*! \brief Comparison function for contact auto-expiration */
|
||||
static int contact_expiration_cmp(void *obj, void *arg, int flags)
|
||||
{
|
||||
const struct contact_expiration *object_left = obj;
|
||||
const struct contact_expiration *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->contact);
|
||||
/* Fall through */
|
||||
case OBJ_SEARCH_KEY:
|
||||
cmp = strcmp(ast_sorcery_object_get_id(object_left->contact), right_key);
|
||||
break;
|
||||
case OBJ_SEARCH_PARTIAL_KEY:
|
||||
/*
|
||||
* We could also use a partial key struct containing a length
|
||||
* so strlen() does not get called for every comparison instead.
|
||||
*/
|
||||
cmp = strncmp(ast_sorcery_object_get_id(object_left->contact), right_key,
|
||||
strlen(right_key));
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
* What arg points to is specific to this traversal callback
|
||||
* and has no special meaning to astobj2.
|
||||
*/
|
||||
cmp = 0;
|
||||
break;
|
||||
}
|
||||
if (cmp) {
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* At this point the traversal callback is identical to a sorted
|
||||
* container.
|
||||
*/
|
||||
return CMP_MATCH;
|
||||
}
|
||||
|
||||
/*! \brief Scheduler function which deletes a contact */
|
||||
static int contact_expiration_expire(const void *data)
|
||||
{
|
||||
struct contact_expiration *expiration = (void *) data;
|
||||
|
||||
expiration->sched = -1;
|
||||
|
||||
/* This will end up invoking the deleted observer callback, which will perform the unlinking and such */
|
||||
ast_sorcery_delete(ast_sip_get_sorcery(), expiration->contact);
|
||||
ao2_ref(expiration, -1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief Observer callback for when a contact is created */
|
||||
static void contact_expiration_observer_created(const void *object)
|
||||
{
|
||||
const struct ast_sip_contact *contact = object;
|
||||
struct contact_expiration *expiration;
|
||||
int expires = MAX(0, ast_tvdiff_ms(contact->expiration_time, ast_tvnow()));
|
||||
|
||||
if (ast_tvzero(contact->expiration_time)) {
|
||||
return;
|
||||
}
|
||||
|
||||
expiration = ao2_alloc_options(sizeof(*expiration), contact_expiration_destroy,
|
||||
AO2_ALLOC_OPT_LOCK_NOLOCK);
|
||||
if (!expiration) {
|
||||
return;
|
||||
}
|
||||
|
||||
expiration->contact = (struct ast_sip_contact*)contact;
|
||||
ao2_ref(expiration->contact, +1);
|
||||
|
||||
ao2_ref(expiration, +1);
|
||||
if ((expiration->sched = ast_sched_add(sched, expires, contact_expiration_expire, expiration)) < 0) {
|
||||
ao2_ref(expiration, -1);
|
||||
ast_log(LOG_ERROR, "Scheduled expiration for contact '%s' could not be performed, contact may persist past life\n",
|
||||
ast_sorcery_object_get_id(contact));
|
||||
} else {
|
||||
ao2_link(contact_autoexpire, expiration);
|
||||
}
|
||||
ao2_ref(expiration, -1);
|
||||
}
|
||||
|
||||
/*! \brief Observer callback for when a contact is updated */
|
||||
static void contact_expiration_observer_updated(const void *object)
|
||||
{
|
||||
const struct ast_sip_contact *contact = object;
|
||||
struct contact_expiration *expiration;
|
||||
int expires = MAX(0, ast_tvdiff_ms(contact->expiration_time, ast_tvnow()));
|
||||
|
||||
expiration = ao2_find(contact_autoexpire, ast_sorcery_object_get_id(contact),
|
||||
OBJ_SEARCH_KEY);
|
||||
if (!expiration) {
|
||||
return;
|
||||
}
|
||||
|
||||
AST_SCHED_REPLACE_UNREF(expiration->sched, sched, expires, contact_expiration_expire,
|
||||
expiration, ao2_cleanup(expiration), ao2_cleanup(expiration), ao2_ref(expiration, +1));
|
||||
ao2_ref(expiration, -1);
|
||||
}
|
||||
|
||||
/*! \brief Observer callback for when a contact is deleted */
|
||||
static void contact_expiration_observer_deleted(const void *object)
|
||||
{
|
||||
struct contact_expiration *expiration;
|
||||
|
||||
expiration = ao2_find(contact_autoexpire, ast_sorcery_object_get_id(object),
|
||||
OBJ_SEARCH_KEY | OBJ_UNLINK);
|
||||
if (!expiration) {
|
||||
return;
|
||||
}
|
||||
|
||||
AST_SCHED_DEL_UNREF(sched, expiration->sched, ao2_cleanup(expiration));
|
||||
ao2_ref(expiration, -1);
|
||||
}
|
||||
|
||||
/*! \brief Observer callbacks for autoexpiring contacts */
|
||||
static const struct ast_sorcery_observer contact_expiration_observer = {
|
||||
.created = contact_expiration_observer_created,
|
||||
.updated = contact_expiration_observer_updated,
|
||||
.deleted = contact_expiration_observer_deleted,
|
||||
};
|
||||
|
||||
/*! \brief Callback function which deletes a contact if it has expired or sets up auto-expiry */
|
||||
static int contact_expiration_setup(void *obj, void *arg, int flags)
|
||||
/*! \brief Callback function which deletes a contact */
|
||||
static int expire_contact(void *obj, void *arg, int flags)
|
||||
{
|
||||
struct ast_sip_contact *contact = obj;
|
||||
int expires = MAX(0, ast_tvdiff_ms(contact->expiration_time, ast_tvnow()));
|
||||
|
||||
if (!expires) {
|
||||
ast_sorcery_delete(ast_sip_get_sorcery(), contact);
|
||||
} else {
|
||||
contact_expiration_observer_created(contact);
|
||||
}
|
||||
ast_sorcery_delete(ast_sip_get_sorcery(), contact);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief Initialize auto-expiration of any existing contacts */
|
||||
static void contact_expiration_initialize_existing(void)
|
||||
static void *check_expiration_thread(void *data)
|
||||
{
|
||||
struct ao2_container *contacts;
|
||||
struct ast_variable *var;
|
||||
char *time = alloca(64);
|
||||
|
||||
contacts = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "contact",
|
||||
AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
|
||||
if (!contacts) {
|
||||
return;
|
||||
while (check_interval) {
|
||||
sleep(check_interval);
|
||||
|
||||
sprintf(time, "%ld", ast_tvnow().tv_sec);
|
||||
var = ast_variable_new("expiration_time <=", time, "");
|
||||
|
||||
ast_debug(4, "Woke up at %s Interval: %d\n", time, check_interval);
|
||||
|
||||
contacts = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "contact",
|
||||
AST_RETRIEVE_FLAG_MULTIPLE, var);
|
||||
|
||||
ast_variables_destroy(var);
|
||||
if (contacts) {
|
||||
ast_debug(3, "Expiring %d contacts\n\n", ao2_container_count(contacts));
|
||||
ao2_callback(contacts, OBJ_NODATA, expire_contact, NULL);
|
||||
ao2_ref(contacts, -1);
|
||||
}
|
||||
}
|
||||
|
||||
ao2_callback(contacts, OBJ_NODATA, contact_expiration_setup, NULL);
|
||||
ao2_ref(contacts, -1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int unload_observer_delete(void *obj, void *arg, int flags)
|
||||
static void expiration_global_loaded(const char *object_type)
|
||||
{
|
||||
struct contact_expiration *expiration = obj;
|
||||
check_interval = ast_sip_get_contact_expiration_check_interval();
|
||||
|
||||
AST_SCHED_DEL_UNREF(sched, expiration->sched, ao2_cleanup(expiration));
|
||||
return CMP_MATCH;
|
||||
/* Observer calls are serialized so this is safe without it's own lock */
|
||||
if (check_interval) {
|
||||
if (check_thread == AST_PTHREADT_NULL) {
|
||||
if (ast_pthread_create_background(&check_thread, NULL, check_expiration_thread, NULL)) {
|
||||
ast_log(LOG_ERROR, "Could not create thread for checking contact expiration.\n");
|
||||
return;
|
||||
}
|
||||
ast_debug(3, "Interval = %d, starting thread\n", check_interval);
|
||||
}
|
||||
} else {
|
||||
if (check_thread != AST_PTHREADT_NULL) {
|
||||
pthread_kill(check_thread, SIGURG);
|
||||
check_thread = AST_PTHREADT_NULL;
|
||||
ast_debug(3, "Interval = 0, shutting thread down\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*! \brief Observer which is used to update our interval when the global setting changes */
|
||||
static struct ast_sorcery_observer expiration_global_observer = {
|
||||
.loaded = expiration_global_loaded,
|
||||
};
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
ast_sorcery_observer_remove(ast_sip_get_sorcery(), "contact", &contact_expiration_observer);
|
||||
if (sched) {
|
||||
ao2_callback(contact_autoexpire, OBJ_MULTIPLE | OBJ_NODATA | OBJ_UNLINK,
|
||||
unload_observer_delete, NULL);
|
||||
ast_sched_context_destroy(sched);
|
||||
sched = NULL;
|
||||
if (check_thread != AST_PTHREADT_NULL) {
|
||||
pthread_kill(check_thread, SIGURG);
|
||||
check_thread = AST_PTHREADT_NULL;
|
||||
}
|
||||
ao2_cleanup(contact_autoexpire);
|
||||
contact_autoexpire = NULL;
|
||||
|
||||
ast_sorcery_observer_remove(ast_sip_get_sorcery(), "global", &expiration_global_observer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
CHECK_PJSIP_MODULE_LOADED();
|
||||
|
||||
contact_autoexpire = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK,
|
||||
CONTACT_AUTOEXPIRE_BUCKETS, contact_expiration_hash, contact_expiration_cmp);
|
||||
if (!contact_autoexpire) {
|
||||
ast_log(LOG_ERROR, "Could not create container for contact auto-expiration\n");
|
||||
return AST_MODULE_LOAD_FAILURE;
|
||||
}
|
||||
|
||||
if (!(sched = ast_sched_context_create())) {
|
||||
ast_log(LOG_ERROR, "Could not create scheduler for contact auto-expiration\n");
|
||||
unload_module();
|
||||
return AST_MODULE_LOAD_FAILURE;
|
||||
}
|
||||
|
||||
if (ast_sched_start_thread(sched)) {
|
||||
ast_log(LOG_ERROR, "Could not start scheduler thread for contact auto-expiration\n");
|
||||
unload_module();
|
||||
return AST_MODULE_LOAD_FAILURE;
|
||||
}
|
||||
|
||||
contact_expiration_initialize_existing();
|
||||
|
||||
if (ast_sorcery_observer_add(ast_sip_get_sorcery(), "contact", &contact_expiration_observer)) {
|
||||
ast_log(LOG_ERROR, "Could not add observer for notifications about contacts for contact auto-expiration\n");
|
||||
unload_module();
|
||||
return AST_MODULE_LOAD_FAILURE;
|
||||
}
|
||||
ast_sorcery_observer_add(ast_sip_get_sorcery(), "global", &expiration_global_observer);
|
||||
ast_sorcery_reload_object(ast_sip_get_sorcery(), "global");
|
||||
|
||||
return AST_MODULE_LOAD_SUCCESS;
|
||||
}
|
||||
|
@@ -63,65 +63,6 @@ static struct ast_sorcery_wizard astdb_object_wizard = {
|
||||
.close = sorcery_astdb_close,
|
||||
};
|
||||
|
||||
/*! \brief Helper function which converts from a sorcery object set to a json object */
|
||||
static struct ast_json *sorcery_objectset_to_json(const struct ast_variable *objectset)
|
||||
{
|
||||
struct ast_json *json = ast_json_object_create();
|
||||
const struct ast_variable *field;
|
||||
|
||||
for (field = objectset; field; field = field->next) {
|
||||
struct ast_json *value = ast_json_string_create(field->value);
|
||||
|
||||
if (!value) {
|
||||
ast_json_unref(json);
|
||||
return NULL;
|
||||
} else if (ast_json_object_set(json, field->name, value)) {
|
||||
ast_json_unref(json);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
/*! \brief Helper function which converts a json object to a sorcery object set */
|
||||
static struct ast_variable *sorcery_json_to_objectset(struct ast_json *json)
|
||||
{
|
||||
struct ast_json_iter *field;
|
||||
struct ast_variable *objset = NULL;
|
||||
|
||||
for (field = ast_json_object_iter(json); field; field = ast_json_object_iter_next(json, field)) {
|
||||
struct ast_json *value = ast_json_object_iter_value(field);
|
||||
struct ast_variable *variable = ast_variable_new(ast_json_object_iter_key(field), ast_json_string_get(value), "");
|
||||
|
||||
if (!variable) {
|
||||
ast_variables_destroy(objset);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
variable->next = objset;
|
||||
objset = variable;
|
||||
}
|
||||
|
||||
return objset;
|
||||
}
|
||||
|
||||
/*! \brief Helper function which compares two json objects and sees if they are equal, but only looks at the criteria provided */
|
||||
static int sorcery_json_equal(struct ast_json *object, struct ast_json *criteria)
|
||||
{
|
||||
struct ast_json_iter *field;
|
||||
|
||||
for (field = ast_json_object_iter(criteria); field; field = ast_json_object_iter_next(criteria, field)) {
|
||||
struct ast_json *object_field = ast_json_object_get(object, ast_json_object_iter_key(field));
|
||||
|
||||
if (!object_field || !ast_json_equal(object_field, ast_json_object_iter_value(field))) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int sorcery_astdb_create(const struct ast_sorcery *sorcery, void *data, void *object)
|
||||
{
|
||||
RAII_VAR(struct ast_json *, objset, ast_sorcery_objectset_json_create(sorcery, object), ast_json_unref);
|
||||
@@ -144,12 +85,11 @@ static void *sorcery_astdb_retrieve_fields_common(const struct ast_sorcery *sorc
|
||||
const char *prefix = data;
|
||||
char family[strlen(prefix) + strlen(type) + 2];
|
||||
RAII_VAR(struct ast_db_entry *, entries, NULL, ast_db_freetree);
|
||||
RAII_VAR(struct ast_json *, criteria, NULL, ast_json_unref);
|
||||
struct ast_db_entry *entry;
|
||||
|
||||
snprintf(family, sizeof(family), "%s/%s", prefix, type);
|
||||
|
||||
if (!(entries = ast_db_gettree(family, NULL)) || (fields && !(criteria = sorcery_objectset_to_json(fields)))) {
|
||||
if (!(entries = ast_db_gettree(family, NULL))) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -158,14 +98,21 @@ static void *sorcery_astdb_retrieve_fields_common(const struct ast_sorcery *sorc
|
||||
RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
|
||||
struct ast_json_error error;
|
||||
RAII_VAR(struct ast_variable *, objset, NULL, ast_variables_destroy);
|
||||
RAII_VAR(struct ast_variable *, existing, NULL, ast_variables_destroy);
|
||||
void *object = NULL;
|
||||
|
||||
if (!(json = ast_json_load_string(entry->data, &error))) {
|
||||
return NULL;
|
||||
} else if (criteria && !sorcery_json_equal(json, criteria)) {
|
||||
}
|
||||
if (ast_json_to_ast_variables(json, &existing) != AST_JSON_TO_AST_VARS_CODE_SUCCESS) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (fields && !ast_variable_lists_match(existing, fields, 0)) {
|
||||
continue;
|
||||
} else if (!(objset = sorcery_json_to_objectset(json)) ||
|
||||
!(object = ast_sorcery_alloc(sorcery, type, key)) ||
|
||||
}
|
||||
|
||||
if (!(object = ast_sorcery_alloc(sorcery, type, key)) ||
|
||||
ast_sorcery_objectset_apply(sorcery, object, objset)) {
|
||||
ao2_cleanup(object);
|
||||
return NULL;
|
||||
@@ -199,9 +146,11 @@ static void *sorcery_astdb_retrieve_id(const struct ast_sorcery *sorcery, void *
|
||||
|
||||
snprintf(family, sizeof(family), "%s/%s", prefix, type);
|
||||
|
||||
if (ast_db_get_allocated(family, id, &value) || !(json = ast_json_load_string(value, &error)) ||
|
||||
!(objset = sorcery_json_to_objectset(json)) || !(object = ast_sorcery_alloc(sorcery, type, id)) ||
|
||||
ast_sorcery_objectset_apply(sorcery, object, objset)) {
|
||||
if (ast_db_get_allocated(family, id, &value)
|
||||
|| !(json = ast_json_load_string(value, &error))
|
||||
|| (ast_json_to_ast_variables(json, &objset) != AST_JSON_TO_AST_VARS_CODE_SUCCESS)
|
||||
|| !(object = ast_sorcery_alloc(sorcery, type, id))
|
||||
|| ast_sorcery_objectset_apply(sorcery, object, objset)) {
|
||||
ast_debug(3, "Failed to retrieve object '%s' from astdb\n", id);
|
||||
ao2_cleanup(object);
|
||||
return NULL;
|
||||
@@ -310,10 +259,10 @@ static void sorcery_astdb_retrieve_regex(const struct ast_sorcery *sorcery, void
|
||||
|
||||
if (regexec(&expression, key, 0, NULL, 0)) {
|
||||
continue;
|
||||
} else if (!(json = ast_json_load_string(entry->data, &error)) ||
|
||||
!(objset = sorcery_json_to_objectset(json)) ||
|
||||
!(object = ast_sorcery_alloc(sorcery, type, key)) ||
|
||||
ast_sorcery_objectset_apply(sorcery, object, objset)) {
|
||||
} else if (!(json = ast_json_load_string(entry->data, &error))
|
||||
|| (ast_json_to_ast_variables(json, &objset) != AST_JSON_TO_AST_VARS_CODE_SUCCESS)
|
||||
|| !(object = ast_sorcery_alloc(sorcery, type, key))
|
||||
|| ast_sorcery_objectset_apply(sorcery, object, objset)) {
|
||||
regfree(&expression);
|
||||
return;
|
||||
}
|
||||
|
@@ -129,7 +129,6 @@ static int sorcery_config_fields_cmp(void *obj, void *arg, int flags)
|
||||
{
|
||||
const struct sorcery_config_fields_cmp_params *params = arg;
|
||||
RAII_VAR(struct ast_variable *, objset, NULL, ast_variables_destroy);
|
||||
RAII_VAR(struct ast_variable *, diff, NULL, ast_variables_destroy);
|
||||
|
||||
if (params->regex) {
|
||||
/* If a regular expression has been provided see if it matches, otherwise move on */
|
||||
@@ -139,11 +138,10 @@ static int sorcery_config_fields_cmp(void *obj, void *arg, int flags)
|
||||
return 0;
|
||||
} else if (params->fields &&
|
||||
(!(objset = ast_sorcery_objectset_create(params->sorcery, obj)) ||
|
||||
(ast_sorcery_changeset_create(objset, params->fields, &diff)) ||
|
||||
diff)) {
|
||||
(!ast_variable_lists_match(objset, params->fields, 0)))) {
|
||||
/* If we can't turn the object into an object set OR if differences exist between the fields
|
||||
* passed in and what are present on the object they are not a match.
|
||||
*/
|
||||
* passed in and what are present on the object they are not a match.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -197,6 +195,7 @@ static void sorcery_config_retrieve_multiple(const struct ast_sorcery *sorcery,
|
||||
if (!config_objects) {
|
||||
return;
|
||||
}
|
||||
|
||||
ao2_callback(config_objects, 0, sorcery_config_fields_cmp, ¶ms);
|
||||
}
|
||||
|
||||
|
@@ -120,7 +120,6 @@ static int sorcery_memory_fields_cmp(void *obj, void *arg, int flags)
|
||||
{
|
||||
const struct sorcery_memory_fields_cmp_params *params = arg;
|
||||
RAII_VAR(struct ast_variable *, objset, NULL, ast_variables_destroy);
|
||||
RAII_VAR(struct ast_variable *, diff, NULL, ast_variables_destroy);
|
||||
|
||||
if (params->regex) {
|
||||
/* If a regular expression has been provided see if it matches, otherwise move on */
|
||||
@@ -130,8 +129,7 @@ static int sorcery_memory_fields_cmp(void *obj, void *arg, int flags)
|
||||
return 0;
|
||||
} else if (params->fields &&
|
||||
(!(objset = ast_sorcery_objectset_create(params->sorcery, obj)) ||
|
||||
(ast_sorcery_changeset_create(objset, params->fields, &diff)) ||
|
||||
diff)) {
|
||||
(!ast_variable_lists_match(objset, params->fields, 0)))) {
|
||||
/* If we can't turn the object into an object set OR if differences exist between the fields
|
||||
* passed in and what are present on the object they are not a match.
|
||||
*/
|
||||
|
@@ -1251,8 +1251,7 @@ static int sorcery_memory_cache_fields_cmp(void *obj, void *arg, int flags)
|
||||
}
|
||||
return 0;
|
||||
} else if (params->fields &&
|
||||
(ast_sorcery_changeset_create(cached->objectset, params->fields, &diff) ||
|
||||
diff)) {
|
||||
(!ast_variable_lists_match(cached->objectset, params->fields, 0))) {
|
||||
/* If we can't turn the object into an object set OR if differences exist between the fields
|
||||
* passed in and what are present on the object they are not a match.
|
||||
*/
|
||||
|
@@ -40,6 +40,18 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
/*! \brief They key field used to store the unique identifier for the object */
|
||||
#define UUID_FIELD "id"
|
||||
|
||||
enum unqualified_fetch {
|
||||
UNQUALIFIED_FETCH_NO,
|
||||
UNQUALIFIED_FETCH_WARN,
|
||||
UNQUALIFIED_FETCH_YES,
|
||||
UNQUALIFIED_FETCH_ERROR,
|
||||
};
|
||||
|
||||
struct sorcery_config {
|
||||
enum unqualified_fetch fetch;
|
||||
char family[];
|
||||
};
|
||||
|
||||
static void *sorcery_realtime_open(const char *data);
|
||||
static int sorcery_realtime_create(const struct ast_sorcery *sorcery, void *data, void *object);
|
||||
static void *sorcery_realtime_retrieve_id(const struct ast_sorcery *sorcery, void *data, const char *type, const char *id);
|
||||
@@ -66,7 +78,7 @@ static struct ast_sorcery_wizard realtime_object_wizard = {
|
||||
|
||||
static int sorcery_realtime_create(const struct ast_sorcery *sorcery, void *data, void *object)
|
||||
{
|
||||
const char *family = data;
|
||||
struct sorcery_config *config = data;
|
||||
RAII_VAR(struct ast_variable *, fields, ast_sorcery_objectset_create(sorcery, object), ast_variables_destroy);
|
||||
struct ast_variable *id = ast_variable_new(UUID_FIELD, ast_sorcery_object_get_id(object), "");
|
||||
|
||||
@@ -79,7 +91,7 @@ static int sorcery_realtime_create(const struct ast_sorcery *sorcery, void *data
|
||||
id->next = fields;
|
||||
fields = id;
|
||||
|
||||
return (ast_store_realtime_fields(family, fields) <= 0) ? -1 : 0;
|
||||
return (ast_store_realtime_fields(config->family, fields) <= 0) ? -1 : 0;
|
||||
}
|
||||
|
||||
/*! \brief Internal helper function which returns a filtered objectset.
|
||||
@@ -149,12 +161,12 @@ static struct ast_variable *sorcery_realtime_filter_objectset(struct ast_variabl
|
||||
|
||||
static void *sorcery_realtime_retrieve_fields(const struct ast_sorcery *sorcery, void *data, const char *type, const struct ast_variable *fields)
|
||||
{
|
||||
const char *family = data;
|
||||
struct sorcery_config *config = data;
|
||||
RAII_VAR(struct ast_variable *, objectset, NULL, ast_variables_destroy);
|
||||
RAII_VAR(struct ast_variable *, id, NULL, ast_variables_destroy);
|
||||
void *object = NULL;
|
||||
|
||||
if (!(objectset = ast_load_realtime_fields(family, fields))) {
|
||||
if (!(objectset = ast_load_realtime_fields(config->family, fields))) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -178,7 +190,7 @@ static void *sorcery_realtime_retrieve_id(const struct ast_sorcery *sorcery, voi
|
||||
|
||||
static void sorcery_realtime_retrieve_multiple(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const struct ast_variable *fields)
|
||||
{
|
||||
const char *family = data;
|
||||
struct sorcery_config *config = data;
|
||||
RAII_VAR(struct ast_config *, rows, NULL, ast_config_destroy);
|
||||
RAII_VAR(struct ast_variable *, all, NULL, ast_variables_destroy);
|
||||
struct ast_category *row = NULL;
|
||||
@@ -186,6 +198,18 @@ static void sorcery_realtime_retrieve_multiple(const struct ast_sorcery *sorcery
|
||||
if (!fields) {
|
||||
char field[strlen(UUID_FIELD) + 6], value[2];
|
||||
|
||||
if (config->fetch == UNQUALIFIED_FETCH_NO) {
|
||||
return;
|
||||
}
|
||||
if (config->fetch == UNQUALIFIED_FETCH_ERROR) {
|
||||
ast_log(LOG_ERROR, "Unqualified fetch prevented on %s\n", config->family);
|
||||
return;
|
||||
}
|
||||
if (config->fetch == UNQUALIFIED_FETCH_WARN) {
|
||||
ast_log(LOG_WARNING, "Unqualified fetch attempted on %s\n", config->family);
|
||||
return;
|
||||
}
|
||||
|
||||
/* If no fields have been specified we want all rows, so trick realtime into doing it */
|
||||
snprintf(field, sizeof(field), "%s LIKE", UUID_FIELD);
|
||||
snprintf(value, sizeof(value), "%%");
|
||||
@@ -197,7 +221,7 @@ static void sorcery_realtime_retrieve_multiple(const struct ast_sorcery *sorcery
|
||||
fields = all;
|
||||
}
|
||||
|
||||
if (!(rows = ast_load_realtime_multientry_fields(family, fields))) {
|
||||
if (!(rows = ast_load_realtime_multientry_fields(config->family, fields))) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -221,16 +245,18 @@ static void sorcery_realtime_retrieve_regex(const struct ast_sorcery *sorcery, v
|
||||
char field[strlen(UUID_FIELD) + 6], value[strlen(regex) + 3];
|
||||
RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
|
||||
|
||||
/* The realtime API provides no direct ability to do regex so for now we support a limited subset using pattern matching */
|
||||
snprintf(field, sizeof(field), "%s LIKE", UUID_FIELD);
|
||||
if (regex[0] == '^') {
|
||||
snprintf(value, sizeof(value), "%s%%", regex + 1);
|
||||
} else {
|
||||
snprintf(value, sizeof(value), "%%%s%%", regex);
|
||||
}
|
||||
if (!ast_strlen_zero(regex)) {
|
||||
/* The realtime API provides no direct ability to do regex so for now we support a limited subset using pattern matching */
|
||||
snprintf(field, sizeof(field), "%s LIKE", UUID_FIELD);
|
||||
if (regex[0] == '^') {
|
||||
snprintf(value, sizeof(value), "%s%%", regex + 1);
|
||||
} else {
|
||||
snprintf(value, sizeof(value), "%%%s%%", regex);
|
||||
}
|
||||
|
||||
if (!(fields = ast_variable_new(field, value, ""))) {
|
||||
return;
|
||||
if (!(fields = ast_variable_new(field, value, ""))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
sorcery_realtime_retrieve_multiple(sorcery, data, type, objects, fields);
|
||||
@@ -238,31 +264,74 @@ static void sorcery_realtime_retrieve_regex(const struct ast_sorcery *sorcery, v
|
||||
|
||||
static int sorcery_realtime_update(const struct ast_sorcery *sorcery, void *data, void *object)
|
||||
{
|
||||
const char *family = data;
|
||||
struct sorcery_config *config = data;
|
||||
RAII_VAR(struct ast_variable *, fields, ast_sorcery_objectset_create(sorcery, object), ast_variables_destroy);
|
||||
|
||||
if (!fields) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (ast_update_realtime_fields(family, UUID_FIELD, ast_sorcery_object_get_id(object), fields) <= 0) ? -1 : 0;
|
||||
return (ast_update_realtime_fields(config->family, UUID_FIELD, ast_sorcery_object_get_id(object), fields) <= 0) ? -1 : 0;
|
||||
}
|
||||
|
||||
static int sorcery_realtime_delete(const struct ast_sorcery *sorcery, void *data, void *object)
|
||||
{
|
||||
const char *family = data;
|
||||
struct sorcery_config *config = data;
|
||||
|
||||
return (ast_destroy_realtime_fields(family, UUID_FIELD, ast_sorcery_object_get_id(object), NULL) <= 0) ? -1 : 0;
|
||||
return (ast_destroy_realtime_fields(config->family, UUID_FIELD, ast_sorcery_object_get_id(object), NULL) <= 0) ? -1 : 0;
|
||||
}
|
||||
|
||||
static void *sorcery_realtime_open(const char *data)
|
||||
{
|
||||
struct sorcery_config *config;
|
||||
char *tmp;
|
||||
char *family;
|
||||
char *option;
|
||||
|
||||
/* We require a prefix for family string generation, or else stuff could mix together */
|
||||
if (ast_strlen_zero(data) || !ast_realtime_is_mapping_defined(data)) {
|
||||
if (ast_strlen_zero(data)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ast_strdup(data);
|
||||
tmp = ast_strdupa(data);
|
||||
family = strsep(&tmp, ",");
|
||||
|
||||
if (!ast_realtime_is_mapping_defined(family)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
config = ast_calloc(1, sizeof(*config) + strlen(family) + 1);
|
||||
if (!config) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strcpy(config->family, family); /* Safe */
|
||||
config->fetch = UNQUALIFIED_FETCH_YES;
|
||||
|
||||
while ((option = strsep(&tmp, ","))) {
|
||||
char *name = strsep(&option, "=");
|
||||
char *value = option;
|
||||
|
||||
if (!strcasecmp(name, "allow_unqualified_fetch")) {
|
||||
if (ast_strlen_zero(value) || !strcasecmp(value, "yes")) {
|
||||
config->fetch = UNQUALIFIED_FETCH_YES;
|
||||
} else if (!strcasecmp(value, "no")) {
|
||||
config->fetch = UNQUALIFIED_FETCH_NO;
|
||||
} else if (!strcasecmp(value, "warn")) {
|
||||
config->fetch = UNQUALIFIED_FETCH_WARN;
|
||||
} else if (!strcasecmp(value, "error")) {
|
||||
config->fetch = UNQUALIFIED_FETCH_ERROR;
|
||||
} else {
|
||||
ast_log(LOG_ERROR, "Unrecognized value in %s:%s: '%s'\n", family, name, value);
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
ast_log(LOG_ERROR, "Unrecognized option in %s: '%s'\n", family, name);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
static void sorcery_realtime_close(void *data)
|
||||
|
Reference in New Issue
Block a user