Add support for observers and JSON objectset creation to sorcery.
[asterisk/asterisk.git] / main / sorcery.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2012 - 2013, Digium, Inc.
5  *
6  * Joshua Colp <jcolp@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Sorcery Data Access Layer API
22  *
23  * \author Joshua Colp <jcolp@digium.com>
24  */
25
26 /*** MODULEINFO
27         <support_level>core</support_level>
28  ***/
29
30 #include "asterisk.h"
31
32 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
33
34 #include "asterisk/logger.h"
35 #include "asterisk/sorcery.h"
36 #include "asterisk/astobj2.h"
37 #include "asterisk/strings.h"
38 #include "asterisk/config_options.h"
39 #include "asterisk/netsock2.h"
40 #include "asterisk/module.h"
41 #include "asterisk/taskprocessor.h"
42 #include "asterisk/threadpool.h"
43 #include "asterisk/json.h"
44
45 /* To prevent DEBUG_FD_LEAKS from interfering with things we undef open and close */
46 #undef open
47 #undef close
48
49 /*! \brief Number of buckets for wizards (should be prime for performance reasons) */
50 #define WIZARD_BUCKETS 7
51
52 /*! \brief Number of buckets for types (should be prime for performance reasons) */
53 #define TYPE_BUCKETS 53
54
55 /*! \brief Maximum length of an object field name */
56 #define MAX_OBJECT_FIELD 128
57
58 /*! \brief Thread pool for observers */
59 static struct ast_threadpool *threadpool;
60
61 /*! \brief Structure for registered object type */
62 struct ast_sorcery_object_type {
63         /*! \brief Unique name of the object type */
64         char name[MAX_OBJECT_TYPE];
65
66         /*! \brief Optional transformation callback */
67         sorcery_transform_handler transform;
68
69         /*! \brief Optional object set apply callback */
70         sorcery_apply_handler apply;
71
72         /*! \brief Optional object copy callback */
73         sorcery_copy_handler copy;
74
75         /*! \brief Optional object diff callback */
76         sorcery_diff_handler diff;
77
78         /*! \brief Wizard instances */
79         struct ao2_container *wizards;
80
81         /*! \brief Object fields */
82         struct ao2_container *fields;
83
84         /*! \brief Configuration framework general information */
85         struct aco_info *info;
86
87         /*! \brief Configuration framework file information */
88         struct aco_file *file;
89
90         /*! \brief Type details */
91         struct aco_type type;
92
93         /*! \brief Observers */
94         struct ao2_container *observers;
95
96         /*! \brief Serializer for observers */
97         struct ast_taskprocessor *serializer;
98 };
99
100 /*! \brief Structure for registered object type observer */
101 struct ast_sorcery_object_type_observer {
102         /*! \brief Pointer to the observer implementation */
103         const struct ast_sorcery_observer *callbacks;
104 };
105
106 /*! \brief Structure used for observer invocations */
107 struct sorcery_observer_invocation {
108         /*! \brief Pointer to the object type */
109         struct ast_sorcery_object_type *object_type;
110
111         /*! \brief Pointer to the object */
112         void *object;
113 };
114
115 /*! \brief Structure for registered object field */
116 struct ast_sorcery_object_field {
117         /*! \brief Name of the field */
118         char name[MAX_OBJECT_FIELD];
119
120         /*! \brief Callback function for translation of a single value */
121         sorcery_field_handler handler;
122
123         /*! \brief Callback function for translation of multiple values */
124         sorcery_fields_handler multiple_handler;
125
126         /*! \brief Position of the field */
127         intptr_t args[];
128 };
129
130 /*! \brief Structure for a wizard instance which operates on objects */
131 struct ast_sorcery_object_wizard {
132         /*! \brief Wizard interface itself */
133         struct ast_sorcery_wizard *wizard;
134
135         /*! \brief Unique data for the wizard */
136         void *data;
137
138         /*! \brief Wizard is acting as an object cache */
139         unsigned int caching:1;
140 };
141
142 /*! \brief Full structure for sorcery */
143 struct ast_sorcery {
144         /*! \brief Container for known object types */
145         struct ao2_container *types;
146 };
147
148 /*! \brief Structure for passing load/reload details */
149 struct sorcery_load_details {
150         /*! \brief Sorcery structure in use */
151         const struct ast_sorcery *sorcery;
152
153         /*! \brief Type of object being loaded */
154         const char *type;
155
156         /*! \brief Whether this is a reload or not */
157         unsigned int reload:1;
158 };
159
160 /*! \brief Registered sorcery wizards */
161 struct ao2_container *wizards;
162
163 static int int_handler_fn(const void *obj, const intptr_t *args, char **buf)
164 {
165         int *field = (int *)(obj + args[0]);
166         return (ast_asprintf(buf, "%d", *field) < 0) ? -1 : 0;
167 }
168
169 static int uint_handler_fn(const void *obj, const intptr_t *args, char **buf)
170 {
171         unsigned int *field = (unsigned int *)(obj + args[0]);
172         return (ast_asprintf(buf, "%u", *field) < 0) ? -1 : 0;
173 }
174
175 static int double_handler_fn(const void *obj, const intptr_t *args, char **buf)
176 {
177         double *field = (double *)(obj + args[0]);
178         return (ast_asprintf(buf, "%f", *field) < 0) ? -1 : 0;
179 }
180
181 static int stringfield_handler_fn(const void *obj, const intptr_t *args, char **buf)
182 {
183         ast_string_field *field = (const char **)(obj + args[0]);
184         return !(*buf = ast_strdup(*field)) ? -1 : 0;
185 }
186
187 static int bool_handler_fn(const void *obj, const intptr_t *args, char **buf)
188 {
189         unsigned int *field = (unsigned int *)(obj + args[0]);
190         return !(*buf = ast_strdup(*field ? "true" : "false")) ? -1 : 0;
191 }
192
193 static int sockaddr_handler_fn(const void *obj, const intptr_t *args, char **buf)
194 {
195         struct ast_sockaddr *field = (struct ast_sockaddr *)(obj + args[0]);
196         return !(*buf = ast_strdup(ast_sockaddr_stringify(field))) ? -1 : 0;
197 }
198
199 static int chararray_handler_fn(const void *obj, const intptr_t *args, char **buf)
200 {
201         char *field = (char *)(obj + args[0]);
202         return !(*buf = ast_strdup(field)) ? -1 : 0;
203 }
204
205 static sorcery_field_handler sorcery_field_default_handler(enum aco_option_type type)
206 {
207         switch(type) {
208         case OPT_BOOL_T: return bool_handler_fn;
209         case OPT_CHAR_ARRAY_T: return chararray_handler_fn;
210         case OPT_DOUBLE_T: return double_handler_fn;
211         case OPT_INT_T: return int_handler_fn;
212         case OPT_SOCKADDR_T: return sockaddr_handler_fn;
213         case OPT_STRINGFIELD_T: return stringfield_handler_fn;
214         case OPT_UINT_T: return uint_handler_fn;
215
216         default:
217         case OPT_CUSTOM_T: return NULL;
218         }
219
220         return NULL;
221 }
222
223 /*! \brief Hashing function for sorcery wizards */
224 static int sorcery_wizard_hash(const void *obj, const int flags)
225 {
226         const struct ast_sorcery_wizard *wizard = obj;
227         const char *name = obj;
228
229         return ast_str_hash(flags & OBJ_KEY ? name : wizard->name);
230 }
231
232 /*! \brief Comparator function for sorcery wizards */
233 static int sorcery_wizard_cmp(void *obj, void *arg, int flags)
234 {
235         struct ast_sorcery_wizard *wizard1 = obj, *wizard2 = arg;
236         const char *name = arg;
237
238         return !strcmp(wizard1->name, flags & OBJ_KEY ? name : wizard2->name) ? CMP_MATCH | CMP_STOP : 0;
239 }
240
241 int ast_sorcery_init(void)
242 {
243         struct ast_threadpool_options options = {
244                 .version = AST_THREADPOOL_OPTIONS_VERSION,
245                 .auto_increment = 1,
246                 .max_size = 0,
247                 .idle_timeout = 60,
248                 .initial_size = 0,
249         };
250         ast_assert(wizards == NULL);
251
252         if (!(threadpool = ast_threadpool_create("Sorcery", NULL, &options))) {
253                 return -1;
254         }
255
256         if (!(wizards = ao2_container_alloc(WIZARD_BUCKETS, sorcery_wizard_hash, sorcery_wizard_cmp))) {
257                 ast_threadpool_shutdown(threadpool);
258                 return -1;
259         }
260
261         return 0;
262 }
263
264 int __ast_sorcery_wizard_register(const struct ast_sorcery_wizard *interface, struct ast_module *module)
265 {
266         struct ast_sorcery_wizard *wizard;
267         int res = -1;
268
269         ast_assert(!ast_strlen_zero(interface->name));
270
271         ao2_lock(wizards);
272
273         if ((wizard = ao2_find(wizards, interface->name, OBJ_KEY | OBJ_NOLOCK))) {
274                 ast_log(LOG_WARNING, "Attempted to register sorcery wizard '%s' twice\n",
275                         interface->name);
276                 goto done;
277         }
278
279         if (!(wizard = ao2_alloc(sizeof(*wizard), NULL))) {
280                 goto done;
281         }
282
283         *wizard = *interface;
284         wizard->module = module;
285
286         ao2_link_flags(wizards, wizard, OBJ_NOLOCK);
287         res = 0;
288
289         ast_verb(2, "Sorcery registered wizard '%s'\n", interface->name);
290
291 done:
292         ao2_cleanup(wizard);
293         ao2_unlock(wizards);
294
295         return res;
296 }
297
298 int ast_sorcery_wizard_unregister(const struct ast_sorcery_wizard *interface)
299 {
300         RAII_VAR(struct ast_sorcery_wizard *, wizard, ao2_find(wizards, interface->name, OBJ_KEY | OBJ_UNLINK), ao2_cleanup);
301
302         if (wizard) {
303                 ast_verb(2, "Sorcery unregistered wizard '%s'\n", interface->name);
304                 return 0;
305         } else {
306                 return -1;
307         }
308 }
309
310 /*! \brief Destructor called when sorcery structure is destroyed */
311 static void sorcery_destructor(void *obj)
312 {
313         struct ast_sorcery *sorcery = obj;
314
315         ao2_cleanup(sorcery->types);
316 }
317
318 /*! \brief Hashing function for sorcery types */
319 static int sorcery_type_hash(const void *obj, const int flags)
320 {
321         const struct ast_sorcery_object_type *type = obj;
322         const char *name = obj;
323
324         return ast_str_hash(flags & OBJ_KEY ? name : type->name);
325 }
326
327 /*! \brief Comparator function for sorcery types */
328 static int sorcery_type_cmp(void *obj, void *arg, int flags)
329 {
330         struct ast_sorcery_object_type *type1 = obj, *type2 = arg;
331         const char *name = arg;
332
333         return !strcmp(type1->name, flags & OBJ_KEY ? name : type2->name) ? CMP_MATCH | CMP_STOP : 0;
334 }
335
336 struct ast_sorcery *ast_sorcery_open(void)
337 {
338         struct ast_sorcery *sorcery;
339
340         if (!(sorcery = ao2_alloc(sizeof(*sorcery), sorcery_destructor))) {
341                 return NULL;
342         }
343
344         if (!(sorcery->types = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_RWLOCK, TYPE_BUCKETS, sorcery_type_hash, sorcery_type_cmp))) {
345                 ao2_ref(sorcery, -1);
346                 sorcery = NULL;
347         }
348
349         return sorcery;
350 }
351
352 /*! \brief Destructor function for object types */
353 static void sorcery_object_type_destructor(void *obj)
354 {
355         struct ast_sorcery_object_type *object_type = obj;
356
357         ao2_cleanup(object_type->wizards);
358         ao2_cleanup(object_type->fields);
359         ao2_cleanup(object_type->observers);
360
361         if (object_type->info) {
362                 aco_info_destroy(object_type->info);
363                 ast_free(object_type->info);
364         }
365
366         ast_free(object_type->file);
367
368         ast_taskprocessor_unreference(object_type->serializer);
369 }
370
371 /*! \brief Internal function which allocates an object type structure */
372 static struct ast_sorcery_object_type *sorcery_object_type_alloc(const char *type, const char *module)
373 {
374         struct ast_sorcery_object_type *object_type;
375         char uuid[AST_UUID_STR_LEN];
376
377         if (!(object_type = ao2_alloc(sizeof(*object_type), sorcery_object_type_destructor))) {
378                 return NULL;
379         }
380
381         /* Order matters for object wizards */
382         if (!(object_type->wizards = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, NULL))) {
383                 ao2_ref(object_type, -1);
384                 return NULL;
385         }
386
387         if (!(object_type->fields = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, NULL))) {
388                 ao2_ref(object_type, -1);
389                 return NULL;
390         }
391
392         if (!(object_type->observers = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_RWLOCK, 1, NULL, NULL))) {
393                 ao2_ref(object_type, -1);
394                 return NULL;
395         }
396
397         if (!(object_type->info = ast_calloc(1, sizeof(*object_type->info) + 2 * sizeof(object_type->info->files[0])))) {
398                 ao2_ref(object_type, -1);
399                 return NULL;
400         }
401
402         if (!(object_type->file = ast_calloc(1, sizeof(*object_type->file) + 2 * sizeof(object_type->file->types[0])))) {
403                 ao2_ref(object_type, -1);
404                 return NULL;
405         }
406
407         if (!ast_uuid_generate_str(uuid, sizeof(uuid))) {
408                 ao2_ref(object_type, -1);
409                 return NULL;
410         }
411
412         if (!(object_type->serializer = ast_threadpool_serializer(uuid, threadpool))) {
413                 ao2_ref(object_type, -1);
414                 return NULL;
415         }
416
417         object_type->info->files[0] = object_type->file;
418         object_type->info->files[1] = NULL;
419         object_type->info->module = module;
420
421         ast_copy_string(object_type->name, type, sizeof(object_type->name));
422
423         return object_type;
424 }
425
426 /*! \brief Object wizard destructor */
427 static void sorcery_object_wizard_destructor(void *obj)
428 {
429         struct ast_sorcery_object_wizard *object_wizard = obj;
430
431         if (object_wizard->data) {
432                 object_wizard->wizard->close(object_wizard->data);
433         }
434
435         if (object_wizard->wizard) {
436                 ast_module_unref(object_wizard->wizard->module);
437         }
438 }
439
440 /*! \brief Internal function which creates an object type and adds a wizard mapping */
441 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)
442 {
443         RAII_VAR(struct ast_sorcery_object_type *, object_type,  ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
444         RAII_VAR(struct ast_sorcery_wizard *, wizard, ao2_find(wizards, name, OBJ_KEY), ao2_cleanup);
445         RAII_VAR(struct ast_sorcery_object_wizard *, object_wizard, ao2_alloc(sizeof(*object_wizard), sorcery_object_wizard_destructor), ao2_cleanup);
446         int created = 0;
447
448         if (!wizard || !object_wizard) {
449                 return -1;
450         }
451
452         if (!object_type) {
453                 if (!(object_type = sorcery_object_type_alloc(type, module))) {
454                         return -1;
455                 }
456                 created = 1;
457         }
458
459         if (wizard->open && !(object_wizard->data = wizard->open(data))) {
460                 return -1;
461         }
462
463         ast_module_ref(wizard->module);
464
465         object_wizard->wizard = wizard;
466         object_wizard->caching = caching;
467
468         ao2_link(object_type->wizards, object_wizard);
469
470         if (created) {
471                 ao2_link(sorcery->types, object_type);
472         }
473
474         return 0;
475 }
476
477 int __ast_sorcery_apply_config(struct ast_sorcery *sorcery, const char *name, const char *module)
478 {
479         struct ast_flags flags = { 0 };
480         struct ast_config *config = ast_config_load2("sorcery.conf", "sorcery", flags);
481         struct ast_variable *mapping;
482         int res = 0;
483
484         if (!config || (config == CONFIG_STATUS_FILEMISSING) || (config == CONFIG_STATUS_FILEINVALID)) {
485                 return -1;
486         }
487
488         for (mapping = ast_variable_browse(config, name); mapping; mapping = mapping->next) {
489                 RAII_VAR(char *, mapping_name, ast_strdup(mapping->name), ast_free);
490                 RAII_VAR(char *, mapping_value, ast_strdup(mapping->value), ast_free);
491                 char *options = mapping_name, *name = strsep(&options, "/");
492                 char *data = mapping_value, *wizard = strsep(&data, ",");
493                 unsigned int caching = 0;
494
495                 /* If no wizard exists just skip, nothing we can do */
496                 if (ast_strlen_zero(wizard)) {
497                         continue;
498                 }
499
500                 /* If the wizard is configured as a cache treat it as such */
501                 if (!ast_strlen_zero(options) && strstr(options, "cache")) {
502                         caching = 1;
503                 }
504
505                 /* Any error immediately causes us to stop */
506                 if ((res = sorcery_apply_wizard_mapping(sorcery, name, module, wizard, data, caching))) {
507                         break;
508                 }
509         }
510
511         ast_config_destroy(config);
512
513         return res;
514 }
515
516 int __ast_sorcery_apply_default(struct ast_sorcery *sorcery, const char *type, const char *module, const char *name, const char *data)
517 {
518         RAII_VAR(struct ast_sorcery_object_type *, object_type,  ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
519
520         /* Defaults can not be added if any existing mapping exists */
521         if (object_type) {
522                 return -1;
523         }
524
525         return sorcery_apply_wizard_mapping(sorcery, type, module, name, data, 0);
526 }
527
528 int ast_sorcery_object_register(struct ast_sorcery *sorcery, const char *type, aco_type_item_alloc alloc, sorcery_transform_handler transform, sorcery_apply_handler apply)
529 {
530         RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
531
532         if (!object_type || object_type->type.item_alloc) {
533                 return -1;
534         }
535
536         object_type->type.name = object_type->name;
537         object_type->type.type = ACO_ITEM;
538         object_type->type.category = "";
539         object_type->type.item_alloc = alloc;
540
541         object_type->transform = transform;
542         object_type->apply = apply;
543         object_type->file->types[0] = &object_type->type;
544         object_type->file->types[1] = NULL;
545
546         if (aco_info_init(object_type->info)) {
547                 return -1;
548         }
549
550         return 0;
551 }
552
553 void ast_sorcery_object_set_copy_handler(struct ast_sorcery *sorcery, const char *type, sorcery_copy_handler copy)
554 {
555         RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
556
557         if (!object_type) {
558                 return;
559         }
560
561         object_type->copy = copy;
562 }
563
564 void ast_sorcery_object_set_diff_handler(struct ast_sorcery *sorcery, const char *type, sorcery_diff_handler diff)
565 {
566         RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
567
568         if (!object_type) {
569                 return;
570         }
571
572         object_type->diff = diff;
573 }
574
575 int ast_sorcery_object_fields_register(struct ast_sorcery *sorcery, const char *type, const char *regex, aco_option_handler config_handler, sorcery_fields_handler sorcery_handler)
576 {
577         RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
578         RAII_VAR(struct ast_sorcery_object_field *, object_field, NULL, ao2_cleanup);
579
580         if (!object_type || !object_type->type.item_alloc || !config_handler || !(object_field = ao2_alloc(sizeof(*object_field), NULL))) {
581                 return -1;
582         }
583
584         ast_copy_string(object_field->name, regex, sizeof(object_field->name));
585         object_field->multiple_handler = sorcery_handler;
586
587         ao2_link(object_type->fields, object_field);
588         __aco_option_register(object_type->info, regex, ACO_REGEX, object_type->file->types, "", OPT_CUSTOM_T, config_handler, 0, 0);
589
590         return 0;
591 }
592
593 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,
594                                         aco_option_handler config_handler, sorcery_field_handler sorcery_handler, unsigned int flags, size_t argc, ...)
595 {
596         RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
597         RAII_VAR(struct ast_sorcery_object_field *, object_field, NULL, ao2_cleanup);
598         int pos;
599         va_list args;
600
601         if (!strcmp(type, "id") || !object_type || !object_type->type.item_alloc) {
602                 return -1;
603         }
604
605         if (!sorcery_handler) {
606                 sorcery_handler = sorcery_field_default_handler(opt_type);
607         }
608
609         if (!(object_field = ao2_alloc(sizeof(*object_field) + argc * sizeof(object_field->args[0]), NULL))) {
610                 return -1;
611         }
612
613         ast_copy_string(object_field->name, name, sizeof(object_field->name));
614         object_field->handler = sorcery_handler;
615
616         va_start(args, argc);
617         for (pos = 0; pos < argc; pos++) {
618                 object_field->args[pos] = va_arg(args, size_t);
619         }
620         va_end(args);
621
622         ao2_link(object_type->fields, object_field);
623
624         /* TODO: Improve this hack */
625         if (!argc) {
626                 __aco_option_register(object_type->info, name, ACO_EXACT, object_type->file->types, default_val, opt_type, config_handler, flags, argc);
627         } else if (argc == 1) {
628                 __aco_option_register(object_type->info, name, ACO_EXACT, object_type->file->types, default_val, opt_type, config_handler, flags, argc,
629                                       object_field->args[0]);
630         } else if (argc == 2) {
631                 __aco_option_register(object_type->info, name, ACO_EXACT, object_type->file->types, default_val, opt_type, config_handler, flags, argc,
632                                       object_field->args[0], object_field->args[1]);
633         } else if (argc == 3) {
634                 __aco_option_register(object_type->info, name, ACO_EXACT, object_type->file->types, default_val, opt_type, config_handler, flags, argc,
635                                       object_field->args[0], object_field->args[1], object_field->args[2]);
636         } else {
637                 ast_assert(0); /* The hack... she does us no good for this */
638         }
639
640         return 0;
641 }
642
643 static int sorcery_wizard_load(void *obj, void *arg, int flags)
644 {
645         struct ast_sorcery_object_wizard *wizard = obj;
646         struct sorcery_load_details *details = arg;
647         void (*load)(void *data, const struct ast_sorcery *sorcery, const char *type);
648
649         load = !details->reload ? wizard->wizard->load : wizard->wizard->reload;
650
651         if (load) {
652                 load(wizard->data, details->sorcery, details->type);
653         }
654
655         return 0;
656 }
657
658 /*! \brief Destructor for observer invocation */
659 static void sorcery_observer_invocation_destroy(void *obj)
660 {
661         struct sorcery_observer_invocation *invocation = obj;
662
663         ao2_cleanup(invocation->object_type);
664         ao2_cleanup(invocation->object);
665 }
666
667 /*! \brief Allocator function for observer invocation */
668 static struct sorcery_observer_invocation *sorcery_observer_invocation_alloc(struct ast_sorcery_object_type *object_type, void *object)
669 {
670         struct sorcery_observer_invocation *invocation = ao2_alloc(sizeof(*invocation), sorcery_observer_invocation_destroy);
671
672         if (!invocation) {
673                 return NULL;
674         }
675
676         ao2_ref(object_type, +1);
677         invocation->object_type = object_type;
678
679         if (object) {
680                 ao2_ref(object, +1);
681                 invocation->object = object;
682         }
683
684         return invocation;
685 }
686
687 /*! \brief Internal callback function which notifies an individual observer that an object type has been loaded */
688 static int sorcery_observer_notify_loaded(void *obj, void *arg, int flags)
689 {
690         const struct ast_sorcery_object_type_observer *observer = obj;
691
692         if (observer->callbacks->loaded) {
693                 observer->callbacks->loaded(arg);
694         }
695
696         return 0;
697 }
698
699 /*! \brief Internal callback function which notifies observers that an object type has been loaded */
700 static int sorcery_observers_notify_loaded(void *data)
701 {
702         struct sorcery_observer_invocation *invocation = data;
703
704         ao2_callback(invocation->object_type->observers, OBJ_NODATA, sorcery_observer_notify_loaded, invocation->object_type->name);
705         ao2_cleanup(invocation);
706
707         return 0;
708 }
709
710 static int sorcery_object_load(void *obj, void *arg, int flags)
711 {
712         struct ast_sorcery_object_type *type = obj;
713         struct sorcery_load_details *details = arg;
714
715         details->type = type->name;
716         ao2_callback(type->wizards, OBJ_NODATA, sorcery_wizard_load, details);
717
718         if (ao2_container_count(type->observers)) {
719                 struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(type, NULL);
720
721                 if (invocation && ast_taskprocessor_push(type->serializer, sorcery_observers_notify_loaded, invocation)) {
722                         ao2_cleanup(invocation);
723                 }
724         }
725
726         return 0;
727 }
728
729 void ast_sorcery_load(const struct ast_sorcery *sorcery)
730 {
731         struct sorcery_load_details details = {
732                 .sorcery = sorcery,
733                 .reload = 0,
734         };
735
736         ao2_callback(sorcery->types, OBJ_NODATA, sorcery_object_load, &details);
737 }
738
739 void ast_sorcery_load_object(const struct ast_sorcery *sorcery, const char *type)
740 {
741         RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
742         struct sorcery_load_details details = {
743                 .sorcery = sorcery,
744                 .reload = 0,
745         };
746
747         if (!object_type) {
748                 return;
749         }
750
751         sorcery_object_load(object_type, &details, 0);
752 }
753
754 void ast_sorcery_reload(const struct ast_sorcery *sorcery)
755 {
756         struct sorcery_load_details details = {
757                 .sorcery = sorcery,
758                 .reload = 1,
759         };
760
761         ao2_callback(sorcery->types, OBJ_NODATA, sorcery_object_load, &details);
762 }
763
764 void ast_sorcery_reload_object(const struct ast_sorcery *sorcery, const char *type)
765 {
766         RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
767         struct sorcery_load_details details = {
768                 .sorcery = sorcery,
769                 .reload = 1,
770         };
771
772         if (!object_type) {
773                 return;
774         }
775
776         sorcery_object_load(object_type, &details, 0);
777 }
778
779 void ast_sorcery_ref(struct ast_sorcery *sorcery)
780 {
781         ao2_ref(sorcery, +1);
782 }
783
784 struct ast_variable *ast_sorcery_objectset_create(const struct ast_sorcery *sorcery, const void *object)
785 {
786         const struct ast_sorcery_object_details *details = object;
787         RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, details->type, OBJ_KEY), ao2_cleanup);
788         struct ao2_iterator i;
789         struct ast_sorcery_object_field *object_field;
790         struct ast_variable *fields = NULL;
791         int res = 0;
792
793         if (!object_type) {
794                 return NULL;
795         }
796
797         i = ao2_iterator_init(object_type->fields, 0);
798
799         for (; (object_field = ao2_iterator_next(&i)) && !res; ao2_ref(object_field, -1)) {
800                 struct ast_variable *tmp = NULL;
801
802                 if (object_field->multiple_handler) {
803                         if ((res = object_field->multiple_handler(object, &tmp))) {
804                                 ast_variables_destroy(tmp);
805                         }
806                 } else if (object_field->handler) {
807                         char *buf = NULL;
808
809                         if ((res = object_field->handler(object, object_field->args, &buf)) ||
810                                 !(tmp = ast_variable_new(object_field->name, S_OR(buf, ""), ""))) {
811                                 res = -1;
812                         }
813
814                         ast_free(buf);
815                 } else {
816                         continue;
817                 }
818
819                 if (!res) {
820                         tmp->next = fields;
821                         fields = tmp;
822                 }
823         }
824
825         ao2_iterator_destroy(&i);
826
827         /* If any error occurs we destroy all fields handled before so a partial objectset is not returned */
828         if (res) {
829                 ast_variables_destroy(fields);
830                 fields = NULL;
831         }
832
833         return fields;
834 }
835
836 struct ast_json *ast_sorcery_objectset_json_create(const struct ast_sorcery *sorcery, const void *object)
837 {
838         const struct ast_sorcery_object_details *details = object;
839         RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, details->type, OBJ_KEY), ao2_cleanup);
840         struct ao2_iterator i;
841         struct ast_sorcery_object_field *object_field;
842         struct ast_json *json = ast_json_object_create();
843         int res = 0;
844
845         if (!object_type || !json) {
846                 return NULL;
847         }
848
849         i = ao2_iterator_init(object_type->fields, 0);
850
851         for (; (object_field = ao2_iterator_next(&i)) && !res; ao2_ref(object_field, -1)) {
852                 if (object_field->multiple_handler) {
853                         struct ast_variable *tmp = NULL;
854                         struct ast_variable *field;
855
856                         if ((res = object_field->multiple_handler(object, &tmp))) {
857                                 break;
858                         }
859
860                         for (field = tmp; field; field = field->next) {
861                                 struct ast_json *value = ast_json_string_create(field->value);
862
863                                 if (value && ast_json_object_set(json, field->name, value)) {
864                                         ast_json_unref(value);
865                                         res = -1;
866                                 }
867                         }
868
869                         ast_variables_destroy(tmp);
870                 } else if (object_field->handler) {
871                         char *buf = NULL;
872                         struct ast_json *value = NULL;
873
874                         if ((res = object_field->handler(object, object_field->args, &buf)) ||
875                                 !(value = ast_json_string_create(buf)) ||
876                                 ast_json_object_set(json, object_field->name, value)) {
877                                 ast_json_unref(value);
878                                 res = -1;
879                         }
880
881                         ast_free(buf);
882                 } else {
883                         continue;
884                 }
885         }
886
887         ao2_iterator_destroy(&i);
888
889         /* If any error occurs we destroy the JSON object so a partial objectset is not returned */
890         if (res) {
891                 ast_json_unref(json);
892                 json = NULL;
893         }
894
895         return json;
896 }
897
898 int ast_sorcery_objectset_apply(const struct ast_sorcery *sorcery, void *object, struct ast_variable *objectset)
899 {
900         const struct ast_sorcery_object_details *details = object;
901         RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, details->type, OBJ_KEY), ao2_cleanup);
902         RAII_VAR(struct ast_variable *, transformed, NULL, ast_variables_destroy);
903         struct ast_variable *field;
904         int res = 0;
905
906         if (!object_type) {
907                 return -1;
908         }
909
910         if (object_type->transform && (transformed = object_type->transform(objectset))) {
911                 field = transformed;
912         } else {
913                 field = objectset;
914         }
915
916         for (; field; field = field->next) {
917                 if ((res = aco_process_var(&object_type->type, details->id, field, object))) {
918                         break;
919                 }
920         }
921
922         if (!res && object_type->apply) {
923                 res = object_type->apply(sorcery, object);
924         }
925
926         return res;
927 }
928
929 static const struct ast_variable *sorcery_find_field(const struct ast_variable *fields, const char *name)
930 {
931         const struct ast_variable *field;
932
933         /* Search the linked list of fields to find the correct one */
934         for (field = fields; field; field = field->next) {
935                 if (!strcmp(field->name, name)) {
936                         return field;
937                 }
938         }
939
940         return NULL;
941 }
942
943 int ast_sorcery_changeset_create(const struct ast_variable *original, const struct ast_variable *modified, struct ast_variable **changes)
944 {
945         const struct ast_variable *field;
946         int res = 0;
947
948         *changes = NULL;
949
950         /* Unless the ast_variable list changes when examined... it can't differ from itself */
951         if (original == modified) {
952                 return 0;
953         }
954
955         for (field = modified; field; field = field->next) {
956                 const struct ast_variable *old = sorcery_find_field(original, field->name);
957
958                 if (!old || strcmp(old->value, field->value)) {
959                         struct ast_variable *tmp;
960
961                         if (!(tmp = ast_variable_new(field->name, field->value, ""))) {
962                                 res = -1;
963                                 break;
964                         }
965
966                         tmp->next = *changes;
967                         *changes = tmp;
968                 }
969         }
970
971         /* If an error occurred do not return a partial changeset */
972         if (res) {
973                 ast_variables_destroy(*changes);
974                 *changes = NULL;
975         }
976
977         return res;
978 }
979
980 void *ast_sorcery_alloc(const struct ast_sorcery *sorcery, const char *type, const char *id)
981 {
982         RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
983         struct ast_sorcery_object_details *details;
984
985         if (!object_type || !object_type->type.item_alloc ||
986             !(details = object_type->type.item_alloc(""))) {
987                 return NULL;
988         }
989
990         if (ast_strlen_zero(id)) {
991                 ast_uuid_generate_str(details->id, sizeof(details->id));
992         } else {
993                 ast_copy_string(details->id, id, sizeof(details->id));
994         }
995
996         ast_copy_string(details->type, type, sizeof(details->type));
997
998         if (aco_set_defaults(&object_type->type, id, details)) {
999                 ao2_ref(details, -1);
1000                 return NULL;
1001         }
1002
1003         return details;
1004 }
1005
1006 void *ast_sorcery_copy(const struct ast_sorcery *sorcery, const void *object)
1007 {
1008         const struct ast_sorcery_object_details *details = object;
1009         RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, details->type, OBJ_KEY), ao2_cleanup);
1010         struct ast_sorcery_object_details *copy = ast_sorcery_alloc(sorcery, details->type, details->id);
1011         RAII_VAR(struct ast_variable *, objectset, NULL, ast_variables_destroy);
1012         int res = 0;
1013
1014         if (!copy) {
1015                 return NULL;
1016         } else if (object_type->copy) {
1017                 res = object_type->copy(object, copy);
1018         } else if ((objectset = ast_sorcery_objectset_create(sorcery, object))) {
1019                 res = ast_sorcery_objectset_apply(sorcery, copy, objectset);
1020         } else {
1021                 /* No native copy available and could not create an objectset, this copy has failed */
1022                 res = -1;
1023         }
1024
1025         if (res) {
1026                 ao2_cleanup(copy);
1027                 copy = NULL;
1028         }
1029
1030         return copy;
1031 }
1032
1033 int ast_sorcery_diff(const struct ast_sorcery *sorcery, const void *original, const void *modified, struct ast_variable **changes)
1034 {
1035         RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, ast_sorcery_object_get_type(original), OBJ_KEY), ao2_cleanup);
1036
1037         *changes = NULL;
1038
1039         if (strcmp(ast_sorcery_object_get_type(original), ast_sorcery_object_get_type(modified))) {
1040                 return -1;
1041         }
1042
1043         if (original == modified) {
1044                 return 0;
1045         } else if (!object_type->diff) {
1046                 RAII_VAR(struct ast_variable *, objectset1, NULL, ast_variables_destroy);
1047                 RAII_VAR(struct ast_variable *, objectset2, NULL, ast_variables_destroy);
1048
1049                 objectset1 = ast_sorcery_objectset_create(sorcery, original);
1050                 objectset2 = ast_sorcery_objectset_create(sorcery, modified);
1051
1052                 return ast_sorcery_changeset_create(objectset1, objectset2, changes);
1053         } else {
1054                 return object_type->diff(original, modified, changes);
1055         }
1056 }
1057
1058 /*! \brief Structure used when calling create, update, or delete */
1059 struct sorcery_details {
1060         /*! \brief Pointer to the sorcery instance */
1061         const struct ast_sorcery *sorcery;
1062         /*! \brief Pointer to the object itself */
1063         void *obj;
1064 };
1065
1066 /*! \brief Internal function used to create an object in caching wizards */
1067 static int sorcery_cache_create(void *obj, void *arg, int flags)
1068 {
1069         const struct ast_sorcery_object_wizard *object_wizard = obj;
1070         const struct sorcery_details *details = arg;
1071
1072         if (!object_wizard->caching || !object_wizard->wizard->create) {
1073                 return 0;
1074         }
1075
1076         object_wizard->wizard->create(details->sorcery, object_wizard->data, details->obj);
1077
1078         return 0;
1079 }
1080
1081 void *ast_sorcery_retrieve_by_id(const struct ast_sorcery *sorcery, const char *type, const char *id)
1082 {
1083         RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
1084         void *object = NULL;
1085         struct ao2_iterator i;
1086         struct ast_sorcery_object_wizard *wizard;
1087         unsigned int cached = 0;
1088
1089         if (!object_type || ast_strlen_zero(id)) {
1090                 return NULL;
1091         }
1092
1093         i = ao2_iterator_init(object_type->wizards, 0);
1094         for (; (wizard = ao2_iterator_next(&i)); ao2_ref(wizard, -1)) {
1095                 if (wizard->wizard->retrieve_id &&
1096                     !(object = wizard->wizard->retrieve_id(sorcery, wizard->data, object_type->name, id))) {
1097                         continue;
1098                 }
1099
1100                 cached = wizard->caching;
1101
1102                 ao2_ref(wizard, -1);
1103                 break;
1104         }
1105         ao2_iterator_destroy(&i);
1106
1107         if (!cached && object) {
1108                 ao2_callback(object_type->wizards, 0, sorcery_cache_create, object);
1109         }
1110
1111         return object;
1112 }
1113
1114 void *ast_sorcery_retrieve_by_fields(const struct ast_sorcery *sorcery, const char *type, unsigned int flags, struct ast_variable *fields)
1115 {
1116         RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
1117         void *object = NULL;
1118         struct ao2_iterator i;
1119         struct ast_sorcery_object_wizard *wizard;
1120         unsigned int cached = 0;
1121
1122         if (!object_type) {
1123                 ast_log(LOG_NOTICE, "Can't find object type '%s'\n", type);
1124                 return NULL;
1125         }
1126
1127         /* If returning multiple objects create a container to store them in */
1128         if ((flags & AST_RETRIEVE_FLAG_MULTIPLE)) {
1129                 if (!(object = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, NULL))) {
1130                         return NULL;
1131                 }
1132         }
1133
1134         /* Inquire with the available wizards for retrieval */
1135         i = ao2_iterator_init(object_type->wizards, 0);
1136         for (; (wizard = ao2_iterator_next(&i)); ao2_ref(wizard, -1)) {
1137                 if ((flags & AST_RETRIEVE_FLAG_MULTIPLE)) {
1138                         if (wizard->wizard->retrieve_multiple) {
1139                                 wizard->wizard->retrieve_multiple(sorcery, wizard->data, object_type->name, object, fields);
1140                         }
1141                 } else if (fields && wizard->wizard->retrieve_fields) {
1142                         if (wizard->wizard->retrieve_fields) {
1143                                 object = wizard->wizard->retrieve_fields(sorcery, wizard->data, object_type->name, fields);
1144                         }
1145                 }
1146
1147                 if ((flags & AST_RETRIEVE_FLAG_MULTIPLE) || !object) {
1148                         continue;
1149                 }
1150
1151                 cached = wizard->caching;
1152
1153                 ao2_ref(wizard, -1);
1154                 break;
1155         }
1156         ao2_iterator_destroy(&i);
1157
1158         /* If we are returning a single object and it came from a non-cache source create it in any caches */
1159         if (!(flags & AST_RETRIEVE_FLAG_MULTIPLE) && !cached && object) {
1160                 ao2_callback(object_type->wizards, 0, sorcery_cache_create, object);
1161         }
1162
1163         return object;
1164 }
1165
1166 struct ao2_container *ast_sorcery_retrieve_by_regex(const struct ast_sorcery *sorcery, const char *type, const char *regex)
1167 {
1168         RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
1169         struct ao2_container *objects;
1170         struct ao2_iterator i;
1171         struct ast_sorcery_object_wizard *wizard;
1172
1173         if (!object_type || !(objects = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, NULL))) {
1174                 return NULL;
1175         }
1176
1177         i = ao2_iterator_init(object_type->wizards, 0);
1178         for (; (wizard = ao2_iterator_next(&i)); ao2_ref(wizard, -1)) {
1179                 if (!wizard->wizard->retrieve_regex) {
1180                         continue;
1181                 }
1182
1183                 wizard->wizard->retrieve_regex(sorcery, wizard->data, object_type->name, objects, regex);
1184         }
1185         ao2_iterator_destroy(&i);
1186
1187         return objects;
1188 }
1189
1190 /*! \brief Internal function which returns if the wizard has created the object */
1191 static int sorcery_wizard_create(void *obj, void *arg, int flags)
1192 {
1193         const struct ast_sorcery_object_wizard *object_wizard = obj;
1194         const struct sorcery_details *details = arg;
1195
1196         return (!object_wizard->caching && !object_wizard->wizard->create(details->sorcery, object_wizard->data, details->obj)) ? CMP_MATCH | CMP_STOP : 0;
1197 }
1198
1199 /*! \brief Internal callback function which notifies an individual observer that an object has been created */
1200 static int sorcery_observer_notify_create(void *obj, void *arg, int flags)
1201 {
1202         const struct ast_sorcery_object_type_observer *observer = obj;
1203
1204         if (observer->callbacks->created) {
1205                 observer->callbacks->created(arg);
1206         }
1207
1208         return 0;
1209 }
1210
1211 /*! \brief Internal callback function which notifies observers that an object has been created */
1212 static int sorcery_observers_notify_create(void *data)
1213 {
1214         struct sorcery_observer_invocation *invocation = data;
1215
1216         ao2_callback(invocation->object_type->observers, OBJ_NODATA, sorcery_observer_notify_create, invocation->object);
1217         ao2_cleanup(invocation);
1218
1219         return 0;
1220 }
1221
1222 int ast_sorcery_create(const struct ast_sorcery *sorcery, void *object)
1223 {
1224         const struct ast_sorcery_object_details *details = object;
1225         RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, details->type, OBJ_KEY), ao2_cleanup);
1226         RAII_VAR(struct ast_sorcery_object_wizard *, object_wizard, NULL, ao2_cleanup);
1227         struct sorcery_details sdetails = {
1228                 .sorcery = sorcery,
1229                 .obj = object,
1230         };
1231
1232         if (!object_type) {
1233                 return -1;
1234         }
1235
1236         if ((object_wizard = ao2_callback(object_type->wizards, 0, sorcery_wizard_create, &sdetails)) &&
1237                 ao2_container_count(object_type->observers)) {
1238                 struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(object_type, object);
1239
1240                 if (invocation && ast_taskprocessor_push(object_type->serializer, sorcery_observers_notify_create, invocation)) {
1241                         ao2_cleanup(invocation);
1242                 }
1243         }
1244
1245         return object_wizard ? 0 : -1;
1246 }
1247
1248 /*! \brief Internal callback function which notifies an individual observer that an object has been updated */
1249 static int sorcery_observer_notify_update(void *obj, void *arg, int flags)
1250 {
1251         const struct ast_sorcery_object_type_observer *observer = obj;
1252
1253         if (observer->callbacks->updated) {
1254                 observer->callbacks->updated(arg);
1255         }
1256
1257         return 0;
1258 }
1259
1260 /*! \brief Internal callback function which notifies observers that an object has been updated */
1261 static int sorcery_observers_notify_update(void *data)
1262 {
1263         struct sorcery_observer_invocation *invocation = data;
1264
1265         ao2_callback(invocation->object_type->observers, OBJ_NODATA, sorcery_observer_notify_update, invocation->object);
1266         ao2_cleanup(invocation);
1267
1268         return 0;
1269 }
1270
1271 /*! \brief Internal function which returns if a wizard has updated the object */
1272 static int sorcery_wizard_update(void *obj, void *arg, int flags)
1273 {
1274         const struct ast_sorcery_object_wizard *object_wizard = obj;
1275         const struct sorcery_details *details = arg;
1276
1277         return (object_wizard->wizard->update && !object_wizard->wizard->update(details->sorcery, object_wizard->data, details->obj) &&
1278                 !object_wizard->caching) ? CMP_MATCH | CMP_STOP : 0;
1279 }
1280
1281 int ast_sorcery_update(const struct ast_sorcery *sorcery, void *object)
1282 {
1283         const struct ast_sorcery_object_details *details = object;
1284         RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, details->type, OBJ_KEY), ao2_cleanup);
1285         RAII_VAR(struct ast_sorcery_object_wizard *, object_wizard, NULL, ao2_cleanup);
1286         struct sorcery_details sdetails = {
1287                 .sorcery = sorcery,
1288                 .obj = object,
1289         };
1290
1291         if (!object_type) {
1292                 return -1;
1293         }
1294
1295         if ((object_wizard = ao2_callback(object_type->wizards, 0, sorcery_wizard_update, &sdetails)) &&
1296                 ao2_container_count(object_type->observers)) {
1297                 struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(object_type, object);
1298
1299                 if (invocation && ast_taskprocessor_push(object_type->serializer, sorcery_observers_notify_update, invocation)) {
1300                         ao2_cleanup(invocation);
1301                 }
1302         }
1303
1304         return object_wizard ? 0 : -1;
1305 }
1306
1307 /*! \brief Internal callback function which notifies an individual observer that an object has been deleted */
1308 static int sorcery_observer_notify_delete(void *obj, void *arg, int flags)
1309 {
1310         const struct ast_sorcery_object_type_observer *observer = obj;
1311
1312         if (observer->callbacks->deleted) {
1313                 observer->callbacks->deleted(arg);
1314         }
1315
1316         return 0;
1317 }
1318
1319 /*! \brief Internal callback function which notifies observers that an object has been deleted */
1320 static int sorcery_observers_notify_delete(void *data)
1321 {
1322         struct sorcery_observer_invocation *invocation = data;
1323
1324         ao2_callback(invocation->object_type->observers, OBJ_NODATA, sorcery_observer_notify_delete, invocation->object);
1325         ao2_cleanup(invocation);
1326
1327         return 0;
1328 }
1329
1330 /*! \brief Internal function which returns if a wizard has deleted the object */
1331 static int sorcery_wizard_delete(void *obj, void *arg, int flags)
1332 {
1333         const struct ast_sorcery_object_wizard *object_wizard = obj;
1334         const struct sorcery_details *details = arg;
1335
1336         return (object_wizard->wizard->delete && !object_wizard->wizard->delete(details->sorcery, object_wizard->data, details->obj) &&
1337                 !object_wizard->caching) ? CMP_MATCH | CMP_STOP : 0;
1338 }
1339
1340 int ast_sorcery_delete(const struct ast_sorcery *sorcery, void *object)
1341 {
1342         const struct ast_sorcery_object_details *details = object;
1343         RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, details->type, OBJ_KEY), ao2_cleanup);
1344         RAII_VAR(struct ast_sorcery_object_wizard *, object_wizard, NULL, ao2_cleanup);
1345         struct sorcery_details sdetails = {
1346                 .sorcery = sorcery,
1347                 .obj = object,
1348         };
1349
1350         if (!object_type) {
1351                 return -1;
1352         }
1353
1354         if ((object_wizard = ao2_callback(object_type->wizards, 0, sorcery_wizard_delete, &sdetails)) &&
1355                 ao2_container_count(object_type->observers)) {
1356                 struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(object_type, object);
1357
1358                 if (invocation && ast_taskprocessor_push(object_type->serializer, sorcery_observers_notify_delete, invocation)) {
1359                         ao2_cleanup(invocation);
1360                 }
1361         }
1362
1363         return object_wizard ? 0 : -1;
1364 }
1365
1366 void ast_sorcery_unref(struct ast_sorcery *sorcery)
1367 {
1368         ao2_cleanup(sorcery);
1369 }
1370
1371 const char *ast_sorcery_object_get_id(const void *object)
1372 {
1373         const struct ast_sorcery_object_details *details = object;
1374         return details->id;
1375 }
1376
1377 const char *ast_sorcery_object_get_type(const void *object)
1378 {
1379         const struct ast_sorcery_object_details *details = object;
1380         return details->type;
1381 }
1382
1383 int ast_sorcery_observer_add(const struct ast_sorcery *sorcery, const char *type, const struct ast_sorcery_observer *callbacks)
1384 {
1385         RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
1386         struct ast_sorcery_object_type_observer *observer;
1387
1388         if (!object_type || !callbacks) {
1389                 return -1;
1390         }
1391
1392         if (!(observer = ao2_alloc(sizeof(*observer), NULL))) {
1393                 return -1;
1394         }
1395
1396         observer->callbacks = callbacks;
1397         ao2_link(object_type->observers, observer);
1398         ao2_ref(observer, -1);
1399
1400         return 0;
1401 }
1402
1403 /*! \brief Internal callback function for removing an observer */
1404 static int sorcery_observer_remove(void *obj, void *arg, int flags)
1405 {
1406         const struct ast_sorcery_object_type_observer *observer = obj;
1407
1408         return (observer->callbacks == arg) ? CMP_MATCH | CMP_STOP : 0;
1409 }
1410
1411 void ast_sorcery_observer_remove(const struct ast_sorcery *sorcery, const char *type, struct ast_sorcery_observer *callbacks)
1412 {
1413         RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
1414
1415         if (!object_type) {
1416                 return;
1417         }
1418
1419         ao2_callback(object_type->observers, OBJ_NODATA | OBJ_UNLINK, sorcery_observer_remove, callbacks);
1420 }