bucket.c 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966
  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. /*! \file
  19. *
  20. * \brief Bucket File API
  21. *
  22. * \author Joshua Colp <jcolp@digium.com>
  23. */
  24. /*** MODULEINFO
  25. <use type="external">uriparser</use>
  26. <support_level>core</support_level>
  27. ***/
  28. /*** DOCUMENTATION
  29. <configInfo name="core" language="en_US">
  30. <synopsis>Bucket file API</synopsis>
  31. <configFile name="bucket">
  32. <configObject name="bucket">
  33. <configOption name="scheme">
  34. <synopsis>Scheme in use for bucket</synopsis>
  35. </configOption>
  36. <configOption name="created">
  37. <synopsis>Time at which the bucket was created</synopsis>
  38. </configOption>
  39. <configOption name="modified">
  40. <synopsis>Time at which the bucket was last modified</synopsis>
  41. </configOption>
  42. </configObject>
  43. <configObject name="file">
  44. <configOption name="scheme">
  45. <synopsis>Scheme in use for file</synopsis>
  46. </configOption>
  47. <configOption name="created">
  48. <synopsis>Time at which the file was created</synopsis>
  49. </configOption>
  50. <configOption name="modified">
  51. <synopsis>Time at which the file was last modified</synopsis>
  52. </configOption>
  53. </configObject>
  54. </configFile>
  55. </configInfo>
  56. ***/
  57. #include "asterisk.h"
  58. ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  59. #ifdef HAVE_URIPARSER
  60. #include <uriparser/Uri.h>
  61. #endif
  62. #include "asterisk/logger.h"
  63. #include "asterisk/sorcery.h"
  64. #include "asterisk/bucket.h"
  65. #include "asterisk/config_options.h"
  66. #include "asterisk/astobj2.h"
  67. #include "asterisk/strings.h"
  68. #include "asterisk/json.h"
  69. #include "asterisk/file.h"
  70. #include "asterisk/module.h"
  71. /*! \brief Number of buckets for the container of schemes */
  72. #define SCHEME_BUCKETS 53
  73. /*! \brief Number of buckets for the container of metadata in a file */
  74. #define METADATA_BUCKETS 53
  75. /*! \brief Sorcery instance for all bucket operations */
  76. static struct ast_sorcery *bucket_sorcery;
  77. /*! \brief Container of registered schemes */
  78. static struct ao2_container *schemes;
  79. /*! \brief Structure for available schemes */
  80. struct ast_bucket_scheme {
  81. /*! \brief Wizard for buckets */
  82. struct ast_sorcery_wizard *bucket;
  83. /*! \brief Wizard for files */
  84. struct ast_sorcery_wizard *file;
  85. /*! \brief Pointer to the file snapshot creation callback */
  86. bucket_file_create_cb create;
  87. /*! \brief Pointer to the file snapshot destruction callback */
  88. bucket_file_destroy_cb destroy;
  89. /*! \brief Name of the scheme */
  90. char name[0];
  91. };
  92. /*! \brief Callback function for creating a bucket */
  93. static int bucket_wizard_create(const struct ast_sorcery *sorcery, void *data, void *object)
  94. {
  95. struct ast_bucket *bucket = object;
  96. return bucket->scheme_impl->bucket->create(sorcery, data, object);
  97. }
  98. /*! \brief Callback function for retrieving a bucket */
  99. static void *bucket_wizard_retrieve(const struct ast_sorcery *sorcery, void *data, const char *type,
  100. const char *id)
  101. {
  102. #ifdef HAVE_URIPARSER
  103. UriParserStateA state;
  104. UriUriA uri;
  105. size_t len;
  106. #else
  107. char *tmp = ast_strdupa(id);
  108. #endif
  109. SCOPED_AO2RDLOCK(lock, schemes);
  110. char *uri_scheme;
  111. RAII_VAR(struct ast_bucket_scheme *, scheme, NULL, ao2_cleanup);
  112. #ifdef HAVE_URIPARSER
  113. state.uri = &uri;
  114. if (uriParseUriA(&state, id) != URI_SUCCESS ||
  115. !uri.scheme.first || !uri.scheme.afterLast) {
  116. uriFreeUriMembersA(&uri);
  117. return NULL;
  118. }
  119. len = (uri.scheme.afterLast - uri.scheme.first) + 1;
  120. uri_scheme = ast_alloca(len);
  121. ast_copy_string(uri_scheme, uri.scheme.first, len);
  122. uriFreeUriMembersA(&uri);
  123. #else
  124. uri_scheme = tmp;
  125. if (!(tmp = strchr(uri_scheme, ':'))) {
  126. return NULL;
  127. }
  128. *tmp = '\0';
  129. #endif
  130. scheme = ao2_find(schemes, uri_scheme, OBJ_KEY | OBJ_NOLOCK);
  131. if (!scheme) {
  132. return NULL;
  133. }
  134. return scheme->bucket->retrieve_id(sorcery, data, type, id);
  135. }
  136. /*! \brief Callback function for deleting a bucket */
  137. static int bucket_wizard_delete(const struct ast_sorcery *sorcery, void *data, void *object)
  138. {
  139. struct ast_bucket *bucket = object;
  140. return bucket->scheme_impl->bucket->delete(sorcery, data, object);
  141. }
  142. /*! \brief Intermediary bucket wizard */
  143. static struct ast_sorcery_wizard bucket_wizard = {
  144. .name = "bucket",
  145. .create = bucket_wizard_create,
  146. .retrieve_id = bucket_wizard_retrieve,
  147. .delete = bucket_wizard_delete,
  148. };
  149. /*! \brief Callback function for creating a bucket file */
  150. static int bucket_file_wizard_create(const struct ast_sorcery *sorcery, void *data, void *object)
  151. {
  152. struct ast_bucket_file *file = object;
  153. return file->scheme_impl->file->create(sorcery, data, object);
  154. }
  155. /*! \brief Callback function for retrieving a bucket file */
  156. static void *bucket_file_wizard_retrieve(const struct ast_sorcery *sorcery, void *data, const char *type,
  157. const char *id)
  158. {
  159. #ifdef HAVE_URIPARSER
  160. UriParserStateA state;
  161. UriUriA uri;
  162. size_t len;
  163. #else
  164. char *tmp = ast_strdupa(id);
  165. #endif
  166. char *uri_scheme;
  167. SCOPED_AO2RDLOCK(lock, schemes);
  168. RAII_VAR(struct ast_bucket_scheme *, scheme, NULL, ao2_cleanup);
  169. #ifdef HAVE_URIPARSER
  170. state.uri = &uri;
  171. if (uriParseUriA(&state, id) != URI_SUCCESS ||
  172. !uri.scheme.first || !uri.scheme.afterLast) {
  173. uriFreeUriMembersA(&uri);
  174. return NULL;
  175. }
  176. len = (uri.scheme.afterLast - uri.scheme.first) + 1;
  177. uri_scheme = ast_alloca(len);
  178. ast_copy_string(uri_scheme, uri.scheme.first, len);
  179. uriFreeUriMembersA(&uri);
  180. #else
  181. uri_scheme = tmp;
  182. if (!(tmp = strchr(uri_scheme, ':'))) {
  183. return NULL;
  184. }
  185. *tmp = '\0';
  186. #endif
  187. scheme = ao2_find(schemes, uri_scheme, OBJ_KEY | OBJ_NOLOCK);
  188. if (!scheme) {
  189. return NULL;
  190. }
  191. return scheme->file->retrieve_id(sorcery, data, type, id);
  192. }
  193. /*! \brief Callback function for updating a bucket file */
  194. static int bucket_file_wizard_update(const struct ast_sorcery *sorcery, void *data, void *object)
  195. {
  196. struct ast_bucket_file *file = object;
  197. return file->scheme_impl->file->update(sorcery, data, object);
  198. }
  199. /*! \brief Callback function for deleting a bucket file */
  200. static int bucket_file_wizard_delete(const struct ast_sorcery *sorcery, void *data, void *object)
  201. {
  202. struct ast_bucket_file *file = object;
  203. return file->scheme_impl->file->delete(sorcery, data, object);
  204. }
  205. /*! \brief Intermediary file wizard */
  206. static struct ast_sorcery_wizard bucket_file_wizard = {
  207. .name = "bucket_file",
  208. .create = bucket_file_wizard_create,
  209. .retrieve_id = bucket_file_wizard_retrieve,
  210. .update = bucket_file_wizard_update,
  211. .delete = bucket_file_wizard_delete,
  212. };
  213. int __ast_bucket_scheme_register(const char *name, struct ast_sorcery_wizard *bucket,
  214. struct ast_sorcery_wizard *file, bucket_file_create_cb create_cb,
  215. bucket_file_destroy_cb destroy_cb, struct ast_module *module)
  216. {
  217. SCOPED_AO2WRLOCK(lock, schemes);
  218. RAII_VAR(struct ast_bucket_scheme *, scheme, NULL, ao2_cleanup);
  219. if (ast_strlen_zero(name) || !bucket || !file ||
  220. !bucket->create || !bucket->delete || !bucket->retrieve_id ||
  221. !create_cb) {
  222. return -1;
  223. }
  224. scheme = ao2_find(schemes, name, OBJ_KEY | OBJ_NOLOCK);
  225. if (scheme) {
  226. return -1;
  227. }
  228. scheme = ao2_alloc(sizeof(*scheme) + strlen(name) + 1, NULL);
  229. if (!scheme) {
  230. return -1;
  231. }
  232. strcpy(scheme->name, name);
  233. scheme->bucket = bucket;
  234. scheme->file = file;
  235. scheme->create = create_cb;
  236. scheme->destroy = destroy_cb;
  237. ao2_link_flags(schemes, scheme, OBJ_NOLOCK);
  238. ast_verb(2, "Registered bucket scheme '%s'\n", name);
  239. ast_module_ref(module);
  240. return 0;
  241. }
  242. /*! \brief Allocator for metadata attributes */
  243. static struct ast_bucket_metadata *bucket_metadata_alloc(const char *name, const char *value)
  244. {
  245. int name_len = strlen(name) + 1, value_len = strlen(value) + 1;
  246. struct ast_bucket_metadata *metadata = ao2_alloc(sizeof(*metadata) + name_len + value_len, NULL);
  247. char *dst;
  248. if (!metadata) {
  249. return NULL;
  250. }
  251. dst = metadata->data;
  252. metadata->name = strcpy(dst, name);
  253. dst += name_len;
  254. metadata->value = strcpy(dst, value);
  255. return metadata;
  256. }
  257. int ast_bucket_file_metadata_set(struct ast_bucket_file *file, const char *name, const char *value)
  258. {
  259. RAII_VAR(struct ast_bucket_metadata *, metadata, bucket_metadata_alloc(name, value), ao2_cleanup);
  260. if (!metadata) {
  261. return -1;
  262. }
  263. ao2_find(file->metadata, name, OBJ_NODATA | OBJ_UNLINK | OBJ_KEY);
  264. ao2_link(file->metadata, metadata);
  265. return 0;
  266. }
  267. int ast_bucket_file_metadata_unset(struct ast_bucket_file *file, const char *name)
  268. {
  269. RAII_VAR(struct ast_bucket_metadata *, metadata, ao2_find(file->metadata, name, OBJ_UNLINK | OBJ_KEY), ao2_cleanup);
  270. if (!metadata) {
  271. return -1;
  272. }
  273. return 0;
  274. }
  275. struct ast_bucket_metadata *ast_bucket_file_metadata_get(struct ast_bucket_file *file, const char *name)
  276. {
  277. return ao2_find(file->metadata, name, OBJ_KEY);
  278. }
  279. /*! \brief Destructor for buckets */
  280. static void bucket_destroy(void *obj)
  281. {
  282. struct ast_bucket *bucket = obj;
  283. ao2_cleanup(bucket->scheme_impl);
  284. ast_string_field_free_memory(bucket);
  285. ao2_cleanup(bucket->buckets);
  286. ao2_cleanup(bucket->files);
  287. }
  288. /*! \brief Sorting function for red black tree string container */
  289. static int bucket_rbtree_str_sort_cmp(const void *obj_left, const void *obj_right, int flags)
  290. {
  291. const char *str_left = obj_left;
  292. const char *str_right = obj_right;
  293. int cmp = 0;
  294. switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
  295. default:
  296. case OBJ_POINTER:
  297. case OBJ_KEY:
  298. cmp = strcmp(str_left, str_right);
  299. break;
  300. case OBJ_PARTIAL_KEY:
  301. cmp = strncmp(str_left, str_right, strlen(str_right));
  302. break;
  303. }
  304. return cmp;
  305. }
  306. /*! \brief Allocator for buckets */
  307. static void *bucket_alloc(const char *name)
  308. {
  309. RAII_VAR(struct ast_bucket *, bucket, NULL, ao2_cleanup);
  310. bucket = ast_sorcery_generic_alloc(sizeof(*bucket), bucket_destroy);
  311. if (!bucket) {
  312. return NULL;
  313. }
  314. if (ast_string_field_init(bucket, 128)) {
  315. return NULL;
  316. }
  317. bucket->buckets = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_NOLOCK,
  318. AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT, bucket_rbtree_str_sort_cmp, NULL);
  319. if (!bucket->buckets) {
  320. return NULL;
  321. }
  322. bucket->files = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_NOLOCK,
  323. AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT, bucket_rbtree_str_sort_cmp, NULL);
  324. if (!bucket->files) {
  325. return NULL;
  326. }
  327. ao2_ref(bucket, +1);
  328. return bucket;
  329. }
  330. struct ast_bucket *ast_bucket_alloc(const char *uri)
  331. {
  332. #ifdef HAVE_URIPARSER
  333. UriParserStateA state;
  334. UriUriA full_uri;
  335. size_t len;
  336. #else
  337. char *tmp = ast_strdupa(uri);
  338. #endif
  339. char *uri_scheme;
  340. RAII_VAR(struct ast_bucket_scheme *, scheme, NULL, ao2_cleanup);
  341. struct ast_bucket *bucket;
  342. if (ast_strlen_zero(uri)) {
  343. return NULL;
  344. }
  345. #ifdef HAVE_URIPARSER
  346. state.uri = &full_uri;
  347. if (uriParseUriA(&state, uri) != URI_SUCCESS ||
  348. !full_uri.scheme.first || !full_uri.scheme.afterLast ||
  349. !full_uri.pathTail) {
  350. uriFreeUriMembersA(&full_uri);
  351. return NULL;
  352. }
  353. len = (full_uri.scheme.afterLast - full_uri.scheme.first) + 1;
  354. uri_scheme = ast_alloca(len);
  355. ast_copy_string(uri_scheme, full_uri.scheme.first, len);
  356. uriFreeUriMembersA(&full_uri);
  357. #else
  358. uri_scheme = tmp;
  359. if (!(tmp = strchr(uri_scheme, ':'))) {
  360. return NULL;
  361. }
  362. *tmp = '\0';
  363. #endif
  364. scheme = ao2_find(schemes, uri_scheme, OBJ_KEY);
  365. if (!scheme) {
  366. return NULL;
  367. }
  368. bucket = ast_sorcery_alloc(bucket_sorcery, "bucket", uri);
  369. if (!bucket) {
  370. return NULL;
  371. }
  372. ao2_ref(scheme, +1);
  373. bucket->scheme_impl = scheme;
  374. ast_string_field_set(bucket, scheme, uri_scheme);
  375. return bucket;
  376. }
  377. int ast_bucket_create(struct ast_bucket *bucket)
  378. {
  379. return ast_sorcery_create(bucket_sorcery, bucket);
  380. }
  381. struct ast_bucket *ast_bucket_retrieve(const char *uri)
  382. {
  383. if (ast_strlen_zero(uri)) {
  384. return NULL;
  385. }
  386. return ast_sorcery_retrieve_by_id(bucket_sorcery, "bucket", uri);
  387. }
  388. int ast_bucket_observer_add(const struct ast_sorcery_observer *callbacks)
  389. {
  390. return ast_sorcery_observer_add(bucket_sorcery, "bucket", callbacks);
  391. }
  392. void ast_bucket_observer_remove(const struct ast_sorcery_observer *callbacks)
  393. {
  394. ast_sorcery_observer_remove(bucket_sorcery, "bucket", callbacks);
  395. }
  396. int ast_bucket_delete(struct ast_bucket *bucket)
  397. {
  398. return ast_sorcery_delete(bucket_sorcery, bucket);
  399. }
  400. struct ast_json *ast_bucket_json(const struct ast_bucket *bucket)
  401. {
  402. RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
  403. struct ast_json *id, *files, *buckets;
  404. struct ao2_iterator i;
  405. char *uri;
  406. int res = 0;
  407. json = ast_sorcery_objectset_json_create(bucket_sorcery, bucket);
  408. if (!json) {
  409. return NULL;
  410. }
  411. id = ast_json_string_create(ast_sorcery_object_get_id(bucket));
  412. if (!id) {
  413. return NULL;
  414. }
  415. if (ast_json_object_set(json, "id", id)) {
  416. return NULL;
  417. }
  418. buckets = ast_json_array_create();
  419. if (!buckets) {
  420. return NULL;
  421. }
  422. if (ast_json_object_set(json, "buckets", buckets)) {
  423. return NULL;
  424. }
  425. i = ao2_iterator_init(bucket->buckets, 0);
  426. for (; (uri = ao2_iterator_next(&i)); ao2_ref(uri, -1)) {
  427. struct ast_json *bucket_uri = ast_json_string_create(uri);
  428. if (!bucket_uri || ast_json_array_append(buckets, bucket_uri)) {
  429. res = -1;
  430. ao2_ref(uri, -1);
  431. break;
  432. }
  433. }
  434. ao2_iterator_destroy(&i);
  435. if (res) {
  436. return NULL;
  437. }
  438. files = ast_json_array_create();
  439. if (!files) {
  440. return NULL;
  441. }
  442. if (ast_json_object_set(json, "files", files)) {
  443. return NULL;
  444. }
  445. i = ao2_iterator_init(bucket->files, 0);
  446. for (; (uri = ao2_iterator_next(&i)); ao2_ref(uri, -1)) {
  447. struct ast_json *file_uri = ast_json_string_create(uri);
  448. if (!file_uri || ast_json_array_append(files, file_uri)) {
  449. res = -1;
  450. ao2_ref(uri, -1);
  451. break;
  452. }
  453. }
  454. ao2_iterator_destroy(&i);
  455. if (res) {
  456. return NULL;
  457. }
  458. ast_json_ref(json);
  459. return json;
  460. }
  461. /*! \brief Hashing function for file metadata */
  462. static int bucket_file_metadata_hash(const void *obj, const int flags)
  463. {
  464. const struct ast_bucket_metadata *object;
  465. const char *key;
  466. switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
  467. case OBJ_KEY:
  468. key = obj;
  469. return ast_str_hash(key);
  470. case OBJ_POINTER:
  471. object = obj;
  472. return ast_str_hash(object->name);
  473. default:
  474. /* Hash can only work on something with a full key */
  475. ast_assert(0);
  476. return 0;
  477. }
  478. }
  479. /*! \brief Comparison function for file metadata */
  480. static int bucket_file_metadata_cmp(void *obj, void *arg, int flags)
  481. {
  482. struct ast_bucket_metadata *metadata1 = obj, *metadata2 = arg;
  483. const char *name = arg;
  484. return !strcmp(metadata1->name, flags & OBJ_KEY ? name : metadata2->name) ? CMP_MATCH | CMP_STOP : 0;
  485. }
  486. /*! \brief Destructor for bucket files */
  487. static void bucket_file_destroy(void *obj)
  488. {
  489. struct ast_bucket_file *file = obj;
  490. if (file->scheme_impl->destroy) {
  491. file->scheme_impl->destroy(file);
  492. }
  493. ao2_cleanup(file->scheme_impl);
  494. ao2_cleanup(file->metadata);
  495. }
  496. /*! \brief Allocator for bucket files */
  497. static void *bucket_file_alloc(const char *name)
  498. {
  499. RAII_VAR(struct ast_bucket_file *, file, NULL, ao2_cleanup);
  500. file = ast_sorcery_generic_alloc(sizeof(*file), bucket_file_destroy);
  501. if (!file) {
  502. return NULL;
  503. }
  504. if (ast_string_field_init(file, 128)) {
  505. return NULL;
  506. }
  507. file->metadata = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, METADATA_BUCKETS,
  508. bucket_file_metadata_hash, bucket_file_metadata_cmp);
  509. if (!file->metadata) {
  510. return NULL;
  511. }
  512. ao2_ref(file, +1);
  513. return file;
  514. }
  515. struct ast_bucket_file *ast_bucket_file_alloc(const char *uri)
  516. {
  517. #ifdef HAVE_URIPARSER
  518. UriParserStateA state;
  519. UriUriA full_uri;
  520. size_t len;
  521. #else
  522. char *tmp = ast_strdupa(uri);
  523. #endif
  524. char *uri_scheme;
  525. RAII_VAR(struct ast_bucket_scheme *, scheme, NULL, ao2_cleanup);
  526. struct ast_bucket_file *file;
  527. if (ast_strlen_zero(uri)) {
  528. return NULL;
  529. }
  530. #ifdef HAVE_URIPARSER
  531. state.uri = &full_uri;
  532. if (uriParseUriA(&state, uri) != URI_SUCCESS ||
  533. !full_uri.scheme.first || !full_uri.scheme.afterLast ||
  534. !full_uri.pathTail) {
  535. uriFreeUriMembersA(&full_uri);
  536. return NULL;
  537. }
  538. len = (full_uri.scheme.afterLast - full_uri.scheme.first) + 1;
  539. uri_scheme = ast_alloca(len);
  540. ast_copy_string(uri_scheme, full_uri.scheme.first, len);
  541. uriFreeUriMembersA(&full_uri);
  542. #else
  543. uri_scheme = tmp;
  544. if (!(tmp = strchr(uri_scheme, ':'))) {
  545. return NULL;
  546. }
  547. *tmp = '\0';
  548. #endif
  549. scheme = ao2_find(schemes, uri_scheme, OBJ_KEY);
  550. if (!scheme) {
  551. return NULL;
  552. }
  553. file = ast_sorcery_alloc(bucket_sorcery, "file", uri);
  554. if (!file) {
  555. return NULL;
  556. }
  557. ao2_ref(scheme, +1);
  558. file->scheme_impl = scheme;
  559. ast_string_field_set(file, scheme, uri_scheme);
  560. if (scheme->create(file)) {
  561. ao2_ref(file, -1);
  562. return NULL;
  563. }
  564. return file;
  565. }
  566. int ast_bucket_file_create(struct ast_bucket_file *file)
  567. {
  568. return ast_sorcery_create(bucket_sorcery, file);
  569. }
  570. /*! \brief Copy a file, shamelessly taken from file.c */
  571. static int bucket_copy(const char *infile, const char *outfile)
  572. {
  573. int ifd, ofd, len;
  574. char buf[4096]; /* XXX make it lerger. */
  575. if ((ifd = open(infile, O_RDONLY)) < 0) {
  576. ast_log(LOG_WARNING, "Unable to open %s in read-only mode, error: %s\n", infile, strerror(errno));
  577. return -1;
  578. }
  579. if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, AST_FILE_MODE)) < 0) {
  580. ast_log(LOG_WARNING, "Unable to open %s in write-only mode, error: %s\n", outfile, strerror(errno));
  581. close(ifd);
  582. return -1;
  583. }
  584. while ( (len = read(ifd, buf, sizeof(buf)) ) ) {
  585. int res;
  586. if (len < 0) {
  587. ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
  588. break;
  589. }
  590. /* XXX handle partial writes */
  591. res = write(ofd, buf, len);
  592. if (res != len) {
  593. ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
  594. len = -1; /* error marker */
  595. break;
  596. }
  597. }
  598. close(ifd);
  599. close(ofd);
  600. if (len < 0) {
  601. unlink(outfile);
  602. return -1; /* error */
  603. }
  604. return 0; /* success */
  605. }
  606. struct ast_bucket_file *ast_bucket_file_copy(struct ast_bucket_file *file, const char *uri)
  607. {
  608. RAII_VAR(struct ast_bucket_file *, copy, ast_bucket_file_alloc(uri), ao2_cleanup);
  609. if (!copy) {
  610. return NULL;
  611. }
  612. ao2_cleanup(copy->metadata);
  613. copy->metadata = ao2_container_clone(file->metadata, 0);
  614. if (!copy->metadata ||
  615. bucket_copy(file->path, copy->path)) {
  616. return NULL;
  617. }
  618. ao2_ref(copy, +1);
  619. return copy;
  620. }
  621. struct ast_bucket_file *ast_bucket_file_retrieve(const char *uri)
  622. {
  623. if (ast_strlen_zero(uri)) {
  624. return NULL;
  625. }
  626. return ast_sorcery_retrieve_by_id(bucket_sorcery, "file", uri);
  627. }
  628. int ast_bucket_file_observer_add(const struct ast_sorcery_observer *callbacks)
  629. {
  630. return ast_sorcery_observer_add(bucket_sorcery, "file", callbacks);
  631. }
  632. void ast_bucket_file_observer_remove(const struct ast_sorcery_observer *callbacks)
  633. {
  634. ast_sorcery_observer_remove(bucket_sorcery, "file", callbacks);
  635. }
  636. int ast_bucket_file_update(struct ast_bucket_file *file)
  637. {
  638. return ast_sorcery_update(bucket_sorcery, file);
  639. }
  640. int ast_bucket_file_delete(struct ast_bucket_file *file)
  641. {
  642. return ast_sorcery_delete(bucket_sorcery, file);
  643. }
  644. struct ast_json *ast_bucket_file_json(const struct ast_bucket_file *file)
  645. {
  646. RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
  647. struct ast_json *id, *metadata;
  648. struct ao2_iterator i;
  649. struct ast_bucket_metadata *attribute;
  650. int res = 0;
  651. json = ast_sorcery_objectset_json_create(bucket_sorcery, file);
  652. if (!json) {
  653. return NULL;
  654. }
  655. id = ast_json_string_create(ast_sorcery_object_get_id(file));
  656. if (!id) {
  657. return NULL;
  658. }
  659. if (ast_json_object_set(json, "id", id)) {
  660. return NULL;
  661. }
  662. metadata = ast_json_object_create();
  663. if (!metadata) {
  664. return NULL;
  665. }
  666. if (ast_json_object_set(json, "metadata", metadata)) {
  667. return NULL;
  668. }
  669. i = ao2_iterator_init(file->metadata, 0);
  670. for (; (attribute = ao2_iterator_next(&i)); ao2_ref(attribute, -1)) {
  671. struct ast_json *value = ast_json_string_create(attribute->value);
  672. if (!value || ast_json_object_set(metadata, attribute->name, value)) {
  673. res = -1;
  674. break;
  675. }
  676. }
  677. ao2_iterator_destroy(&i);
  678. if (res) {
  679. return NULL;
  680. }
  681. ast_json_ref(json);
  682. return json;
  683. }
  684. int ast_bucket_file_temporary_create(struct ast_bucket_file *file)
  685. {
  686. int fd;
  687. ast_copy_string(file->path, "/tmp/bucket-XXXXXX", sizeof(file->path));
  688. fd = mkstemp(file->path);
  689. if (fd < 0) {
  690. return -1;
  691. }
  692. close(fd);
  693. return 0;
  694. }
  695. void ast_bucket_file_temporary_destroy(struct ast_bucket_file *file)
  696. {
  697. if (!ast_strlen_zero(file->path)) {
  698. unlink(file->path);
  699. }
  700. }
  701. /*! \brief Hashing function for scheme container */
  702. static int bucket_scheme_hash(const void *obj, const int flags)
  703. {
  704. const struct ast_bucket_scheme *object;
  705. const char *key;
  706. switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
  707. case OBJ_KEY:
  708. key = obj;
  709. return ast_str_hash(key);
  710. case OBJ_POINTER:
  711. object = obj;
  712. return ast_str_hash(object->name);
  713. default:
  714. /* Hash can only work on something with a full key */
  715. ast_assert(0);
  716. return 0;
  717. }
  718. }
  719. /*! \brief Comparison function for scheme container */
  720. static int bucket_scheme_cmp(void *obj, void *arg, int flags)
  721. {
  722. struct ast_bucket_scheme *scheme1 = obj, *scheme2 = arg;
  723. const char *name = arg;
  724. return !strcmp(scheme1->name, flags & OBJ_KEY ? name : scheme2->name) ? CMP_MATCH | CMP_STOP : 0;
  725. }
  726. /*! \brief Cleanup function for graceful shutdowns */
  727. static void bucket_cleanup(void)
  728. {
  729. if (bucket_sorcery) {
  730. ast_sorcery_unref(bucket_sorcery);
  731. }
  732. ast_sorcery_wizard_unregister(&bucket_wizard);
  733. ast_sorcery_wizard_unregister(&bucket_file_wizard);
  734. ao2_cleanup(schemes);
  735. }
  736. /*! \brief Custom handler for translating from a string timeval to actual structure */
  737. static int timeval_str2struct(const struct aco_option *opt, struct ast_variable *var, void *obj)
  738. {
  739. struct timeval *field = (struct timeval *)(obj + aco_option_get_argument(opt, 0));
  740. return ast_get_timeval(var->value, field, ast_tv(0, 0), NULL);
  741. }
  742. /*! \brief Custom handler for translating from an actual structure timeval to string */
  743. static int timeval_struct2str(const void *obj, const intptr_t *args, char **buf)
  744. {
  745. struct timeval *field = (struct timeval *)(obj + args[0]);
  746. return (ast_asprintf(buf, "%lu.%06lu", field->tv_sec, (unsigned long)field->tv_usec) < 0) ? -1 : 0;
  747. }
  748. /*! \brief Initialize bucket support */
  749. int ast_bucket_init(void)
  750. {
  751. ast_register_cleanup(&bucket_cleanup);
  752. schemes = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_RWLOCK, SCHEME_BUCKETS, bucket_scheme_hash,
  753. bucket_scheme_cmp);
  754. if (!schemes) {
  755. ast_log(LOG_ERROR, "Failed to create container for Bucket schemes\n");
  756. return -1;
  757. }
  758. if (__ast_sorcery_wizard_register(&bucket_wizard, NULL)) {
  759. ast_log(LOG_ERROR, "Failed to register sorcery wizard for 'bucket' intermediary\n");
  760. return -1;
  761. }
  762. if (__ast_sorcery_wizard_register(&bucket_file_wizard, NULL)) {
  763. ast_log(LOG_ERROR, "Failed to register sorcery wizard for 'file' intermediary\n");
  764. return -1;
  765. }
  766. if (!(bucket_sorcery = ast_sorcery_open())) {
  767. ast_log(LOG_ERROR, "Failed to create sorcery instance for Bucket support\n");
  768. return -1;
  769. }
  770. if (ast_sorcery_apply_default(bucket_sorcery, "bucket", "bucket", NULL) == AST_SORCERY_APPLY_FAIL) {
  771. ast_log(LOG_ERROR, "Failed to apply intermediary for 'bucket' object type in Bucket sorcery\n");
  772. return -1;
  773. }
  774. if (ast_sorcery_object_register(bucket_sorcery, "bucket", bucket_alloc, NULL, NULL)) {
  775. ast_log(LOG_ERROR, "Failed to register 'bucket' object type in Bucket sorcery\n");
  776. return -1;
  777. }
  778. ast_sorcery_object_field_register(bucket_sorcery, "bucket", "scheme", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_bucket, scheme));
  779. ast_sorcery_object_field_register_custom(bucket_sorcery, "bucket", "created", "", timeval_str2struct, timeval_struct2str, NULL, 0, FLDSET(struct ast_bucket, created));
  780. ast_sorcery_object_field_register_custom(bucket_sorcery, "bucket", "modified", "", timeval_str2struct, timeval_struct2str, NULL, 0, FLDSET(struct ast_bucket, modified));
  781. if (ast_sorcery_apply_default(bucket_sorcery, "file", "bucket_file", NULL) == AST_SORCERY_APPLY_FAIL) {
  782. ast_log(LOG_ERROR, "Failed to apply intermediary for 'file' object type in Bucket sorcery\n");
  783. return -1;
  784. }
  785. if (ast_sorcery_object_register(bucket_sorcery, "file", bucket_file_alloc, NULL, NULL)) {
  786. ast_log(LOG_ERROR, "Failed to register 'file' object type in Bucket sorcery\n");
  787. return -1;
  788. }
  789. ast_sorcery_object_field_register(bucket_sorcery, "file", "scheme", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_bucket_file, scheme));
  790. ast_sorcery_object_field_register_custom(bucket_sorcery, "file", "created", "", timeval_str2struct, timeval_struct2str, NULL, 0, FLDSET(struct ast_bucket_file, created));
  791. ast_sorcery_object_field_register_custom(bucket_sorcery, "file", "modified", "", timeval_str2struct, timeval_struct2str, NULL, 0, FLDSET(struct ast_bucket_file, modified));
  792. return 0;
  793. }