ARI: Add ability to raise arbitrary User Events
[asterisk/asterisk.git] / main / sorcery.c
index 1bd55d4..9488dee 100644 (file)
@@ -34,6 +34,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/logger.h"
 #include "asterisk/sorcery.h"
 #include "asterisk/astobj2.h"
+#include "asterisk/format.h"
+#include "asterisk/format_cap.h"
 #include "asterisk/strings.h"
 #include "asterisk/config_options.h"
 #include "asterisk/netsock2.h"
@@ -41,6 +43,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/taskprocessor.h"
 #include "asterisk/threadpool.h"
 #include "asterisk/json.h"
+#include "asterisk/format_pref.h"
 
 /* To prevent DEBUG_FD_LEAKS from interfering with things we undef open and close */
 #undef open
@@ -52,8 +55,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 /*! \brief Number of buckets for types (should be prime for performance reasons) */
 #define TYPE_BUCKETS 53
 
-/*! \brief Maximum length of an object field name */
-#define MAX_OBJECT_FIELD 128
+/*! \brief Number of buckets for instances (should be prime for performance reasons) */
+#define INSTANCE_BUCKETS 17
+
+/*! \brief Number of buckets for object fields (should be prime for performance reasons) */
+#define OBJECT_FIELD_BUCKETS 29
 
 /*! \brief Thread pool for observers */
 static struct ast_threadpool *threadpool;
@@ -110,6 +116,9 @@ struct ast_sorcery_object_type {
 
        /*! \brief Serializer for observers */
        struct ast_taskprocessor *serializer;
+
+       /*! \brief Specifies if object type is reloadable or not */
+       unsigned int reloadable:1;
 };
 
 /*! \brief Structure for registered object type observer */
