From cf062303e35e4baa0dc2aa3dfb68b2c25b1ed2c2 Mon Sep 17 00:00:00 2001 From: Sean Bright Date: Thu, 9 Nov 2017 09:21:38 -0500 Subject: sorcery: Add ast_sorcery_retrieve_by_prefix() Some consumers of the sorcery API use ast_sorcery_retrieve_by_regex only so that they can anchor the potential match as a prefix and not because they truly need regular expressions. Rather than using regular expressions for simple prefix lookups, add a new operation - ast_sorcery_retrieve_by_prefix - that does them. Patches against 13 and 15 have a compatibility layer needed to maintain ABI that is not needed in master. Change-Id: I56f4e20ba1154bd52281f995c27a429a854f6a79 --- include/asterisk/sorcery.h | 37 +++++++++++++++++++++++++++++- main/sorcery.c | 51 ++++++++++++++++++++++++++++++++++++++++++ res/res_sorcery_astdb.c | 38 +++++++++++++++++++++++++++++++ res/res_sorcery_config.c | 31 +++++++++++++++++++++++++ res/res_sorcery_memory.c | 25 +++++++++++++++++++++ res/res_sorcery_memory_cache.c | 46 +++++++++++++++++++++++++++++++++++++ res/res_sorcery_realtime.c | 20 +++++++++++++++++ 7 files changed, 247 insertions(+), 1 deletion(-) diff --git a/include/asterisk/sorcery.h b/include/asterisk/sorcery.h index 0cb434766..73ed06078 100644 --- a/include/asterisk/sorcery.h +++ b/include/asterisk/sorcery.h @@ -312,6 +312,14 @@ struct ast_sorcery_wizard { /*! \brief Callback for closing a wizard */ void (*close)(void *data); + + /*! \brief Optional callback for retrieving multiple objects by matching their id with a prefix */ + void (*retrieve_prefix)(const struct ast_sorcery *sorcery, + void *data, + const char *type, + struct ao2_container *objects, + const char *prefix, + const size_t prefix_len); }; /*! \brief Interface for a sorcery object type observer */ @@ -363,10 +371,21 @@ int ast_sorcery_init(void); */ int __ast_sorcery_wizard_register(const struct ast_sorcery_wizard *interface, struct ast_module *module); +/*! + * \brief Register a sorcery wizard + * + * \param interface Pointer to a wizard interface + * \param module Pointer to the module implementing the interface + * + * \retval 0 success + * \retval -1 failure + */ +int __ast_sorcery_wizard_register_with_prefix(const struct ast_sorcery_wizard *interface, struct ast_module *module); + /*! * \brief See \ref __ast_sorcery_wizard_register() */ -#define ast_sorcery_wizard_register(interface) __ast_sorcery_wizard_register(interface, ast_module_info ? ast_module_info->self : NULL) +#define ast_sorcery_wizard_register(interface) __ast_sorcery_wizard_register_with_prefix(interface, ast_module_info ? ast_module_info->self : NULL) /*! * \brief Unregister a sorcery wizard @@ -1217,6 +1236,22 @@ void *ast_sorcery_retrieve_by_fields(const struct ast_sorcery *sorcery, const ch */ struct ao2_container *ast_sorcery_retrieve_by_regex(const struct ast_sorcery *sorcery, const char *type, const char *regex); +/*! + * \brief Retrieve multiple objects whose id begins with the specified prefix + * \since 13.19.0 + * + * \param sorcery Pointer to a sorcery structure + * \param type Type of object to retrieve + * \param prefix Object id prefix + * \param prefix_len The length of prefix in bytes + * + * \retval non-NULL if error occurs + * \retval NULL success + * + * \note The prefix is matched in a case sensitive manner. + */ +struct ao2_container *ast_sorcery_retrieve_by_prefix(const struct ast_sorcery *sorcery, const char *type, const char *prefix, const size_t prefix_len); + /*! * \brief Update an object * diff --git a/main/sorcery.c b/main/sorcery.c index 1bdf2c2f8..cb0aff538 100644 --- a/main/sorcery.c +++ b/main/sorcery.c @@ -543,6 +543,27 @@ static void sorcery_internal_wizard_destructor(void *obj) } int __ast_sorcery_wizard_register(const struct ast_sorcery_wizard *interface, struct ast_module *module) +{ + struct ast_sorcery_wizard compat = { + .name = interface->name, + .open = interface->open, + .load = interface->load, + .reload = interface->reload, + .create = interface->create, + .retrieve_id = interface->retrieve_id, + .retrieve_regex = interface->retrieve_regex, + .retrieve_fields = interface->retrieve_fields, + .retrieve_multiple = interface->retrieve_multiple, + .update = interface->update, + .delete = interface->delete, + .close = interface->close, + .retrieve_prefix = NULL, + }; + + return __ast_sorcery_wizard_register_with_prefix(&compat, module); +} + +int __ast_sorcery_wizard_register_with_prefix(const struct ast_sorcery_wizard *interface, struct ast_module *module) { struct ast_sorcery_internal_wizard *wizard; int res = -1; @@ -1982,6 +2003,36 @@ struct ao2_container *ast_sorcery_retrieve_by_regex(const struct ast_sorcery *so return objects; } +struct ao2_container *ast_sorcery_retrieve_by_prefix(const struct ast_sorcery *sorcery, const char *type, const char *prefix, const size_t prefix_len) +{ + RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup); + struct ao2_container *objects; + int i; + + if (!object_type || !(objects = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, NULL))) { + return NULL; + } + + AST_VECTOR_RW_RDLOCK(&object_type->wizards); + for (i = 0; i < AST_VECTOR_SIZE(&object_type->wizards); i++) { + struct ast_sorcery_object_wizard *wizard = + AST_VECTOR_GET(&object_type->wizards, i); + + if (!wizard->wizard->callbacks.retrieve_prefix) { + continue; + } + + wizard->wizard->callbacks.retrieve_prefix(sorcery, wizard->data, object_type->name, objects, prefix, prefix_len); + + if (wizard->caching && ao2_container_count(objects)) { + break; + } + } + AST_VECTOR_RW_UNLOCK(&object_type->wizards); + + return objects; +} + /*! \brief Internal function which returns if the wizard has created the object */ static int sorcery_wizard_create(const struct ast_sorcery_object_wizard *object_wizard, const struct sorcery_details *details) { diff --git a/res/res_sorcery_astdb.c b/res/res_sorcery_astdb.c index 1aec0be95..fee857525 100644 --- a/res/res_sorcery_astdb.c +++ b/res/res_sorcery_astdb.c @@ -46,6 +46,7 @@ static void *sorcery_astdb_retrieve_fields(const struct ast_sorcery *sorcery, vo static void sorcery_astdb_retrieve_multiple(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const struct ast_variable *fields); static void sorcery_astdb_retrieve_regex(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *regex); +static void sorcery_astdb_retrieve_prefix(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *prefix, const size_t prefix_len); static int sorcery_astdb_update(const struct ast_sorcery *sorcery, void *data, void *object); static int sorcery_astdb_delete(const struct ast_sorcery *sorcery, void *data, void *object); static void sorcery_astdb_close(void *data); @@ -58,6 +59,7 @@ static struct ast_sorcery_wizard astdb_object_wizard = { .retrieve_fields = sorcery_astdb_retrieve_fields, .retrieve_multiple = sorcery_astdb_retrieve_multiple, .retrieve_regex = sorcery_astdb_retrieve_regex, + .retrieve_prefix = sorcery_astdb_retrieve_prefix, .update = sorcery_astdb_update, .delete = sorcery_astdb_delete, .close = sorcery_astdb_close, @@ -329,6 +331,42 @@ static void sorcery_astdb_retrieve_regex(const struct ast_sorcery *sorcery, void regfree(&expression); } +static void sorcery_astdb_retrieve_prefix(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *prefix, const size_t prefix_len) +{ + const char *family_prefix = data; + size_t family_len = strlen(family_prefix) + strlen(type) + 1; /* +1 for slash delimiter */ + char family[family_len + 1]; + char tree[prefix_len + sizeof("%")]; + RAII_VAR(struct ast_db_entry *, entries, NULL, ast_db_freetree); + struct ast_db_entry *entry; + + snprintf(tree, sizeof(tree), "%.*s%%", (int) prefix_len, prefix); + snprintf(family, sizeof(family), "%s/%s", family_prefix, type); + + if (!(entries = ast_db_gettree(family, tree))) { + return; + } + + for (entry = entries; entry; entry = entry->next) { + /* The key in the entry includes the family, so we need to strip it out */ + const char *key = entry->key + family_len + 2; + RAII_VAR(struct ast_json *, json, NULL, ast_json_unref); + struct ast_json_error error; + RAII_VAR(void *, object, NULL, ao2_cleanup); + RAII_VAR(struct ast_variable *, objset, NULL, ast_variables_destroy); + + if (!(json = ast_json_load_string(entry->data, &error)) + || (ast_json_to_ast_variables(json, &objset) != AST_JSON_TO_AST_VARS_CODE_SUCCESS) + || !(objset = sorcery_astdb_filter_objectset(objset, sorcery, type)) + || !(object = ast_sorcery_alloc(sorcery, type, key)) + || ast_sorcery_objectset_apply(sorcery, object, objset)) { + return; + } + + ao2_link(objects, object); + } +} + static int sorcery_astdb_update(const struct ast_sorcery *sorcery, void *data, void *object) { const char *prefix = data; diff --git a/res/res_sorcery_config.c b/res/res_sorcery_config.c index 7fd7e769a..b02000eab 100644 --- a/res/res_sorcery_config.c +++ b/res/res_sorcery_config.c @@ -73,6 +73,12 @@ struct sorcery_config_fields_cmp_params { /*! \brief Regular expression for checking object id */ regex_t *regex; + /*! \brief Prefix for matching object id */ + const char *prefix; + + /*! \brief Prefix length in bytes for matching object id */ + const size_t prefix_len; + /*! \brief Optional container to put object into */ struct ao2_container *container; }; @@ -85,6 +91,7 @@ static void *sorcery_config_retrieve_fields(const struct ast_sorcery *sorcery, v static void sorcery_config_retrieve_multiple(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const struct ast_variable *fields); static void sorcery_config_retrieve_regex(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *regex); +static void sorcery_config_retrieve_prefix(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *prefix, const size_t prefix_len); static void sorcery_config_close(void *data); static struct ast_sorcery_wizard config_object_wizard = { @@ -96,6 +103,7 @@ static struct ast_sorcery_wizard config_object_wizard = { .retrieve_fields = sorcery_config_retrieve_fields, .retrieve_multiple = sorcery_config_retrieve_multiple, .retrieve_regex = sorcery_config_retrieve_regex, + .retrieve_prefix = sorcery_config_retrieve_prefix, .close = sorcery_config_close, }; @@ -120,6 +128,11 @@ static int sorcery_config_fields_cmp(void *obj, void *arg, int flags) ao2_link(params->container, obj); } return 0; + } else if (params->prefix) { + if (!strncmp(params->prefix, ast_sorcery_object_get_id(obj), params->prefix_len)) { + ao2_link(params->container, obj); + } + return 0; } else if (params->fields && (!(objset = ast_sorcery_objectset_create(params->sorcery, obj)) || (!ast_variable_lists_match(objset, params->fields, 0)))) { @@ -208,6 +221,24 @@ static void sorcery_config_retrieve_regex(const struct ast_sorcery *sorcery, voi regfree(&expression); } +static void sorcery_config_retrieve_prefix(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *prefix, const size_t prefix_len) +{ + struct sorcery_config *config = data; + RAII_VAR(struct ao2_container *, config_objects, ao2_global_obj_ref(config->objects), ao2_cleanup); + struct sorcery_config_fields_cmp_params params = { + .sorcery = sorcery, + .container = objects, + .prefix = prefix, + .prefix_len = prefix_len, + }; + + if (!config_objects) { + return; + } + + ao2_callback(config_objects, OBJ_NODATA | OBJ_MULTIPLE, sorcery_config_fields_cmp, ¶ms); +} + /*! \brief Internal function which determines if criteria has been met for considering an object set applicable */ static int sorcery_is_criteria_met(struct ast_variable *objset, struct ast_variable *criteria) { diff --git a/res/res_sorcery_memory.c b/res/res_sorcery_memory.c index b2f05591b..a05f05d9e 100644 --- a/res/res_sorcery_memory.c +++ b/res/res_sorcery_memory.c @@ -48,6 +48,7 @@ static void *sorcery_memory_retrieve_fields(const struct ast_sorcery *sorcery, v static void sorcery_memory_retrieve_multiple(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const struct ast_variable *fields); static void sorcery_memory_retrieve_regex(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *regex); +static void sorcery_memory_retrieve_prefix(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *prefix, const size_t prefix_len); static int sorcery_memory_update(const struct ast_sorcery *sorcery, void *data, void *object); static int sorcery_memory_delete(const struct ast_sorcery *sorcery, void *data, void *object); static void sorcery_memory_close(void *data); @@ -60,6 +61,7 @@ static struct ast_sorcery_wizard memory_object_wizard = { .retrieve_fields = sorcery_memory_retrieve_fields, .retrieve_multiple = sorcery_memory_retrieve_multiple, .retrieve_regex = sorcery_memory_retrieve_regex, + .retrieve_prefix = sorcery_memory_retrieve_prefix, .update = sorcery_memory_update, .delete = sorcery_memory_delete, .close = sorcery_memory_close, @@ -76,6 +78,12 @@ struct sorcery_memory_fields_cmp_params { /*! \brief Regular expression for checking object id */ regex_t *regex; + /*! \brief Prefix for matching object id */ + const char *prefix; + + /*! \brief Prefix length in bytes for matching object id */ + const size_t prefix_len; + /*! \brief Optional container to put object into */ struct ao2_container *container; }; @@ -127,6 +135,11 @@ static int sorcery_memory_fields_cmp(void *obj, void *arg, int flags) ao2_link(params->container, obj); } return 0; + } else if (params->prefix) { + if (!strncmp(params->prefix, ast_sorcery_object_get_id(obj), params->prefix_len)) { + ao2_link(params->container, obj); + } + return 0; } else if (params->fields && (!(objset = ast_sorcery_objectset_create(params->sorcery, obj)) || (!ast_variable_lists_match(objset, params->fields, 0)))) { @@ -200,6 +213,18 @@ static void sorcery_memory_retrieve_regex(const struct ast_sorcery *sorcery, voi regfree(&expression); } +static void sorcery_memory_retrieve_prefix(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *prefix, const size_t prefix_len) +{ + struct sorcery_memory_fields_cmp_params params = { + .sorcery = sorcery, + .container = objects, + .prefix = prefix, + .prefix_len = prefix_len, + }; + + ao2_callback(data, 0, sorcery_memory_fields_cmp, ¶ms); +} + static int sorcery_memory_update(const struct ast_sorcery *sorcery, void *data, void *object) { RAII_VAR(void *, existing, NULL, ao2_cleanup); diff --git a/res/res_sorcery_memory_cache.c b/res/res_sorcery_memory_cache.c index bf2347ccd..30e6ef04b 100644 --- a/res/res_sorcery_memory_cache.c +++ b/res/res_sorcery_memory_cache.c @@ -185,6 +185,10 @@ struct sorcery_memory_cache_fields_cmp_params { const struct ast_variable *fields; /*! \brief Regular expression for checking object id */ regex_t *regex; + /*! \brief Prefix for matching object id */ + const char *prefix; + /*! \brief Prefix length in bytes for matching object id */ + const size_t prefix_len; /*! \brief Optional container to put object into */ struct ao2_container *container; }; @@ -201,6 +205,8 @@ static void sorcery_memory_cache_retrieve_multiple(const struct ast_sorcery *sor struct ao2_container *objects, const struct ast_variable *fields); static void sorcery_memory_cache_retrieve_regex(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *regex); +static void sorcery_memory_cache_retrieve_prefix(const struct ast_sorcery *sorcery, void *data, const char *type, + struct ao2_container *objects, const char *prefix, const size_t prefix_len); static int sorcery_memory_cache_delete(const struct ast_sorcery *sorcery, void *data, void *object); static void sorcery_memory_cache_close(void *data); @@ -216,6 +222,7 @@ static struct ast_sorcery_wizard memory_cache_object_wizard = { .retrieve_fields = sorcery_memory_cache_retrieve_fields, .retrieve_multiple = sorcery_memory_cache_retrieve_multiple, .retrieve_regex = sorcery_memory_cache_retrieve_regex, + .retrieve_prefix = sorcery_memory_cache_retrieve_prefix, .close = sorcery_memory_cache_close, }; @@ -1253,6 +1260,11 @@ static int sorcery_memory_cache_fields_cmp(void *obj, void *arg, int flags) ao2_link(params->container, cached->object); } return 0; + } else if (params->prefix) { + if (!strncmp(params->prefix, ast_sorcery_object_get_id(cached->object), params->prefix_len)) { + ao2_link(params->container, cached->object); + } + return 0; } else if (params->fields && (!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 @@ -1376,6 +1388,40 @@ static void sorcery_memory_cache_retrieve_regex(const struct ast_sorcery *sorcer } } +/*! + * \internal + * \brief Callback function to retrieve multiple objects whose id matches a prefix + * + * \param sorcery The sorcery instance + * \param data The sorcery memory cache + * \param type The type of the object to retrieve + * \param objects Container to place the objects into + * \param prefix Prefix to match against the object id + */ +static void sorcery_memory_cache_retrieve_prefix(const struct ast_sorcery *sorcery, void *data, const char *type, + struct ao2_container *objects, const char *prefix, const size_t prefix_len) +{ + struct sorcery_memory_cache *cache = data; + struct sorcery_memory_cache_fields_cmp_params params = { + .sorcery = sorcery, + .cache = cache, + .container = objects, + .prefix = prefix, + .prefix_len = prefix_len, + }; + + if (is_passthru_update() || !cache->full_backend_cache) { + return; + } + + memory_cache_full_update(sorcery, type, cache); + ao2_callback(cache->objects, 0, sorcery_memory_cache_fields_cmp, ¶ms); + + if (ao2_container_count(objects)) { + memory_cache_stale_check(sorcery, cache); + } +} + /*! * \internal * \brief Callback function to finish configuring the memory cache diff --git a/res/res_sorcery_realtime.c b/res/res_sorcery_realtime.c index 138d6ea95..1c52eb90f 100644 --- a/res/res_sorcery_realtime.c +++ b/res/res_sorcery_realtime.c @@ -59,6 +59,8 @@ static void *sorcery_realtime_retrieve_fields(const struct ast_sorcery *sorcery, 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); static void sorcery_realtime_retrieve_regex(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *regex); +static void sorcery_realtime_retrieve_prefix(const struct ast_sorcery *sorcery, void *data, const char *type, + struct ao2_container *objects, const char *prefix, const size_t prefix_len); static int sorcery_realtime_update(const struct ast_sorcery *sorcery, void *data, void *object); static int sorcery_realtime_delete(const struct ast_sorcery *sorcery, void *data, void *object); static void sorcery_realtime_close(void *data); @@ -71,6 +73,7 @@ static struct ast_sorcery_wizard realtime_object_wizard = { .retrieve_fields = sorcery_realtime_retrieve_fields, .retrieve_multiple = sorcery_realtime_retrieve_multiple, .retrieve_regex = sorcery_realtime_retrieve_regex, + .retrieve_prefix = sorcery_realtime_retrieve_prefix, .update = sorcery_realtime_update, .delete = sorcery_realtime_delete, .close = sorcery_realtime_close, @@ -262,6 +265,23 @@ static void sorcery_realtime_retrieve_regex(const struct ast_sorcery *sorcery, v sorcery_realtime_retrieve_multiple(sorcery, data, type, objects, fields); } +static void sorcery_realtime_retrieve_prefix(const struct ast_sorcery *sorcery, void *data, const char *type, + struct ao2_container *objects, const char *prefix, const size_t prefix_len) +{ + char field[strlen(UUID_FIELD) + 6], value[prefix_len + 2]; + RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy); + + if (prefix_len) { + snprintf(field, sizeof(field), "%s LIKE", UUID_FIELD); + snprintf(value, sizeof(value), "%.*s%%", (int) prefix_len, prefix); + if (!(fields = ast_variable_new(field, value, ""))) { + return; + } + } + + sorcery_realtime_retrieve_multiple(sorcery, data, type, objects, fields); +} + static int sorcery_realtime_update(const struct ast_sorcery *sorcery, void *data, void *object) { struct sorcery_config *config = data; -- cgit v1.2.3