ARI: Add ability to raise arbitrary User Events
[asterisk/asterisk.git] / main / sorcery.c
index cb24cae..9488dee 100644 (file)
@@ -58,6 +58,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 /*! \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;
 
@@ -258,22 +261,22 @@ 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 *object;
-    const char *key;
+       const struct ast_sorcery_wizard *object;
+       const char *key;
 
-    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);
+       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 */
@@ -304,6 +307,54 @@ static int sorcery_wizard_cmp(void *obj, void *arg, int flags)
        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;
+
+       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 */
 static void sorcery_exit(void)
 {
@@ -351,22 +402,22 @@ static int sorcery_instance_cmp(void *obj, void *arg, int flags)
 /*! \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;
+       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);
+       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)
@@ -461,22 +512,22 @@ 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 *object;
-    const char *key;
+       const struct ast_sorcery_object_type *object;
+       const char *key;
 
-    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);
+       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 */
@@ -529,6 +580,14 @@ struct ast_sorcery *__ast_sorcery_open(const char *module_name)
        }
 
        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:
@@ -572,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;
        }
@@ -628,10 +688,13 @@ 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_wizard *, wizard, ao2_find(wizards, name, OBJ_KEY), ao2_cleanup);
@@ -639,23 +702,35 @@ static int sorcery_apply_wizard_mapping(struct ast_sorcery *sorcery, const char
        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);
@@ -664,18 +739,22 @@ 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_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) {
@@ -698,8 +777,8 @@ int __ast_sorcery_apply_config(struct ast_sorcery *sorcery, const char *name, co
                }
 
                /* Any error immediately causes us to stop */
-               if (sorcery_apply_wizard_mapping(sorcery, type, module, wizard, data, caching)) {
-                       res = -1;
+               if (sorcery_apply_wizard_mapping(sorcery, type, module, wizard, data, caching) == AST_SORCERY_APPLY_FAIL) {
+                       res = AST_SORCERY_APPLY_FAIL;
                        break;
                }
        }
@@ -709,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);
 
        /* 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);
@@ -811,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, sorcery_fields_handler multiple_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);
@@ -840,7 +919,9 @@ 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) {
@@ -1778,10 +1859,82 @@ void ast_sorcery_observer_remove(const struct ast_sorcery *sorcery, const char *
                sorcery_observer_remove, cbs);
 }
 
-int ast_sorcery_object_id_compare(const void *obj_left, const void *obj_right, int flags)
+int ast_sorcery_object_id_sort(const void *obj, const void *arg, int flags)
 {
-       if (!obj_left || !obj_right) {
-               return 0;
+       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 strcmp(ast_sorcery_object_get_id(obj_left), ast_sorcery_object_get_id(obj_right));
+       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;
 }