bucket.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2013, Digium, Inc.
00005  *
00006  * Joshua Colp <jcolp@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Bucket File API
00022  *
00023  * \author Joshua Colp <jcolp@digium.com>
00024  */
00025 
00026 /*** MODULEINFO
00027    <use type="external">uriparser</use>
00028    <support_level>core</support_level>
00029  ***/
00030 
00031 /*** DOCUMENTATION
00032         <configInfo name="core" language="en_US">
00033                 <synopsis>Bucket file API</synopsis>
00034                 <configFile name="bucket">
00035                         <configObject name="bucket">
00036                                 <configOption name="scheme">
00037                                         <synopsis>Scheme in use for bucket</synopsis>
00038                                 </configOption>
00039                                 <configOption name="created">
00040                                         <synopsis>Time at which the bucket was created</synopsis>
00041                                 </configOption>
00042                                 <configOption name="modified">
00043                                         <synopsis>Time at which the bucket was last modified</synopsis>
00044                                 </configOption>
00045                         </configObject>
00046                         <configObject name="file">
00047                                 <configOption name="scheme">
00048                                         <synopsis>Scheme in use for file</synopsis>
00049                                 </configOption>
00050                                 <configOption name="created">
00051                                         <synopsis>Time at which the file was created</synopsis>
00052                                 </configOption>
00053                                 <configOption name="modified">
00054                                         <synopsis>Time at which the file was last modified</synopsis>
00055                                 </configOption>
00056                         </configObject>
00057                 </configFile>
00058         </configInfo>
00059 ***/
00060 
00061 #include "asterisk.h"
00062 
00063 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 433421 $")
00064 
00065 #ifdef HAVE_URIPARSER
00066 #include <uriparser/Uri.h>
00067 #endif
00068 
00069 #include "asterisk/logger.h"
00070 #include "asterisk/sorcery.h"
00071 #include "asterisk/bucket.h"
00072 #include "asterisk/config_options.h"
00073 #include "asterisk/astobj2.h"
00074 #include "asterisk/strings.h"
00075 #include "asterisk/json.h"
00076 #include "asterisk/file.h"
00077 #include "asterisk/module.h"
00078 
00079 /*! \brief Number of buckets for the container of schemes */
00080 #define SCHEME_BUCKETS 53
00081 
00082 /*! \brief Number of buckets for the container of metadata in a file */
00083 #define METADATA_BUCKETS 53
00084 
00085 /*! \brief Sorcery instance for all bucket operations */
00086 static struct ast_sorcery *bucket_sorcery;
00087 
00088 /*! \brief Container of registered schemes */
00089 static struct ao2_container *schemes;
00090 
00091 /*! \brief Structure for available schemes */
00092 struct ast_bucket_scheme {
00093    /*! \brief Wizard for buckets */
00094    struct ast_sorcery_wizard *bucket;
00095    /*! \brief Wizard for files */
00096    struct ast_sorcery_wizard *file;
00097    /*! \brief Pointer to the file snapshot creation callback */
00098    bucket_file_create_cb create;
00099    /*! \brief Pointer to the file snapshot destruction callback */
00100    bucket_file_destroy_cb destroy;
00101    /*! \brief Name of the scheme */
00102    char name[0];
00103 };
00104 
00105 /*! \brief Callback function for creating a bucket */
00106 static int bucket_wizard_create(const struct ast_sorcery *sorcery, void *data, void *object)
00107 {
00108    struct ast_bucket *bucket = object;
00109 
00110    return bucket->scheme_impl->bucket->create(sorcery, data, object);
00111 }
00112 
00113 /*! \brief Callback function for retrieving a bucket */
00114 static void *bucket_wizard_retrieve(const struct ast_sorcery *sorcery, void *data, const char *type,
00115    const char *id)
00116 {
00117 #ifdef HAVE_URIPARSER
00118    UriParserStateA state;
00119    UriUriA uri;
00120    size_t len;
00121 #else
00122    char *tmp = ast_strdupa(id);
00123 #endif
00124    SCOPED_AO2RDLOCK(lock, schemes);
00125    char *uri_scheme;
00126    RAII_VAR(struct ast_bucket_scheme *, scheme, NULL, ao2_cleanup);
00127 
00128 #ifdef HAVE_URIPARSER
00129    state.uri = &uri;
00130    if (uriParseUriA(&state, id) != URI_SUCCESS ||
00131       !uri.scheme.first || !uri.scheme.afterLast) {
00132       uriFreeUriMembersA(&uri);
00133       return NULL;
00134    }
00135 
00136    len = (uri.scheme.afterLast - uri.scheme.first) + 1;
00137    uri_scheme = ast_alloca(len);
00138    ast_copy_string(uri_scheme, uri.scheme.first, len);
00139 
00140    uriFreeUriMembersA(&uri);
00141 #else
00142    uri_scheme = tmp;
00143    if (!(tmp = strchr(uri_scheme, ':'))) {
00144       return NULL;
00145    }
00146    *tmp = '\0';
00147 #endif
00148 
00149    scheme = ao2_find(schemes, uri_scheme, OBJ_KEY | OBJ_NOLOCK);
00150 
00151    if (!scheme) {
00152       return NULL;
00153    }
00154 
00155    return scheme->bucket->retrieve_id(sorcery, data, type, id);
00156 }
00157 
00158 /*! \brief Callback function for deleting a bucket */
00159 static int bucket_wizard_delete(const struct ast_sorcery *sorcery, void *data, void *object)
00160 {
00161    struct ast_bucket *bucket = object;
00162 
00163    return bucket->scheme_impl->bucket->delete(sorcery, data, object);
00164 }
00165 
00166 /*! \brief Intermediary bucket wizard */
00167 static struct ast_sorcery_wizard bucket_wizard = {
00168    .name = "bucket",
00169    .create = bucket_wizard_create,
00170    .retrieve_id = bucket_wizard_retrieve,
00171    .delete = bucket_wizard_delete,
00172 };
00173 
00174 /*! \brief Callback function for creating a bucket file */
00175 static int bucket_file_wizard_create(const struct ast_sorcery *sorcery, void *data, void *object)
00176 {
00177    struct ast_bucket_file *file = object;
00178 
00179    return file->scheme_impl->file->create(sorcery, data, object);
00180 }
00181 
00182 /*! \brief Callback function for retrieving a bucket file */
00183 static void *bucket_file_wizard_retrieve(const struct ast_sorcery *sorcery, void *data, const char *type,
00184    const char *id)
00185 {
00186 #ifdef HAVE_URIPARSER
00187    UriParserStateA state;
00188    UriUriA uri;
00189    size_t len;
00190 #else
00191    char *tmp = ast_strdupa(id);
00192 #endif
00193    char *uri_scheme;
00194    SCOPED_AO2RDLOCK(lock, schemes);
00195    RAII_VAR(struct ast_bucket_scheme *, scheme, NULL, ao2_cleanup);
00196 
00197 #ifdef HAVE_URIPARSER
00198    state.uri = &uri;
00199    if (uriParseUriA(&state, id) != URI_SUCCESS ||
00200       !uri.scheme.first || !uri.scheme.afterLast) {
00201       uriFreeUriMembersA(&uri);
00202       return NULL;
00203    }
00204 
00205    len = (uri.scheme.afterLast - uri.scheme.first) + 1;
00206    uri_scheme = ast_alloca(len);
00207    ast_copy_string(uri_scheme, uri.scheme.first, len);
00208 
00209    uriFreeUriMembersA(&uri);
00210 #else
00211    uri_scheme = tmp;
00212    if (!(tmp = strchr(uri_scheme, ':'))) {
00213       return NULL;
00214    }
00215    *tmp = '\0';
00216 #endif
00217 
00218    scheme = ao2_find(schemes, uri_scheme, OBJ_KEY | OBJ_NOLOCK);
00219 
00220    if (!scheme) {
00221       return NULL;
00222    }
00223 
00224    return scheme->file->retrieve_id(sorcery, data, type, id);
00225 }
00226 
00227 /*! \brief Callback function for updating a bucket file */
00228 static int bucket_file_wizard_update(const struct ast_sorcery *sorcery, void *data, void *object)
00229 {
00230    struct ast_bucket_file *file = object;
00231 
00232    return file->scheme_impl->file->update(sorcery, data, object);
00233 }
00234 
00235 /*! \brief Callback function for deleting a bucket file */
00236 static int bucket_file_wizard_delete(const struct ast_sorcery *sorcery, void *data, void *object)
00237 {
00238    struct ast_bucket_file *file = object;
00239 
00240    return file->scheme_impl->file->delete(sorcery, data, object);
00241 }
00242 
00243 /*! \brief Intermediary file wizard */
00244 static struct ast_sorcery_wizard bucket_file_wizard = {
00245    .name = "bucket_file",
00246    .create = bucket_file_wizard_create,
00247    .retrieve_id = bucket_file_wizard_retrieve,
00248    .update = bucket_file_wizard_update,
00249    .delete = bucket_file_wizard_delete,
00250 };
00251 
00252 int __ast_bucket_scheme_register(const char *name, struct ast_sorcery_wizard *bucket,
00253    struct ast_sorcery_wizard *file, bucket_file_create_cb create_cb,
00254    bucket_file_destroy_cb destroy_cb, struct ast_module *module)
00255 {
00256    SCOPED_AO2WRLOCK(lock, schemes);
00257    RAII_VAR(struct ast_bucket_scheme *, scheme, NULL, ao2_cleanup);
00258 
00259    if (ast_strlen_zero(name) || !bucket || !file ||
00260        !bucket->create || !bucket->delete || !bucket->retrieve_id ||
00261        !create_cb) {
00262       return -1;
00263    }
00264 
00265    scheme = ao2_find(schemes, name, OBJ_KEY | OBJ_NOLOCK);
00266    if (scheme) {
00267       return -1;
00268    }
00269 
00270    scheme = ao2_alloc(sizeof(*scheme) + strlen(name) + 1, NULL);
00271    if (!scheme) {
00272       return -1;
00273    }
00274 
00275    strcpy(scheme->name, name);
00276    scheme->bucket = bucket;
00277    scheme->file = file;
00278    scheme->create = create_cb;
00279    scheme->destroy = destroy_cb;
00280 
00281    ao2_link_flags(schemes, scheme, OBJ_NOLOCK);
00282 
00283    ast_verb(2, "Registered bucket scheme '%s'\n", name);
00284 
00285    ast_module_shutdown_ref(module);
00286 
00287    return 0;
00288 }
00289 
00290 /*! \brief Allocator for metadata attributes */
00291 static struct ast_bucket_metadata *bucket_metadata_alloc(const char *name, const char *value)
00292 {
00293    int name_len = strlen(name) + 1, value_len = strlen(value) + 1;
00294    struct ast_bucket_metadata *metadata = ao2_alloc(sizeof(*metadata) + name_len + value_len, NULL);
00295    char *dst;
00296 
00297    if (!metadata) {
00298       return NULL;
00299    }
00300 
00301    dst = metadata->data;
00302    metadata->name = strcpy(dst, name);
00303    dst += name_len;
00304    metadata->value = strcpy(dst, value);
00305 
00306    return metadata;
00307 }
00308 
00309 int ast_bucket_file_metadata_set(struct ast_bucket_file *file, const char *name, const char *value)
00310 {
00311    RAII_VAR(struct ast_bucket_metadata *, metadata, bucket_metadata_alloc(name, value), ao2_cleanup);
00312 
00313    if (!metadata) {
00314       return -1;
00315    }
00316 
00317    ao2_find(file->metadata, name, OBJ_NODATA | OBJ_UNLINK | OBJ_KEY);
00318    ao2_link(file->metadata, metadata);
00319 
00320    return 0;
00321 }
00322 
00323 int ast_bucket_file_metadata_unset(struct ast_bucket_file *file, const char *name)
00324 {
00325    RAII_VAR(struct ast_bucket_metadata *, metadata, ao2_find(file->metadata, name, OBJ_UNLINK | OBJ_KEY), ao2_cleanup);
00326 
00327    if (!metadata) {
00328       return -1;
00329    }
00330 
00331    return 0;
00332 }
00333 
00334 struct ast_bucket_metadata *ast_bucket_file_metadata_get(struct ast_bucket_file *file, const char *name)
00335 {
00336    return ao2_find(file->metadata, name, OBJ_KEY);
00337 }
00338 
00339 /*! \brief Destructor for buckets */
00340 static void bucket_destroy(void *obj)
00341 {
00342    struct ast_bucket *bucket = obj;
00343 
00344    ao2_cleanup(bucket->scheme_impl);
00345    ast_string_field_free_memory(bucket);
00346    ao2_cleanup(bucket->buckets);
00347    ao2_cleanup(bucket->files);
00348 }
00349 
00350 /*! \brief Sorting function for red black tree string container */
00351 static int bucket_rbtree_str_sort_cmp(const void *obj_left, const void *obj_right, int flags)
00352 {
00353    const char *str_left = obj_left;
00354    const char *str_right = obj_right;
00355    int cmp = 0;
00356 
00357    switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
00358    default:
00359    case OBJ_POINTER:
00360    case OBJ_KEY:
00361       cmp = strcmp(str_left, str_right);
00362       break;
00363    case OBJ_PARTIAL_KEY:
00364       cmp = strncmp(str_left, str_right, strlen(str_right));
00365       break;
00366    }
00367    return cmp;
00368 }
00369 
00370 /*! \brief Allocator for buckets */
00371 static void *bucket_alloc(const char *name)
00372 {
00373    RAII_VAR(struct ast_bucket *, bucket, NULL, ao2_cleanup);
00374 
00375    bucket = ast_sorcery_generic_alloc(sizeof(*bucket), bucket_destroy);
00376    if (!bucket) {
00377       return NULL;
00378    }
00379 
00380    if (ast_string_field_init(bucket, 128)) {
00381       return NULL;
00382    }
00383 
00384    bucket->buckets = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_NOLOCK,
00385       AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT, bucket_rbtree_str_sort_cmp, NULL);
00386    if (!bucket->buckets) {
00387       return NULL;
00388    }
00389 
00390    bucket->files = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_NOLOCK,
00391       AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT, bucket_rbtree_str_sort_cmp, NULL);
00392    if (!bucket->files) {
00393       return NULL;
00394    }
00395 
00396    ao2_ref(bucket, +1);
00397    return bucket;
00398 }
00399 
00400 struct ast_bucket *ast_bucket_alloc(const char *uri)
00401 {
00402 #ifdef HAVE_URIPARSER
00403    UriParserStateA state;
00404    UriUriA full_uri;
00405    size_t len;
00406 #else
00407    char *tmp = ast_strdupa(uri);
00408 #endif
00409    char *uri_scheme;
00410    RAII_VAR(struct ast_bucket_scheme *, scheme, NULL, ao2_cleanup);
00411    struct ast_bucket *bucket;
00412 
00413    if (ast_strlen_zero(uri)) {
00414       return NULL;
00415    }
00416 
00417 #ifdef HAVE_URIPARSER
00418    state.uri = &full_uri;
00419    if (uriParseUriA(&state, uri) != URI_SUCCESS ||
00420       !full_uri.scheme.first || !full_uri.scheme.afterLast ||
00421       !full_uri.pathTail) {
00422       uriFreeUriMembersA(&full_uri);
00423       return NULL;
00424    }
00425 
00426    len = (full_uri.scheme.afterLast - full_uri.scheme.first) + 1;
00427    uri_scheme = ast_alloca(len);
00428    ast_copy_string(uri_scheme, full_uri.scheme.first, len);
00429 
00430    uriFreeUriMembersA(&full_uri);
00431 #else
00432    uri_scheme = tmp;
00433    if (!(tmp = strchr(uri_scheme, ':'))) {
00434       return NULL;
00435    }
00436    *tmp = '\0';
00437 #endif
00438 
00439    scheme = ao2_find(schemes, uri_scheme, OBJ_KEY);
00440    if (!scheme) {
00441       return NULL;
00442    }
00443 
00444    bucket = ast_sorcery_alloc(bucket_sorcery, "bucket", uri);
00445    if (!bucket) {
00446       return NULL;
00447    }
00448 
00449    ao2_ref(scheme, +1);
00450    bucket->scheme_impl = scheme;
00451 
00452    ast_string_field_set(bucket, scheme, uri_scheme);
00453 
00454    return bucket;
00455 }
00456 
00457 int ast_bucket_create(struct ast_bucket *bucket)
00458 {
00459    return ast_sorcery_create(bucket_sorcery, bucket);
00460 }
00461 
00462 struct ast_bucket *ast_bucket_retrieve(const char *uri)
00463 {
00464    if (ast_strlen_zero(uri)) {
00465       return NULL;
00466    }
00467 
00468    return ast_sorcery_retrieve_by_id(bucket_sorcery, "bucket", uri);
00469 }
00470 
00471 int ast_bucket_observer_add(const struct ast_sorcery_observer *callbacks)
00472 {
00473    return ast_sorcery_observer_add(bucket_sorcery, "bucket", callbacks);
00474 }
00475 
00476 void ast_bucket_observer_remove(const struct ast_sorcery_observer *callbacks)
00477 {
00478    ast_sorcery_observer_remove(bucket_sorcery, "bucket", callbacks);
00479 }
00480 
00481 int ast_bucket_delete(struct ast_bucket *bucket)
00482 {
00483    return ast_sorcery_delete(bucket_sorcery, bucket);
00484 }
00485 
00486 struct ast_json *ast_bucket_json(const struct ast_bucket *bucket)
00487 {
00488    RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
00489    struct ast_json *id, *files, *buckets;
00490    struct ao2_iterator i;
00491    char *uri;
00492    int res = 0;
00493 
00494    json = ast_sorcery_objectset_json_create(bucket_sorcery, bucket);
00495    if (!json) {
00496       return NULL;
00497    }
00498 
00499    id = ast_json_string_create(ast_sorcery_object_get_id(bucket));
00500    if (!id) {
00501       return NULL;
00502    }
00503 
00504    if (ast_json_object_set(json, "id", id)) {
00505       return NULL;
00506    }
00507 
00508    buckets = ast_json_array_create();
00509    if (!buckets) {
00510       return NULL;
00511    }
00512 
00513    if (ast_json_object_set(json, "buckets", buckets)) {
00514       return NULL;
00515    }
00516 
00517    i = ao2_iterator_init(bucket->buckets, 0);
00518    for (; (uri = ao2_iterator_next(&i)); ao2_ref(uri, -1)) {
00519       struct ast_json *bucket_uri = ast_json_string_create(uri);
00520 
00521       if (!bucket_uri || ast_json_array_append(buckets, bucket_uri)) {
00522          res = -1;
00523          ao2_ref(uri, -1);
00524          break;
00525       }
00526    }
00527    ao2_iterator_destroy(&i);
00528 
00529    if (res) {
00530       return NULL;
00531    }
00532 
00533    files = ast_json_array_create();
00534    if (!files) {
00535       return NULL;
00536    }
00537 
00538    if (ast_json_object_set(json, "files", files)) {
00539       return NULL;
00540    }
00541 
00542    i = ao2_iterator_init(bucket->files, 0);
00543    for (; (uri = ao2_iterator_next(&i)); ao2_ref(uri, -1)) {
00544       struct ast_json *file_uri = ast_json_string_create(uri);
00545 
00546       if (!file_uri || ast_json_array_append(files, file_uri)) {
00547          res = -1;
00548          ao2_ref(uri, -1);
00549          break;
00550       }
00551    }
00552    ao2_iterator_destroy(&i);
00553 
00554    if (res) {
00555       return NULL;
00556    }
00557 
00558    ast_json_ref(json);
00559    return json;
00560 }
00561 
00562 /*! \brief Hashing function for file metadata */
00563 static int bucket_file_metadata_hash(const void *obj, const int flags)
00564 {
00565    const struct ast_bucket_metadata *object;
00566    const char *key;
00567 
00568    switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
00569    case OBJ_KEY:
00570       key = obj;
00571       return ast_str_hash(key);
00572    case OBJ_POINTER:
00573       object = obj;
00574       return ast_str_hash(object->name);
00575    default:
00576       /* Hash can only work on something with a full key */
00577       ast_assert(0);
00578       return 0;
00579    }
00580 }
00581 
00582 /*! \brief Comparison function for file metadata */
00583 static int bucket_file_metadata_cmp(void *obj, void *arg, int flags)
00584 {
00585    struct ast_bucket_metadata *metadata1 = obj, *metadata2 = arg;
00586    const char *name = arg;
00587 
00588    return !strcmp(metadata1->name, flags & OBJ_KEY ? name : metadata2->name) ? CMP_MATCH | CMP_STOP : 0;
00589 }
00590 
00591 /*! \brief Destructor for bucket files */
00592 static void bucket_file_destroy(void *obj)
00593 {
00594    struct ast_bucket_file *file = obj;
00595 
00596    if (file->scheme_impl->destroy) {
00597       file->scheme_impl->destroy(file);
00598    }
00599 
00600    ao2_cleanup(file->scheme_impl);
00601    ao2_cleanup(file->metadata);
00602 }
00603 
00604 /*! \brief Allocator for bucket files */
00605 static void *bucket_file_alloc(const char *name)
00606 {
00607    RAII_VAR(struct ast_bucket_file *, file, NULL, ao2_cleanup);
00608 
00609    file = ast_sorcery_generic_alloc(sizeof(*file), bucket_file_destroy);
00610    if (!file) {
00611       return NULL;
00612    }
00613 
00614    if (ast_string_field_init(file, 128)) {
00615       return NULL;
00616    }
00617 
00618    file->metadata = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, METADATA_BUCKETS,
00619       bucket_file_metadata_hash, bucket_file_metadata_cmp);
00620    if (!file->metadata) {
00621       return NULL;
00622    }
00623 
00624    ao2_ref(file, +1);
00625    return file;
00626 }
00627 
00628 struct ast_bucket_file *ast_bucket_file_alloc(const char *uri)
00629 {
00630 #ifdef HAVE_URIPARSER
00631    UriParserStateA state;
00632    UriUriA full_uri;
00633    size_t len;
00634 #else
00635    char *tmp = ast_strdupa(uri);
00636 #endif
00637    char *uri_scheme;
00638    RAII_VAR(struct ast_bucket_scheme *, scheme, NULL, ao2_cleanup);
00639    struct ast_bucket_file *file;
00640 
00641    if (ast_strlen_zero(uri)) {
00642       return NULL;
00643    }
00644 
00645 #ifdef HAVE_URIPARSER
00646    state.uri = &full_uri;
00647    if (uriParseUriA(&state, uri) != URI_SUCCESS ||
00648       !full_uri.scheme.first || !full_uri.scheme.afterLast ||
00649       !full_uri.pathTail) {
00650       uriFreeUriMembersA(&full_uri);
00651       return NULL;
00652    }
00653 
00654    len = (full_uri.scheme.afterLast - full_uri.scheme.first) + 1;
00655    uri_scheme = ast_alloca(len);
00656    ast_copy_string(uri_scheme, full_uri.scheme.first, len);
00657 
00658    uriFreeUriMembersA(&full_uri);
00659 #else
00660    uri_scheme = tmp;
00661    if (!(tmp = strchr(uri_scheme, ':'))) {
00662       return NULL;
00663    }
00664    *tmp = '\0';
00665 #endif
00666 
00667    scheme = ao2_find(schemes, uri_scheme, OBJ_KEY);
00668    if (!scheme) {
00669       return NULL;
00670    }
00671 
00672    file = ast_sorcery_alloc(bucket_sorcery, "file", uri);
00673    if (!file) {
00674       return NULL;
00675    }
00676 
00677    ao2_ref(scheme, +1);
00678    file->scheme_impl = scheme;
00679 
00680    ast_string_field_set(file, scheme, uri_scheme);
00681 
00682    if (scheme->create(file)) {
00683       ao2_ref(file, -1);
00684       return NULL;
00685    }
00686 
00687    return file;
00688 }
00689 
00690 int ast_bucket_file_create(struct ast_bucket_file *file)
00691 {
00692    return ast_sorcery_create(bucket_sorcery, file);
00693 }
00694 
00695 /*! \brief Copy a file, shamelessly taken from file.c */
00696 static int bucket_copy(const char *infile, const char *outfile)
00697 {
00698    int ifd, ofd, len;
00699    char buf[4096];   /* XXX make it lerger. */
00700 
00701    if ((ifd = open(infile, O_RDONLY)) < 0) {
00702       ast_log(LOG_WARNING, "Unable to open %s in read-only mode, error: %s\n", infile, strerror(errno));
00703       return -1;
00704    }
00705    if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, AST_FILE_MODE)) < 0) {
00706       ast_log(LOG_WARNING, "Unable to open %s in write-only mode, error: %s\n", outfile, strerror(errno));
00707       close(ifd);
00708       return -1;
00709    }
00710    while ( (len = read(ifd, buf, sizeof(buf)) ) ) {
00711       int res;
00712       if (len < 0) {
00713          ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
00714          break;
00715       }
00716       /* XXX handle partial writes */
00717       res = write(ofd, buf, len);
00718       if (res != len) {
00719          ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
00720          len = -1; /* error marker */
00721          break;
00722       }
00723    }
00724    close(ifd);
00725    close(ofd);
00726    if (len < 0) {
00727       unlink(outfile);
00728       return -1; /* error */
00729    }
00730    return 0;   /* success */
00731 }
00732 
00733 struct ast_bucket_file *ast_bucket_file_copy(struct ast_bucket_file *file, const char *uri)
00734 {
00735    RAII_VAR(struct ast_bucket_file *, copy, ast_bucket_file_alloc(uri), ao2_cleanup);
00736 
00737    if (!copy) {
00738       return NULL;
00739    }
00740 
00741    ao2_cleanup(copy->metadata);
00742    copy->metadata = ao2_container_clone(file->metadata, 0);
00743    if (!copy->metadata ||
00744       bucket_copy(file->path, copy->path)) {
00745       return NULL;
00746    }
00747 
00748    ao2_ref(copy, +1);
00749    return copy;
00750 }
00751 
00752 struct ast_bucket_file *ast_bucket_file_retrieve(const char *uri)
00753 {
00754    if (ast_strlen_zero(uri)) {
00755       return NULL;
00756    }
00757 
00758    return ast_sorcery_retrieve_by_id(bucket_sorcery, "file", uri);
00759 }
00760 
00761 int ast_bucket_file_observer_add(const struct ast_sorcery_observer *callbacks)
00762 {
00763    return ast_sorcery_observer_add(bucket_sorcery, "file", callbacks);
00764 }
00765 
00766 void ast_bucket_file_observer_remove(const struct ast_sorcery_observer *callbacks)
00767 {
00768    ast_sorcery_observer_remove(bucket_sorcery, "file", callbacks);
00769 }
00770 
00771 int ast_bucket_file_update(struct ast_bucket_file *file)
00772 {
00773    return ast_sorcery_update(bucket_sorcery, file);
00774 }
00775 
00776 int ast_bucket_file_delete(struct ast_bucket_file *file)
00777 {
00778    return ast_sorcery_delete(bucket_sorcery, file);
00779 }
00780 
00781 struct ast_json *ast_bucket_file_json(const struct ast_bucket_file *file)
00782 {
00783    RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
00784    struct ast_json *id, *metadata;
00785    struct ao2_iterator i;
00786    struct ast_bucket_metadata *attribute;
00787    int res = 0;
00788 
00789    json = ast_sorcery_objectset_json_create(bucket_sorcery, file);
00790    if (!json) {
00791       return NULL;
00792    }
00793 
00794    id = ast_json_string_create(ast_sorcery_object_get_id(file));
00795    if (!id) {
00796       return NULL;
00797    }
00798 
00799    if (ast_json_object_set(json, "id", id)) {
00800       return NULL;
00801    }
00802 
00803    metadata = ast_json_object_create();
00804    if (!metadata) {
00805       return NULL;
00806    }
00807 
00808    if (ast_json_object_set(json, "metadata", metadata)) {
00809       return NULL;
00810    }
00811 
00812    i = ao2_iterator_init(file->metadata, 0);
00813    for (; (attribute = ao2_iterator_next(&i)); ao2_ref(attribute, -1)) {
00814       struct ast_json *value = ast_json_string_create(attribute->value);
00815 
00816       if (!value || ast_json_object_set(metadata, attribute->name, value)) {
00817          res = -1;
00818          break;
00819       }
00820    }
00821    ao2_iterator_destroy(&i);
00822 
00823    if (res) {
00824       return NULL;
00825    }
00826 
00827    ast_json_ref(json);
00828    return json;
00829 }
00830 
00831 int ast_bucket_file_temporary_create(struct ast_bucket_file *file)
00832 {
00833    int fd;
00834 
00835    ast_copy_string(file->path, "/tmp/bucket-XXXXXX", sizeof(file->path));
00836 
00837    fd = mkstemp(file->path);
00838    if (fd < 0) {
00839       return -1;
00840    }
00841 
00842    close(fd);
00843    return 0;
00844 }
00845 
00846 void ast_bucket_file_temporary_destroy(struct ast_bucket_file *file)
00847 {
00848    if (!ast_strlen_zero(file->path)) {
00849       unlink(file->path);
00850    }
00851 }
00852 
00853 /*! \brief Hashing function for scheme container */
00854 static int bucket_scheme_hash(const void *obj, const int flags)
00855 {
00856    const struct ast_bucket_scheme *object;
00857    const char *key;
00858 
00859    switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
00860    case OBJ_KEY:
00861       key = obj;
00862       return ast_str_hash(key);
00863    case OBJ_POINTER:
00864       object = obj;
00865       return ast_str_hash(object->name);
00866    default:
00867       /* Hash can only work on something with a full key */
00868       ast_assert(0);
00869       return 0;
00870    }
00871 }
00872 
00873 /*! \brief Comparison function for scheme container */
00874 static int bucket_scheme_cmp(void *obj, void *arg, int flags)
00875 {
00876    struct ast_bucket_scheme *scheme1 = obj, *scheme2 = arg;
00877    const char *name = arg;
00878 
00879    return !strcmp(scheme1->name, flags & OBJ_KEY ? name : scheme2->name) ? CMP_MATCH | CMP_STOP : 0;
00880 }
00881 
00882 /*! \brief Cleanup function for graceful shutdowns */
00883 static void bucket_cleanup(void)
00884 {
00885    ast_sorcery_unref(bucket_sorcery);
00886    bucket_sorcery = NULL;
00887 
00888    ast_sorcery_wizard_unregister(&bucket_wizard);
00889    ast_sorcery_wizard_unregister(&bucket_file_wizard);
00890 
00891    ao2_cleanup(schemes);
00892 }
00893 
00894 /*! \brief Custom handler for translating from a string timeval to actual structure */
00895 static int timeval_str2struct(const struct aco_option *opt, struct ast_variable *var, void *obj)
00896 {
00897    struct timeval *field = (struct timeval *)(obj + aco_option_get_argument(opt, 0));
00898    return ast_get_timeval(var->value, field, ast_tv(0, 0), NULL);
00899 }
00900 
00901 /*! \brief Custom handler for translating from an actual structure timeval to string */
00902 static int timeval_struct2str(const void *obj, const intptr_t *args, char **buf)
00903 {
00904    struct timeval *field = (struct timeval *)(obj + args[0]);
00905    return (ast_asprintf(buf, "%lu.%06lu", (unsigned long)field->tv_sec, (unsigned long)field->tv_usec) < 0) ? -1 : 0;
00906 }
00907 
00908 /*! \brief Initialize bucket support */
00909 int ast_bucket_init(void)
00910 {
00911    ast_register_cleanup(&bucket_cleanup);
00912 
00913    schemes = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_RWLOCK, SCHEME_BUCKETS, bucket_scheme_hash,
00914       bucket_scheme_cmp);
00915    if (!schemes) {
00916       ast_log(LOG_ERROR, "Failed to create container for Bucket schemes\n");
00917       return -1;
00918    }
00919 
00920    if (__ast_sorcery_wizard_register(&bucket_wizard, NULL)) {
00921       ast_log(LOG_ERROR, "Failed to register sorcery wizard for 'bucket' intermediary\n");
00922       return -1;
00923    }
00924 
00925    if (__ast_sorcery_wizard_register(&bucket_file_wizard, NULL)) {
00926       ast_log(LOG_ERROR, "Failed to register sorcery wizard for 'file' intermediary\n");
00927       return -1;
00928    }
00929 
00930    if (!(bucket_sorcery = ast_sorcery_open())) {
00931       ast_log(LOG_ERROR, "Failed to create sorcery instance for Bucket support\n");
00932       return -1;
00933    }
00934 
00935    if (ast_sorcery_apply_default(bucket_sorcery, "bucket", "bucket", NULL) == AST_SORCERY_APPLY_FAIL) {
00936       ast_log(LOG_ERROR, "Failed to apply intermediary for 'bucket' object type in Bucket sorcery\n");
00937       return -1;
00938    }
00939 
00940    if (ast_sorcery_object_register(bucket_sorcery, "bucket", bucket_alloc, NULL, NULL)) {
00941       ast_log(LOG_ERROR, "Failed to register 'bucket' object type in Bucket sorcery\n");
00942       return -1;
00943    }
00944 
00945    ast_sorcery_object_field_register(bucket_sorcery, "bucket", "scheme", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_bucket, scheme));
00946    ast_sorcery_object_field_register_custom(bucket_sorcery, "bucket", "created", "", timeval_str2struct, timeval_struct2str, NULL, 0, FLDSET(struct ast_bucket, created));
00947    ast_sorcery_object_field_register_custom(bucket_sorcery, "bucket", "modified", "", timeval_str2struct, timeval_struct2str, NULL, 0, FLDSET(struct ast_bucket, modified));
00948 
00949    if (ast_sorcery_apply_default(bucket_sorcery, "file", "bucket_file", NULL) == AST_SORCERY_APPLY_FAIL) {
00950       ast_log(LOG_ERROR, "Failed to apply intermediary for 'file' object type in Bucket sorcery\n");
00951       return -1;
00952    }
00953 
00954    if (ast_sorcery_object_register(bucket_sorcery, "file", bucket_file_alloc, NULL, NULL)) {
00955       ast_log(LOG_ERROR, "Failed to register 'file' object type in Bucket sorcery\n");
00956       return -1;
00957    }
00958 
00959    ast_sorcery_object_field_register(bucket_sorcery, "file", "scheme", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_bucket_file, scheme));
00960    ast_sorcery_object_field_register_custom(bucket_sorcery, "file", "created", "", timeval_str2struct, timeval_struct2str, NULL, 0, FLDSET(struct ast_bucket_file, created));
00961    ast_sorcery_object_field_register_custom(bucket_sorcery, "file", "modified", "", timeval_str2struct, timeval_struct2str, NULL, 0, FLDSET(struct ast_bucket_file, modified));
00962 
00963    return 0;
00964 }

Generated on Thu Apr 16 06:27:16 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6