@@ -158,6 +167,8 @@ struct ast_sorcery_object_wizard {
 struct ast_sorcery {
        /*! \brief Container for known object types */
        struct ao2_container *types;
+       /*! \brief The name of the module owning this sorcery instance */
+       char module_name[0];
 };
 
 /*! \brief Structure for passing load/reload details */
@@ -173,7 +184,10 @@ struct sorcery_load_details {
 };
 
 /*! \brief Registered sorcery wizards */
-struct ao2_container *wizards;
+static struct ao2_container *wizards;
+
+/*! \brief Registered sorcery instances */
+static struct ao2_container *instances;
 
 static int int_handler_fn(const void *obj, const intptr_t *args, char **buf)
 {
@@ -217,11 +231,20 @@ static int chararray_handler_fn(const void *obj, const intptr_t *args, char **bu
        return !(*buf = ast_strdup(field)) ? -1 : 0;
 }
 
+static int codec_handler_fn(const void *obj, const intptr_t *args, char **buf)
+{
+       char tmp_buf[256];
+       struct ast_codec_pref *pref = (struct ast_codec_pref *)(obj + args[0]);
+       ast_codec_pref_string(pref, tmp_buf, sizeof(tmp_buf));
+       return !(*buf = ast_strdup(tmp_buf));
+}
+
 static sorcery_field_handler sorcery_field_default_handler(enum aco_option_type type)
 {
        switch(type) {
        case OPT_BOOL_T: return bool_handler_fn;
        case OPT_CHAR_ARRAY_T: return chararray_handler_fn;
+       case OPT_CODEC_T: return codec_handler_fn;
        case OPT_DOUBLE_T: return double_handler_fn;
        case OPT_INT_T: return int_handler_fn;
        case OPT_SOCKADDR_T: return sockaddr_handler_fn;
@@ -238,19 +261,98 @@ static sorcery_field_handler sorcery_field_default_handler(enum aco_option_type
 /*! \brief Hashing function for sorcery wizards */
 static int sorcery_wizard_hash(const void *obj, const int flags)
 {
-       const struct ast_sorcery_wizard *wizard = obj;
-       const char *name = obj;
+       const struct ast_sorcery_wizard *object;
+       const char *key;
 
-       return ast_str_hash(flags & OBJ_KEY ? name : wizard->name);
+       switch (flags & OBJ_SEARCH_MASK) {
+       case OBJ_SEARCH_KEY:
+               key = obj;
+               break;
+       case OBJ_SEARCH_OBJECT:
+               object = obj;
+               key = object->name;
+               break;
+       default:
+               ast_assert(0);
+               return 0;
+       }
+       return ast_str_hash(key);
 }
 
 /*! \brief Comparator function for sorcery wizards */
 static int sorcery_wizard_cmp(void *obj, void *arg, int flags)
 {
-       struct ast_sorcery_wizard *wizard1 = obj, *wizard2 = arg;
-       const char *name = arg;
+       const struct ast_sorcery_wizard *object_left = obj;
+       const struct ast_sorcery_wizard *object_right = arg;
+       const char *right_key = arg;
+       int cmp;
+
+       switch (flags & OBJ_SEARCH_MASK) {
+       case OBJ_SEARCH_OBJECT:
+               right_key = object_right->name;
+               /* Fall through */
+       case OBJ_SEARCH_KEY:
+               cmp = strcmp(object_left->name, right_key);
+               break;
+       case OBJ_SEARCH_PARTIAL_KEY:
+               cmp = strncmp(object_left->name, right_key, strlen(right_key));
+               break;
+       default:
+               cmp = 0;
+               break;
+       }
+       if (cmp) {
+               return 0;
+       }
+       return CMP_MATCH;
+}
+
+/*! \brief Hashing function for sorcery wizards */
+static int object_type_field_hash(const void *obj, const int flags)
+{
+       const struct ast_sorcery_object_field *object_field;
+       const char *key;
 
-       return !strcmp(wizard1->name, flags & OBJ_KEY ? name : wizard2->name) ? CMP_MATCH | CMP_STOP : 0;
+       switch (flags & OBJ_SEARCH_MASK) {
+       case OBJ_SEARCH_KEY:
+               key = obj;
+               break;
+       case OBJ_SEARCH_OBJECT:
+               object_field = obj;
+               key = object_field->name;
+               break;
+       default:
+               ast_assert(0);
+               return 0;
+       }
+       return ast_str_hash(key);
+}
+
+static int object_type_field_cmp(void *obj, void *arg, int flags)
+{
+       const struct ast_sorcery_object_field *field_left = obj;
+       const struct ast_sorcery_object_field *field_right = arg;
+       const char *right_key = arg;
+       int cmp;
+
+       switch (flags & OBJ_SEARCH_MASK) {
+       case OBJ_SEARCH_OBJECT:
+               right_key = field_right->name;
+               /* Fall through */
+       case OBJ_SEARCH_KEY:
+               cmp = strcmp(field_left->name, right_key);
+               break;
+       case OBJ_SEARCH_PARTIAL_KEY:
+               cmp = strncmp(field_left->name, right_key, strlen(right_key));
+               break;
+       default:
+               cmp = 0;
+               break;
+       }
+       if (cmp) {
+               return 0;
+       }
+       return CMP_MATCH;
 }
 
 /*! \brief Cleanup function */
@@ -264,6 +366,58 @@ static void sorcery_exit(void)
 static void sorcery_cleanup(void)
 {
        ao2_cleanup(wizards);
+       wizards = NULL;
+       ao2_cleanup(instances);
+       instances = NULL;
+}
+
+/*! \brief Compare function for sorcery instances */
+static int sorcery_instance_cmp(void *obj, void *arg, int flags)
+{
+       const struct ast_sorcery *object_left = obj;
+       const struct ast_sorcery *object_right = arg;
+       const char *right_key = arg;
+       int cmp;
+
+       switch (flags & OBJ_SEARCH_MASK) {
+       case OBJ_SEARCH_OBJECT:
+               right_key = object_right->module_name;
+               /* Fall through */
+       case OBJ_SEARCH_KEY:
+               cmp = strcmp(object_left->module_name, right_key);
+               break;
+       case OBJ_SEARCH_PARTIAL_KEY:
+               cmp = strncmp(object_left->module_name, right_key, strlen(right_key));
+               break;
+       default:
+               cmp = 0;
+               break;
+       }
+       if (cmp) {
+               return 0;
+       }
+       return CMP_MATCH;
+}
+
+/*! \brief Hashing function for sorcery instances */
+static int sorcery_instance_hash(const void *obj, const int flags)
+{
+       const struct ast_sorcery *object;
+       const char *key;
+
+       switch (flags & OBJ_SEARCH_MASK) {
+       case OBJ_SEARCH_KEY:
+               key = obj;
+               break;
+       case OBJ_SEARCH_OBJECT:
+               object = obj;
+               key = object->module_name;
+               break;
+       default:
+               ast_assert(0);
+               return 0;
+       }
+       return ast_str_hash(key);
 }
 
 int ast_sorcery_init(void)
@@ -287,6 +441,14 @@ int ast_sorcery_init(void)
                return -1;
        }
 
+       instances = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_RWLOCK, INSTANCE_BUCKETS,
+               sorcery_instance_hash, sorcery_instance_cmp);
+       if (!instances) {
+               sorcery_cleanup();
+               sorcery_exit();
+               return -1;
+       }
+
        ast_register_cleanup(sorcery_cleanup);
        ast_register_atexit(sorcery_exit);
 
@@ -350,37 +512,95 @@ static void sorcery_destructor(void *obj)
 /*! \brief Hashing function for sorcery types */
 static int sorcery_type_hash(const void *obj, const int flags)
 {
-       const struct ast_sorcery_object_type *type = obj;
-       const char *name = obj;
+       const struct ast_sorcery_object_type *object;
+       const char *key;
 
-       return ast_str_hash(flags & OBJ_KEY ? name : type->name);
+       switch (flags & OBJ_SEARCH_MASK) {
+       case OBJ_SEARCH_KEY:
+               key = obj;
+               break;
+       case OBJ_SEARCH_OBJECT:
+               object = obj;
+               key = object->name;
+               break;
+       default:
+               ast_assert(0);
+               return 0;
+       }
+       return ast_str_hash(key);
 }
 
 /*! \brief Comparator function for sorcery types */
 static int sorcery_type_cmp(void *obj, void *arg, int flags)
 {
-       struct ast_sorcery_object_type *type1 = obj, *type2 = arg;
-       const char *name = arg;
-
-       return !strcmp(type1->name, flags & OBJ_KEY ? name : type2->name) ? CMP_MATCH | CMP_STOP : 0;
+       const struct ast_sorcery_object_type *object_left = obj;
+       const struct ast_sorcery_object_type *object_right = arg;
+       const char *right_key = arg;
+       int cmp;
+
+       switch (flags & OBJ_SEARCH_MASK) {
+       case OBJ_SEARCH_OBJECT:
+               right_key = object_right->name;
+               /* Fall through */
+       case OBJ_SEARCH_KEY:
+               cmp = strcmp(object_left->name, right_key);
+               break;
+       case OBJ_SEARCH_PARTIAL_KEY:
+               cmp = strncmp(object_left->name, right_key, strlen(right_key));
+               break;
+       default:
+               cmp = 0;
+               break;
+       }
+       if (cmp) {
+               return 0;
+       }
+       return CMP_MATCH;
 }
 
-struct ast_sorcery *ast_sorcery_open(void)
+struct ast_sorcery *__ast_sorcery_open(const char *module_name)
 {
        struct ast_sorcery *sorcery;
 
-       if (!(sorcery = ao2_alloc(sizeof(*sorcery), sorcery_destructor))) {
-               return NULL;
+       ast_assert(module_name != NULL);
+
+       ao2_wrlock(instances);
+       if ((sorcery = ao2_find(instances, module_name, OBJ_SEARCH_KEY | OBJ_NOLOCK))) {
+               goto done;
+       }
+
+       if (!(sorcery = ao2_alloc(sizeof(*sorcery) + strlen(module_name) + 1, sorcery_destructor))) {
+               goto done;
        }
 
        if (!(sorcery->types = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_RWLOCK, TYPE_BUCKETS, sorcery_type_hash, sorcery_type_cmp))) {
                ao2_ref(sorcery, -1);
                sorcery = NULL;
+               goto done;
        }
 
+       strcpy(sorcery->module_name, module_name); /* Safe */
+
+       if (__ast_sorcery_apply_config(sorcery, module_name, module_name) == AST_SORCERY_APPLY_FAIL) {
+               ast_log(LOG_ERROR, "Error attempting to apply configuration %s to sorcery.\n", module_name);
+               ao2_cleanup(sorcery);
+               sorcery = NULL;
+               goto done;
+       }
+
+       ao2_link_flags(instances, sorcery, OBJ_NOLOCK);
+
+done:
+       ao2_unlock(instances);
        return sorcery;
 }
 
+/*! \brief Search function for sorcery instances */
+struct ast_sorcery *ast_sorcery_retrieve_by_module_name(const char *module_name)
+{
+       return ao2_find(instances, module_name, OBJ_SEARCH_KEY);
+}
+
 /*! \brief Destructor function for object types */
 static void sorcery_object_type_destructor(void *obj)
 {
@@ -411,12 +631,13 @@ static struct ast_sorcery_object_type *sorcery_object_type_alloc(const char *typ
        }
 
        /* Order matters for object wizards */
-       if (!(object_type->wizards = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, NULL))) {
+       if (!(object_type->wizards = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, sorcery_wizard_cmp))) {
                ao2_ref(object_type, -1);
                return NULL;
        }
 
-       if (!(object_type->fields = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, NULL))) {
+       if (!(object_type->fields = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, OBJECT_FIELD_BUCKETS,
+                                       object_type_field_hash, object_type_field_cmp))) {
                ao2_ref(object_type, -1);
                return NULL;
        }
@@ -467,34 +688,49 @@ static void sorcery_object_wizard_destructor(void *obj)
        if (object_wizard->wizard) {
                ast_module_unref(object_wizard->wizard->module);
        }
+
+       ao2_cleanup(object_wizard->wizard);
 }
 
 /*! \brief Internal function which creates an object type and adds a wizard mapping */
-static int sorcery_apply_wizard_mapping(struct ast_sorcery *sorcery, const char *type, const char *module, const char *name, const char *data, unsigned int caching)
+static enum ast_sorcery_apply_result sorcery_apply_wizard_mapping(struct ast_sorcery *sorcery,
+               const char *type, const char *module, const char *name, const char *data, unsigned int caching)
 {
-       RAII_VAR(struct ast_sorcery_object_type *, object_type,  ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
+       RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
        RAII_VAR(struct ast_sorcery_wizard *, wizard, ao2_find(wizards, name, OBJ_KEY), ao2_cleanup);
        RAII_VAR(struct ast_sorcery_object_wizard *, object_wizard, ao2_alloc(sizeof(*object_wizard), sorcery_object_wizard_destructor), ao2_cleanup);
        int created = 0;
 
        if (!wizard || !object_wizard) {
-               return -1;
+               return AST_SORCERY_APPLY_FAIL;
        }
 
        if (!object_type) {
                if (!(object_type = sorcery_object_type_alloc(type, module))) {
-                       return -1;
+                       return AST_SORCERY_APPLY_FAIL;
                }
                created = 1;
        }
 
+       if (!created) {
+               struct ast_sorcery_wizard *found;
+
+               found = ao2_find(object_type->wizards, wizard, OBJ_SEARCH_OBJECT);
+               if (found) {
+                       ast_debug(1, "Wizard %s already applied to object type %s\n",
+                                       wizard->name, object_type->name);
+                       ao2_cleanup(found);
+                       return AST_SORCERY_APPLY_DUPLICATE;
+               }
+       }
+
        if (wizard->open && !(object_wizard->data = wizard->open(data))) {
-               return -1;
+               return AST_SORCERY_APPLY_FAIL;
        }
 
        ast_module_ref(wizard->module);
 
-       object_wizard->wizard = wizard;
+       object_wizard->wizard = ao2_bump(wizard);
        object_wizard->caching = caching;
 
        ao2_link(object_type->wizards, object_wizard);
@@ -503,29 +739,35 @@ static int sorcery_apply_wizard_mapping(struct ast_sorcery *sorcery, const char
                ao2_link(sorcery->types, object_type);
        }
 
-       return 0;
+       return AST_SORCERY_APPLY_SUCCESS;
 }
 
-int __ast_sorcery_apply_config(struct ast_sorcery *sorcery, const char *name, const char *module)
+enum ast_sorcery_apply_result  __ast_sorcery_apply_config(struct ast_sorcery *sorcery, const char *name, const char *module)
 {
        struct ast_flags flags = { 0 };
        struct ast_config *config = ast_config_load2("sorcery.conf", "sorcery", flags);
        struct ast_variable *mapping;
-       int res = 0;
+       int res = AST_SORCERY_APPLY_SUCCESS;
 
-       if (!config || (config == CONFIG_STATUS_FILEMISSING) || (config == CONFIG_STATUS_FILEINVALID)) {
-               return -1;
+       if (!config) {
+               return AST_SORCERY_APPLY_NO_CONFIGURATION;
+       }
+
+       if (config == CONFIG_STATUS_FILEINVALID) {
+               return AST_SORCERY_APPLY_FAIL;
        }
 
        for (mapping = ast_variable_browse(config, name); mapping; mapping = mapping->next) {
                RAII_VAR(char *, mapping_name, ast_strdup(mapping->name), ast_free);
                RAII_VAR(char *, mapping_value, ast_strdup(mapping->value), ast_free);
-               char *options = mapping_name, *name = strsep(&options, "/");
-               char *data = mapping_value, *wizard = strsep(&data, ",");
+               char *options = mapping_name;
+               char *type = strsep(&options, "/");
+               char *data = mapping_value;
+               char *wizard = strsep(&data, ",");
                unsigned int caching = 0;
 
-               /* If no wizard exists just skip, nothing we can do */
-               if (ast_strlen_zero(wizard)) {
+               /* If no object type or wizard exists just skip, nothing we can do */
+               if (ast_strlen_zero(type) || ast_strlen_zero(wizard)) {
                        continue;
                }
 
@@ -535,7 +777,8 @@ int __ast_sorcery_apply_config(struct ast_sorcery *sorcery, const char *name, co
                }
 
                /* Any error immediately causes us to stop */
-               if ((res = sorcery_apply_wizard_mapping(sorcery, name, module, wizard, data, caching))) {
+               if (sorcery_apply_wizard_mapping(sorcery, type, module, wizard, data, caching) == AST_SORCERY_APPLY_FAIL) {
+                       res = AST_SORCERY_APPLY_FAIL;
                        break;
                }
        }
@@ -545,13 +788,13 @@ int __ast_sorcery_apply_config(struct ast_sorcery *sorcery, const char *name, co
        return res;
 }
 
-int __ast_sorcery_apply_default(struct ast_sorcery *sorcery, const char *type, const char *module, const char *name, const char *data)
+enum ast_sorcery_apply_result __ast_sorcery_apply_default(struct ast_sorcery *sorcery, const char *type, const char *module, const char *name, const char *data)
 {
-       RAII_VAR(struct ast_sorcery_object_type *, object_type,  ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
+       RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
 
        /* Defaults can not be added if any existing mapping exists */
        if (object_type) {
-               return -1;
+               return AST_SORCERY_APPLY_DEFAULT_UNNECESSARY;
        }
 
        return sorcery_apply_wizard_mapping(sorcery, type, module, name, data, 0);
@@ -575,7 +818,7 @@ static int sorcery_extended_fields_handler(const void *obj, struct ast_variable
        return 0;
 }
 
-int __ast_sorcery_object_register(struct ast_sorcery *sorcery, const char *type, unsigned int hidden, aco_type_item_alloc alloc, sorcery_transform_handler transform, sorcery_apply_handler apply)
+int __ast_sorcery_object_register(struct ast_sorcery *sorcery, const char *type, unsigned int hidden, unsigned int reloadable, aco_type_item_alloc alloc, sorcery_transform_handler transform, sorcery_apply_handler apply)
 {
        RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
 
@@ -589,6 +832,7 @@ int __ast_sorcery_object_register(struct ast_sorcery *sorcery, const char *type,
        object_type->type.item_alloc = alloc;
        object_type->type.hidden = hidden;
 
+       object_type->reloadable = reloadable;
        object_type->transform = transform;
        object_type->apply = apply;
        object_type->file->types[0] = &object_type->type;
@@ -646,7 +890,7 @@ int ast_sorcery_object_fields_register(struct ast_sorcery *sorcery, const char *
 }
 
 int __ast_sorcery_object_field_register(struct ast_sorcery *sorcery, const char *type, const char *name, const char *default_val, enum aco_option_type opt_type,
-                                       aco_option_handler config_handler, sorcery_field_handler sorcery_handler, unsigned int flags, unsigned int no_doc, size_t argc, ...)
+                                       aco_option_handler config_handler, sorcery_field_handler sorcery_handler, sorcery_fields_handler multiple_handler, unsigned int flags, unsigned int no_doc, unsigned int alias, size_t argc, ...)
 {
        RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
        RAII_VAR(struct ast_sorcery_object_field *, object_field, NULL, ao2_cleanup);
@@ -667,6 +911,7 @@ int __ast_sorcery_object_field_register(struct ast_sorcery *sorcery, const char
 
        ast_copy_string(object_field->name, name, sizeof(object_field->name));
        object_field->handler = sorcery_handler;
+       object_field->multiple_handler = multiple_handler;
 
        va_start(args, argc);
        for (pos = 0; pos < argc; pos++) {
@@ -674,20 +919,22 @@ int __ast_sorcery_object_field_register(struct ast_sorcery *sorcery, const char
        }
        va_end(args);
 
-       ao2_link(object_type->fields, object_field);
+       if (!alias) {
+               ao2_link(object_type->fields, object_field);
+       }
 
        /* TODO: Improve this hack */
        if (!argc) {
                __aco_option_register(object_type->info, name, ACO_EXACT, object_type->file->types, default_val, opt_type, config_handler, flags, no_doc, argc);
        } else if (argc == 1) {
                __aco_option_register(object_type->info, name, ACO_EXACT, object_type->file->types, default_val, opt_type, config_handler, flags, no_doc, argc,
-                                     object_field->args[0]);
+                       object_field->args[0]);
        } else if (argc == 2) {
                __aco_option_register(object_type->info, name, ACO_EXACT, object_type->file->types, default_val, opt_type, config_handler, flags, no_doc, argc,
-                                     object_field->args[0], object_field->args[1]);
+                       object_field->args[0], object_field->args[1]);
        } else if (argc == 3) {
                __aco_option_register(object_type->info, name, ACO_EXACT, object_type->file->types, default_val, opt_type, config_handler, flags, no_doc, argc,
-                                     object_field->args[0], object_field->args[1], object_field->args[2]);
+                       object_field->args[0], object_field->args[1], object_field->args[2]);
        } else {
                ast_assert(0); /* The hack... she does us no good for this */
        }
@@ -695,12 +942,26 @@ int __ast_sorcery_object_field_register(struct ast_sorcery *sorcery, const char
        return 0;
 }
 
+/*! \brief Retrieves whether or not the type is reloadable */
+static int sorcery_reloadable(const struct ast_sorcery *sorcery, const char *type)
+{
+       RAII_VAR(struct ast_sorcery_object_type *, object_type,
+                ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
+       return object_type && object_type->reloadable;
+}
+
 static int sorcery_wizard_load(void *obj, void *arg, int flags)
 {
        struct ast_sorcery_object_wizard *wizard = obj;
        struct sorcery_load_details *details = arg;
        void (*load)(void *data, const struct ast_sorcery *sorcery, const char *type);
 
+       if (details->reload && !sorcery_reloadable(details->sorcery, details->type)) {
+               ast_log(LOG_NOTICE, "Type '%s' is not reloadable, "
+                       "maintaining previous values\n", details->type);
+               return 0;
+       }
+
        load = !details->reload ? wizard->wizard->load : wizard->wizard->reload;
 
        if (load) {
@@ -836,14 +1097,48 @@ void ast_sorcery_ref(struct ast_sorcery *sorcery)
        ao2_ref(sorcery, +1);
 }
 
-struct ast_variable *ast_sorcery_objectset_create(const struct ast_sorcery *sorcery, const void *object)
+static struct ast_variable *get_single_field_as_var_list(const void *object, struct ast_sorcery_object_field *object_field)
+{
+       struct ast_variable *tmp = NULL;
+       char *buf = NULL;
+
+       if (!object_field->handler) {
+               return NULL;
+       }
+
+       if (!(object_field->handler(object, object_field->args, &buf))) {
+               tmp = ast_variable_new(object_field->name, S_OR(buf, ""), "");
+       }
+       ast_free(buf);
+
+       return tmp;
+}
+
+static struct ast_variable *get_multiple_fields_as_var_list(const void *object, struct ast_sorcery_object_field *object_field)
+{
+       struct ast_variable *tmp = NULL;
+
+       if (!object_field->multiple_handler) {
+               return NULL;
+       }
+
+       if (object_field->multiple_handler(object, &tmp)) {
+               ast_variables_destroy(tmp);
+               tmp = NULL;
+       }
+
+       return tmp;
+}
+
+struct ast_variable *ast_sorcery_objectset_create2(const struct ast_sorcery *sorcery,
+       const void *object,     enum ast_sorcery_field_handler_flags flags)
 {
        const struct ast_sorcery_object_details *details = object;
        RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, details->object->type, OBJ_KEY), ao2_cleanup);
        struct ao2_iterator i;
        struct ast_sorcery_object_field *object_field;
-       struct ast_variable *fields = NULL;
-       int res = 0;
+       struct ast_variable *head = NULL;
+       struct ast_variable *tail = NULL;
 
        if (!object_type) {
                return NULL;
@@ -851,41 +1146,42 @@ struct ast_variable *ast_sorcery_objectset_create(const struct ast_sorcery *sorc
 
        i = ao2_iterator_init(object_type->fields, 0);
 
-       for (; (object_field = ao2_iterator_next(&i)) && !res; ao2_ref(object_field, -1)) {
-               struct ast_variable *tmp = NULL;
+       for (; (object_field = ao2_iterator_next(&i)); ao2_ref(object_field, -1)) {
+               struct ast_variable *tmp;
 
-               if (object_field->multiple_handler) {
-                       if ((res = object_field->multiple_handler(object, &tmp))) {
-                               ast_variables_destroy(tmp);
+               switch (flags) {
+               case AST_HANDLER_PREFER_LIST:
+                       if ((tmp = get_multiple_fields_as_var_list(object, object_field)) ||
+                               (tmp = get_single_field_as_var_list(object, object_field))) {
+                               break;
                        }
-               } else if (object_field->handler) {
-                       char *buf = NULL;
-
-                       if ((res = object_field->handler(object, object_field->args, &buf)) ||
-                               !(tmp = ast_variable_new(object_field->name, S_OR(buf, ""), ""))) {
-                               res = -1;
+                       continue;
+               case AST_HANDLER_PREFER_STRING:
+                       if ((tmp = get_single_field_as_var_list(object, object_field)) ||
+                               (tmp = get_multiple_fields_as_var_list(object, object_field))) {
+                               break;
                        }
-
-                       ast_free(buf);
-               } else {
+                       continue;
+               case AST_HANDLER_ONLY_LIST:
+                       if ((tmp = get_multiple_fields_as_var_list(object, object_field))) {
+                               break;
+                       }
+                       continue;
+               case AST_HANDLER_ONLY_STRING:
+                       if ((tmp = get_single_field_as_var_list(object, object_field))) {
+                               break;
+                       }
+                       continue;
+               default:
                        continue;
                }
 
-               if (!res && tmp) {
-                       tmp->next = fields;
-                       fields = tmp;
-               }
+               tail = ast_variable_list_append_hint(&head, tail, tmp);
        }
 
        ao2_iterator_destroy(&i);
 
-       /* If any error occurs we destroy all fields handled before so a partial objectset is not returned */
-       if (res) {
-               ast_variables_destroy(fields);
-               fields = NULL;
-       }
-
-       return fields;
+       return head;
 }
 
 struct ast_json *ast_sorcery_objectset_json_create(const struct ast_sorcery *sorcery, const void *object)
@@ -903,21 +1199,23 @@ struct ast_json *ast_sorcery_objectset_json_create(const struct ast_sorcery *sor
 
        i = ao2_iterator_init(object_type->fields, 0);
 
-       for (; (object_field = ao2_iterator_next(&i)) && !res; ao2_ref(object_field, -1)) {
+       for (; !res && (object_field = ao2_iterator_next(&i)); ao2_ref(object_field, -1)) {
                if (object_field->multiple_handler) {
                        struct ast_variable *tmp = NULL;
                        struct ast_variable *field;
 
                        if ((res = object_field->multiple_handler(object, &tmp))) {
+                               ast_variables_destroy(tmp);
+                               ao2_ref(object_field, -1);
                                break;
                        }
 
                        for (field = tmp; field; field = field->next) {
                                struct ast_json *value = ast_json_string_create(field->value);
 
-                               if (value && ast_json_object_set(json, field->name, value)) {
-                                       ast_json_unref(value);
+                               if (!value || ast_json_object_set(json, field->name, value)) {
                                        res = -1;
+                                       break;
                                }
                        }
 
@@ -926,10 +1224,9 @@ struct ast_json *ast_sorcery_objectset_json_create(const struct ast_sorcery *sor
                        char *buf = NULL;
                        struct ast_json *value = NULL;
 
-                       if ((res = object_field->handler(object, object_field->args, &buf)) ||
-                               !(value = ast_json_string_create(buf)) ||
-                               ast_json_object_set(json, object_field->name, value)) {
-                               ast_json_unref(value);
+                       if ((res = object_field->handler(object, object_field->args, &buf))
+                               || !(value = ast_json_string_create(buf))
+                               || ast_json_object_set(json, object_field->name, value)) {
                                res = -1;
                        }
 
@@ -1065,7 +1362,7 @@ void *ast_sorcery_alloc(const struct ast_sorcery *sorcery, const char *type, con
        struct ast_sorcery_object_details *details;
 
        if (!object_type || !object_type->type.item_alloc ||
-           !(details = object_type->type.item_alloc(id))) {
+               !(details = object_type->type.item_alloc(id))) {
                return NULL;
        }
 
@@ -1178,7 +1475,7 @@ void *ast_sorcery_retrieve_by_id(const struct ast_sorcery *sorcery, const char *
        i = ao2_iterator_init(object_type->wizards, 0);
        for (; (wizard = ao2_iterator_next(&i)); ao2_ref(wizard, -1)) {
                if (wizard->wizard->retrieve_id &&
-                   !(object = wizard->wizard->retrieve_id(sorcery, wizard->data, object_type->name, id))) {
+                       !(object = wizard->wizard->retrieve_id(sorcery, wizard->data, object_type->name, id))) {
                        continue;
                }
 
@@ -1187,7 +1484,7 @@ void *ast_sorcery_retrieve_by_id(const struct ast_sorcery *sorcery, const char *
                ao2_ref(wizard, -1);
                break;
        }
-        ao2_iterator_destroy(&i);
+       ao2_iterator_destroy(&i);
 
        if (!cached && object) {
                ao2_callback(object_type->wizards, 0, sorcery_cache_create, object);
@@ -1449,7 +1746,14 @@ int ast_sorcery_delete(const struct ast_sorcery *sorcery, void *object)
 
 void ast_sorcery_unref(struct ast_sorcery *sorcery)
 {
-       ao2_cleanup(sorcery);
+       if (sorcery) {
+               /* One ref for what we just released, the other for the instances container. */
+               ao2_wrlock(instances);
+               if (ao2_ref(sorcery, -1) == 2) {
+                       ao2_unlink_flags(instances, sorcery, OBJ_NOLOCK);
+               }
+               ao2_unlock(instances);
+       }
 }
 
 const char *ast_sorcery_object_get_id(const void *object)
@@ -1510,6 +1814,7 @@ int ast_sorcery_observer_add(const struct ast_sorcery *sorcery, const char *type
 {
        RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
        struct ast_sorcery_object_type_observer *observer;
+       int res;
 
        if (!object_type || !callbacks) {
                return -1;
@@ -1520,10 +1825,13 @@ int ast_sorcery_observer_add(const struct ast_sorcery *sorcery, const char *type
        }
 
        observer->callbacks = callbacks;
-       ao2_link(object_type->observers, observer);
+       res = 0;
+       if (!ao2_link(object_type->observers, observer)) {
+               res = -1;
+       }
        ao2_ref(observer, -1);
 
-       return 0;
+       return res;
 }
 
 /*! \brief Internal callback function for removing an observer */
@@ -1534,13 +1842,99 @@ static int sorcery_observer_remove(void *obj, void *arg, int flags)
        return (observer->callbacks == arg) ? CMP_MATCH | CMP_STOP : 0;
 }
 
-void ast_sorcery_observer_remove(const struct ast_sorcery *sorcery, const char *type, struct ast_sorcery_observer *callbacks)
+void ast_sorcery_observer_remove(const struct ast_sorcery *sorcery, const char *type, const struct ast_sorcery_observer *callbacks)
 {
-       RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
+       RAII_VAR(struct ast_sorcery_object_type *, object_type, NULL, ao2_cleanup);
+       struct ast_sorcery_observer *cbs = (struct ast_sorcery_observer *) callbacks;/* Remove const for traversal. */
 
+       if (!sorcery) {
+               return;
+       }
+       object_type = ao2_find(sorcery->types, type, OBJ_KEY);
        if (!object_type) {
                return;
        }
 
-       ao2_callback(object_type->observers, OBJ_NODATA | OBJ_UNLINK, sorcery_observer_remove, callbacks);
+       ao2_callback(object_type->observers, OBJ_NODATA | OBJ_UNLINK,
+               sorcery_observer_remove, cbs);
+}
+
+int ast_sorcery_object_id_sort(const void *obj, const void *arg, int flags)
+{
+       const char *right_key = arg;
+       int cmp;
+
+       switch (flags & OBJ_SEARCH_MASK) {
+       case OBJ_SEARCH_OBJECT:
+               right_key = ast_sorcery_object_get_id(arg);
+               /* Fall through */
+       case OBJ_SEARCH_KEY:
+               cmp = strcmp(ast_sorcery_object_get_id(obj), right_key);
+               break;
+       case OBJ_SEARCH_PARTIAL_KEY:
+               cmp = strncmp(ast_sorcery_object_get_id(obj), right_key, strlen(right_key));
+               break;
+       default:
+               cmp = 0;
+               break;
+       }
+       return cmp;
+}
+
+int ast_sorcery_object_id_compare(void *obj, void *arg, int flags)
+{
+       const char *right_key = arg;
+       int cmp = 0;
+
+       switch (flags & OBJ_SEARCH_MASK) {
+       case OBJ_SEARCH_OBJECT:
+               right_key = ast_sorcery_object_get_id(arg);
+               /* Fall through */
+       case OBJ_SEARCH_KEY:
+               if (strcmp(ast_sorcery_object_get_id(obj), right_key) == 0) {
+                       cmp = CMP_MATCH | CMP_STOP;
+               }
+               break;
+       case OBJ_SEARCH_PARTIAL_KEY:
+               if (strncmp(ast_sorcery_object_get_id(obj), right_key, strlen(right_key)) == 0) {
+                       cmp = CMP_MATCH;
+               }
+               break;
+       default:
+               cmp = 0;
+               break;
+       }
+       return cmp;
+}
+
+int ast_sorcery_object_id_hash(const void *obj, int flags) {
+       if (flags & OBJ_SEARCH_OBJECT) {
+               return ast_str_hash(ast_sorcery_object_get_id(obj));
+       } else if (flags & OBJ_SEARCH_KEY) {
+               return ast_str_hash(obj);
+       }
+       return -1;
+}
+
+struct ast_sorcery_object_type *ast_sorcery_get_object_type(const struct ast_sorcery *sorcery,
+               const char *type)
+{
+       return ao2_find(sorcery->types, type, OBJ_SEARCH_KEY);
+}
+
+int ast_sorcery_is_object_field_registered(const struct ast_sorcery_object_type *object_type,
+               const char *field_name)
+{
+       struct ast_sorcery_object_field *object_field;
+       int res = 1;
+
+       ast_assert(object_type != NULL);
+
+       object_field = ao2_find(object_type->fields, field_name, OBJ_SEARCH_KEY);
+       if (!object_field) {
+               res = 0;
+       }
+
+       ao2_cleanup(object_field);
+       return res;
 }