Asterisk - The Open Source Telephony Project GIT-master-7e7a603
res_sorcery_astdb.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 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/*!
20 * \file
21 *
22 * \brief Sorcery Astdb Object Wizard
23 *
24 * \author Joshua Colp <jcolp@digium.com>
25 */
26
27/*** MODULEINFO
28 <support_level>core</support_level>
29 ***/
30
31#include "asterisk.h"
32
33#include <regex.h>
34
35#include "asterisk/module.h"
36#include "asterisk/sorcery.h"
37#include "asterisk/astdb.h"
38#include "asterisk/json.h"
39
40static void *sorcery_astdb_open(const char *data);
41static int sorcery_astdb_create(const struct ast_sorcery *sorcery, void *data, void *object);
42static void *sorcery_astdb_retrieve_id(const struct ast_sorcery *sorcery, void *data, const char *type, const char *id);
43static void *sorcery_astdb_retrieve_fields(const struct ast_sorcery *sorcery, void *data, const char *type, const struct ast_variable *fields);
44static void sorcery_astdb_retrieve_multiple(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects,
45 const struct ast_variable *fields);
46static void sorcery_astdb_retrieve_regex(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *regex);
47static void sorcery_astdb_retrieve_prefix(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *prefix, const size_t prefix_len);
48static int sorcery_astdb_update(const struct ast_sorcery *sorcery, void *data, void *object);
49static int sorcery_astdb_delete(const struct ast_sorcery *sorcery, void *data, void *object);
50static void sorcery_astdb_close(void *data);
51
53 .name = "astdb",
54 .open = sorcery_astdb_open,
55 .create = sorcery_astdb_create,
56 .retrieve_id = sorcery_astdb_retrieve_id,
57 .retrieve_fields = sorcery_astdb_retrieve_fields,
58 .retrieve_multiple = sorcery_astdb_retrieve_multiple,
59 .retrieve_regex = sorcery_astdb_retrieve_regex,
60 .retrieve_prefix = sorcery_astdb_retrieve_prefix,
61 .update = sorcery_astdb_update,
62 .delete = sorcery_astdb_delete,
63 .close = sorcery_astdb_close,
64};
65
66static int sorcery_astdb_create(const struct ast_sorcery *sorcery, void *data, void *object)
67{
70 const char *prefix = data;
71 char family[strlen(prefix) + strlen(ast_sorcery_object_get_type(object)) + 2];
72
73 if (!objset || !(value = ast_json_dump_string(objset))) {
74 return -1;
75 }
76
77 snprintf(family, sizeof(family), "%s/%s", prefix, ast_sorcery_object_get_type(object));
78
79 return ast_db_put(family, ast_sorcery_object_get_id(object), value);
80}
81
82/*! \brief Internal helper function which returns a filtered objectset.
83 *
84 * The following are filtered out of the objectset:
85 * \li Fields that are not registered with sorcery.
86 *
87 * \param objectset Objectset to filter.
88 * \param sorcery The sorcery instance that is requesting an objectset.
89 * \param type The object type
90 *
91 * \return The filtered objectset
92 */
93static struct ast_variable *sorcery_astdb_filter_objectset(struct ast_variable *objectset, const struct ast_sorcery *sorcery,
94 const char *type)
95{
96 struct ast_variable *previous = NULL, *field = objectset;
97 struct ast_sorcery_object_type *object_type;
98
100 if (!object_type) {
101 ast_log(LOG_WARNING, "Unknown sorcery object type %s. Expect errors\n", type);
102 return objectset;
103 }
104
105 while (field) {
106 struct ast_variable *removed;
107
108 if (ast_sorcery_is_object_field_registered(object_type, field->name)) {
109 previous = field;
110 field = field->next;
111 continue;
112 }
113
114 ast_debug(1, "Filtering out astdb field '%s' from retrieval\n", field->name);
115
116 if (previous) {
117 previous->next = field->next;
118 } else {
119 objectset = field->next;
120 }
121
122 removed = field;
123 field = field->next;
124 removed->next = NULL;
125
126 ast_variables_destroy(removed);
127 }
128
129 ao2_cleanup(object_type);
130
131 return objectset;
132}
133
134/*! \brief Internal helper function which retrieves an object, or multiple objects, using fields for criteria */
135static void *sorcery_astdb_retrieve_fields_common(const struct ast_sorcery *sorcery, void *data, const char *type, const struct ast_variable *fields, struct ao2_container *objects)
136{
137 const char *prefix = data;
138 char family[strlen(prefix) + strlen(type) + 2];
139 RAII_VAR(struct ast_db_entry *, entries, NULL, ast_db_freetree);
140 struct ast_db_entry *entry;
141
142 snprintf(family, sizeof(family), "%s/%s", prefix, type);
143
144 if (!(entries = ast_db_gettree(family, NULL))) {
145 return NULL;
146 }
147
148 for (entry = entries; entry; entry = entry->next) {
149 const char *key = entry->key + strlen(family) + 2;
150 RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
151 struct ast_json_error error;
152 RAII_VAR(struct ast_variable *, existing, NULL, ast_variables_destroy);
153 void *object = NULL;
154
155 if (!(json = ast_json_load_string(entry->data, &error))) {
156 return NULL;
157 }
158
160 return NULL;
161 }
162
163 existing = sorcery_astdb_filter_objectset(existing, sorcery, type);
164
165 if (fields && !ast_variable_lists_match(existing, fields, 0)) {
166 continue;
167 }
168
169 if (!(object = ast_sorcery_alloc(sorcery, type, key)) ||
170 ast_sorcery_objectset_apply(sorcery, object, existing)) {
171 ao2_cleanup(object);
172 return NULL;
173 }
174
175 if (!objects) {
176 return object;
177 }
178
179 ao2_link(objects, object);
180 ao2_cleanup(object);
181 }
182
183 return NULL;
184}
185
186static void *sorcery_astdb_retrieve_fields(const struct ast_sorcery *sorcery, void *data, const char *type, const struct ast_variable *fields)
187{
189}
190
191static void *sorcery_astdb_retrieve_id(const struct ast_sorcery *sorcery, void *data, const char *type, const char *id)
192{
193 const char *prefix = data;
194 char family[strlen(prefix) + strlen(type) + 2];
195 RAII_VAR(char *, value, NULL, ast_free_ptr);
196 RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
197 struct ast_json_error error;
199 void *object = NULL;
200
201 snprintf(family, sizeof(family), "%s/%s", prefix, type);
202
203 if (ast_db_get_allocated(family, id, &value)
204 || !(json = ast_json_load_string(value, &error))
206 || !(objset = sorcery_astdb_filter_objectset(objset, sorcery, type))
207 || !(object = ast_sorcery_alloc(sorcery, type, id))
208 || ast_sorcery_objectset_apply(sorcery, object, objset)) {
209 ast_debug(3, "Failed to retrieve object '%s' from astdb\n", id);
210 ao2_cleanup(object);
211 return NULL;
212 }
213
214 return object;
215}
216
217static void sorcery_astdb_retrieve_multiple(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const struct ast_variable *fields)
218{
219 sorcery_astdb_retrieve_fields_common(sorcery, data, type, fields, objects);
220}
221
222/*!
223 * \internal
224 * \brief Convert regex prefix pattern to astDB prefix pattern if possible.
225 *
226 * \param tree astDB prefix pattern buffer to fill.
227 * \param regex Extended regular expression with the start anchor character '^'.
228 *
229 * \note Since this is a helper function, the tree buffer is
230 * assumed to always be large enough.
231 *
232 * \retval 0 on success.
233 * \retval -1 on error. regex is invalid.
234 */
235static int make_astdb_prefix_pattern(char *tree, const char *regex)
236{
237 const char *src;
238 char *dst;
239
240 for (dst = tree, src = regex + 1; *src; ++src) {
241 if (*src == '\\') {
242 /* Escaped regex char. */
243 ++src;
244 if (!*src) {
245 /* Invalid regex. The caller escaped the string terminator. */
246 return -1;
247 }
248 } else if (*src == '$') {
249 if (!src[1]) {
250 /* Remove the tail anchor character. */
251 *dst = '\0';
252 return 0;
253 }
254 } else if (strchr(".?*+{[(|", *src)) {
255 /*
256 * The regex is not a simple prefix pattern.
257 *
258 * XXX With more logic, it is possible to simply
259 * use the current prefix pattern. The last character
260 * needs to be removed if possible when the current regex
261 * token is "?*{". Also the rest of the regex pattern
262 * would need to be checked for subgroup/alternation.
263 * Subgroup/alternation is too complex for a simple prefix
264 * match.
265 */
266 dst = tree;
267 break;
268 }
269 *dst++ = *src;
270 }
271 if (dst != tree) {
272 *dst++ = '%';
273 }
274 *dst = '\0';
275 return 0;
276}
277
278static void sorcery_astdb_retrieve_regex(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *regex)
279{
280 const char *prefix = data;
281 char family[strlen(prefix) + strlen(type) + 2];
282 char tree[strlen(regex) + 1];
283 RAII_VAR(struct ast_db_entry *, entries, NULL, ast_db_freetree);
284 regex_t expression;
285 struct ast_db_entry *entry;
286
287 snprintf(family, sizeof(family), "%s/%s", prefix, type);
288
289 if (regex[0] == '^') {
290 /*
291 * For performance reasons, try to create an astDB prefix
292 * pattern from the regex to reduce the number of entries
293 * retrieved from astDB for regex to then match.
294 */
295 if (make_astdb_prefix_pattern(tree, regex)) {
296 return;
297 }
298 } else {
299 tree[0] = '\0';
300 }
301
302 if (!(entries = ast_db_gettree(family, tree))
303 || regcomp(&expression, regex, REG_EXTENDED | REG_NOSUB)) {
304 return;
305 }
306
307 for (entry = entries; entry; entry = entry->next) {
308 /* The key in the entry includes the family, so we need to strip it out for regex purposes */
309 const char *key = entry->key + strlen(family) + 2;
310 RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
311 struct ast_json_error error;
312 RAII_VAR(void *, object, NULL, ao2_cleanup);
314
315 if (regexec(&expression, key, 0, NULL, 0)) {
316 continue;
317 } else if (!(json = ast_json_load_string(entry->data, &error))
319 || !(objset = sorcery_astdb_filter_objectset(objset, sorcery, type))
320 || !(object = ast_sorcery_alloc(sorcery, type, key))
321 || ast_sorcery_objectset_apply(sorcery, object, objset)) {
322 regfree(&expression);
323 return;
324 }
325
326 ao2_link(objects, object);
327 }
328
329 regfree(&expression);
330}
331
332static void sorcery_astdb_retrieve_prefix(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *prefix, const size_t prefix_len)
333{
334 const char *family_prefix = data;
335 size_t family_len = strlen(family_prefix) + strlen(type) + 1; /* +1 for slash delimiter */
336 char family[family_len + 1];
337 char tree[prefix_len + 1];
338 RAII_VAR(struct ast_db_entry *, entries, NULL, ast_db_freetree);
339 struct ast_db_entry *entry;
340
341 snprintf(tree, sizeof(tree), "%.*s", (int) prefix_len, prefix);
342 snprintf(family, sizeof(family), "%s/%s", family_prefix, type);
343
344 if (!(entries = ast_db_gettree_by_prefix(family, tree))) {
345 return;
346 }
347
348 for (entry = entries; entry; entry = entry->next) {
349 /* The key in the entry includes the family, so we need to strip it out */
350 const char *key = entry->key + family_len + 2;
351 RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
352 struct ast_json_error error;
353 RAII_VAR(void *, object, NULL, ao2_cleanup);
355
356 if (!(json = ast_json_load_string(entry->data, &error))
358 || !(objset = sorcery_astdb_filter_objectset(objset, sorcery, type))
359 || !(object = ast_sorcery_alloc(sorcery, type, key))
360 || ast_sorcery_objectset_apply(sorcery, object, objset)) {
361 return;
362 }
363
364 ao2_link(objects, object);
365 }
366}
367
368static int sorcery_astdb_update(const struct ast_sorcery *sorcery, void *data, void *object)
369{
370 const char *prefix = data;
371 char family[strlen(prefix) + strlen(ast_sorcery_object_get_type(object)) + 2], value[2];
372
373 snprintf(family, sizeof(family), "%s/%s", prefix, ast_sorcery_object_get_type(object));
374
375 /* It is okay for the value to be truncated, we are only checking that it exists */
376 if (ast_db_get(family, ast_sorcery_object_get_id(object), value, sizeof(value))) {
377 return -1;
378 }
379
380 /* The only difference between update and create is that for update the object must already exist */
381 return sorcery_astdb_create(sorcery, data, object);
382}
383
384static int sorcery_astdb_delete(const struct ast_sorcery *sorcery, void *data, void *object)
385{
386 const char *prefix = data;
387 char family[strlen(prefix) + strlen(ast_sorcery_object_get_type(object)) + 2];
388 char value[2];
389
390 snprintf(family, sizeof(family), "%s/%s", prefix, ast_sorcery_object_get_type(object));
391
392 if (ast_db_get(family, ast_sorcery_object_get_id(object), value, sizeof(value))) {
393 return -1;
394 }
395
396 return ast_db_del(family, ast_sorcery_object_get_id(object));
397}
398
399static void *sorcery_astdb_open(const char *data)
400{
401 /* We require a prefix for family string generation, or else stuff could mix together */
402 if (ast_strlen_zero(data)) {
403 return NULL;
404 }
405
406 return ast_strdup(data);
407}
408
409static void sorcery_astdb_close(void *data)
410{
411 ast_free(data);
412}
413
414static int load_module(void)
415{
418 }
419
421}
422
423static int unload_module(void)
424{
426 return 0;
427}
428
430 .support_level = AST_MODULE_SUPPORT_CORE,
431 .load = load_module,
432 .unload = unload_module,
433 .load_pri = AST_MODPRI_REALTIME_DRIVER,
Persistent data storage (akin to *doze registry)
int ast_db_put(const char *family, const char *key, const char *value)
Store value addressed by family/key.
Definition: main/db.c:342
struct ast_db_entry * ast_db_gettree_by_prefix(const char *family, const char *key_prefix)
Get a list of values with the given key prefix.
Definition: main/db.c:650
int ast_db_get_allocated(const char *family, const char *key, char **out)
Get key value specified by family/key as a heap allocated string.
Definition: main/db.c:437
int ast_db_get(const char *family, const char *key, char *value, int valuelen)
Get key value specified by family/key.
Definition: main/db.c:427
int ast_db_del(const char *family, const char *key)
Delete entry in astdb.
Definition: main/db.c:476
struct ast_db_entry * ast_db_gettree(const char *family, const char *keytree)
Get a list of values within the astdb tree.
Definition: main/db.c:610
void ast_db_freetree(struct ast_db_entry *entry)
Free structure created by ast_db_gettree()
Definition: main/db.c:677
Asterisk main include file. File version handling, generic pbx functions.
#define ast_free(a)
Definition: astmm.h:180
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
void ast_free_ptr(void *ptr)
free() wrapper
Definition: astmm.c:1739
#define ast_log
Definition: astobj2.c:42
#define ao2_link(container, obj)
Add an object to a container.
Definition: astobj2.h:1532
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
static const char type[]
Definition: chan_ooh323.c:109
static int regex(struct ast_channel *chan, const char *cmd, char *parse, char *buf, size_t len)
static char prefix[MAX_PREFIX]
Definition: http.c:144
int ast_variable_lists_match(const struct ast_variable *left, const struct ast_variable *right, int exact_match)
Tests 2 variable lists to see if they match.
Definition: main/config.c:861
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1262
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_WARNING
Asterisk JSON abstraction layer.
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
Definition: json.c:73
void ast_json_free(void *p)
Asterisk's custom JSON allocator. Exposed for use by unit tests.
Definition: json.c:52
#define ast_json_dump_string(root)
Encode a JSON value to a compact string.
Definition: json.h:810
struct ast_json * ast_json_load_string(const char *input, struct ast_json_error *error)
Parse null terminated string into a JSON object or array.
Definition: json.c:567
@ AST_JSON_TO_AST_VARS_CODE_SUCCESS
Conversion successful.
Definition: json.h:1111
enum ast_json_to_ast_vars_code ast_json_to_ast_variables(struct ast_json *json_variables, struct ast_variable **variables)
Convert a ast_json list of key/value pair tuples into a ast_variable list.
Definition: json.c:818
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition: module.h:317
@ AST_MODFLAG_GLOBAL_SYMBOLS
Definition: module.h:316
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:543
@ AST_MODPRI_REALTIME_DRIVER
Definition: module.h:323
@ AST_MODULE_SUPPORT_CORE
Definition: module.h:121
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
static struct ast_sorcery * sorcery
static struct ast_sorcery_wizard astdb_object_wizard
static void sorcery_astdb_retrieve_multiple(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const struct ast_variable *fields)
static void * sorcery_astdb_retrieve_fields(const struct ast_sorcery *sorcery, void *data, const char *type, const struct ast_variable *fields)
static void * sorcery_astdb_retrieve_id(const struct ast_sorcery *sorcery, void *data, const char *type, const char *id)
static int sorcery_astdb_delete(const struct ast_sorcery *sorcery, void *data, void *object)
static int sorcery_astdb_update(const struct ast_sorcery *sorcery, void *data, void *object)
static void sorcery_astdb_retrieve_regex(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *regex)
static void sorcery_astdb_retrieve_prefix(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *prefix, const size_t prefix_len)
static struct ast_variable * sorcery_astdb_filter_objectset(struct ast_variable *objectset, const struct ast_sorcery *sorcery, const char *type)
Internal helper function which returns a filtered objectset.
static void sorcery_astdb_close(void *data)
static int load_module(void)
static int sorcery_astdb_create(const struct ast_sorcery *sorcery, void *data, void *object)
static int make_astdb_prefix_pattern(char *tree, const char *regex)
static int unload_module(void)
static void * sorcery_astdb_open(const char *data)
static void * sorcery_astdb_retrieve_fields_common(const struct ast_sorcery *sorcery, void *data, const char *type, const struct ast_variable *fields, struct ao2_container *objects)
Internal helper function which retrieves an object, or multiple objects, using fields for criteria.
#define NULL
Definition: resample.c:96
Sorcery Data Access Layer API.
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
Definition: sorcery.c:2317
int ast_sorcery_wizard_unregister(const struct ast_sorcery_wizard *interface)
Unregister a sorcery wizard.
Definition: sorcery.c:474
const char * ast_sorcery_object_get_type(const void *object)
Get the type of a sorcery object.
Definition: sorcery.c:2329
struct ast_sorcery_object_type * ast_sorcery_get_object_type(const struct ast_sorcery *sorcery, const char *type)
Get the sorcery object type given a type name.
Definition: sorcery.c:2494
#define ast_sorcery_wizard_register(interface)
See __ast_sorcery_wizard_register()
Definition: sorcery.h:383
int ast_sorcery_is_object_field_registered(const struct ast_sorcery_object_type *object_type, const char *field_name)
Determine if a particular object field has been registered with sorcery.
Definition: sorcery.c:2514
void * ast_sorcery_alloc(const struct ast_sorcery *sorcery, const char *type, const char *id)
Allocate an object.
Definition: sorcery.c:1744
int ast_sorcery_objectset_apply(const struct ast_sorcery *sorcery, void *object, struct ast_variable *objectset)
Apply an object set (KVP list) to an object.
Definition: sorcery.c:1632
struct ast_json * ast_sorcery_objectset_json_create(const struct ast_sorcery *sorcery, const void *object)
Create an object set in JSON format for an object.
Definition: sorcery.c:1565
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
Generic container type.
Definition: astdb.h:31
char * key
Definition: astdb.h:33
JSON parsing error information.
Definition: json.h:887
Abstract JSON element (object, array, string, int, ...).
Structure for registered object type.
Definition: sorcery.c:148
char name[MAX_OBJECT_TYPE]
Unique name of the object type.
Definition: sorcery.c:150
Interface for a sorcery wizard.
Definition: sorcery.h:276
const char * name
Name of the wizard.
Definition: sorcery.h:278
Full structure for sorcery.
Definition: sorcery.c:230
Structure for variables, used for configurations and for channel variables.
struct ast_variable * next
Definition: search.h:40
char * key
Definition: search.h:41
char * data
Definition: search.h:42
int value
Definition: syslog.c:37
int error(const char *format,...)
Definition: utils/frame.c:999
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941