Asterisk - The Open Source Telephony Project  18.5.0
media_cache.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2015, Matt Jordan
5  *
6  * Matt Jordan <[email protected]>
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  * \brief An in-memory media cache
22  *
23  * \author \verbatim Matt Jordan <[email protected]> \endverbatim
24  *
25  */
26 
27 /*** MODULEINFO
28  <support_level>core</support_level>
29  ***/
30 
31 #include "asterisk.h"
32 
33 #include <sys/stat.h>
34 #include "asterisk/config.h"
35 #include "asterisk/bucket.h"
36 #include "asterisk/astdb.h"
37 #include "asterisk/cli.h"
38 #include "asterisk/file.h"
39 #include "asterisk/media_cache.h"
40 
41 /*! The name of the AstDB family holding items in the cache. */
42 #define AST_DB_FAMILY "MediaCache"
43 
44 /*! Length of 'MediaCache' + 2 '/' characters */
45 #define AST_DB_FAMILY_LEN 12
46 
47 /*! Number of buckets in the ao2 container holding our media items */
48 #define AO2_BUCKETS 61
49 
50 /*! Our one and only container holding media items */
51 static struct ao2_container *media_cache;
52 
53 int ast_media_cache_exists(const char *uri)
54 {
55  struct ast_bucket_file *bucket_file;
56 
57  if (ast_strlen_zero(uri)) {
58  return 0;
59  }
60 
61  bucket_file = ao2_find(media_cache, uri, OBJ_SEARCH_KEY);
62  if (bucket_file) {
63  ao2_ref(bucket_file, -1);
64  return 1;
65  }
66 
67  /* Check to see if any bucket implementation could return this item */
68  bucket_file = ast_bucket_file_retrieve(uri);
69  if (bucket_file) {
70  ao2_ref(bucket_file, -1);
71  return 1;
72  }
73 
74  return 0;
75 }
76 
77 /*!
78  * \internal
79  * \brief Sync \c bucket_file metadata to the AstDB
80  */
81 static int metadata_sync_to_astdb(void *obj, void *arg, int flags)
82 {
83  struct ast_bucket_metadata *metadata = obj;
84  const char *hash = arg;
85 
86  ast_db_put(hash, metadata->name, metadata->value);
87 
88  return 0;
89 }
90 
91 /*!
92  * \internal
93  * \brief Sync a media cache item to the AstDB
94  * \param bucket_file The \c ast_bucket_file media cache item to sync
95  */
96 static void media_cache_item_sync_to_astdb(struct ast_bucket_file *bucket_file)
97 {
98  char hash[41]; /* 40 character SHA1 hash */
99 
100  ast_sha1_hash(hash, ast_sorcery_object_get_id(bucket_file));
101  if (ast_db_put(AST_DB_FAMILY, ast_sorcery_object_get_id(bucket_file), hash)) {
102  return;
103  }
104 
105  ast_db_put(hash, "path", bucket_file->path);
107 }
108 
109 /*!
110  * \internal
111  * \brief Delete a media cache item from the AstDB
112  * \param bucket_file The \c ast_bucket_file media cache item to delete
113  */
114 static void media_cache_item_del_from_astdb(struct ast_bucket_file *bucket_file)
115 {
116  char *hash_value;
117 
118  if (ast_db_get_allocated(AST_DB_FAMILY, ast_sorcery_object_get_id(bucket_file), &hash_value)) {
119  return;
120  }
121 
122  ast_db_deltree(hash_value, NULL);
124  ast_free(hash_value);
125 }
126 
127 /*!
128  * \internal
129  * \brief Normalize the value of a Content-Type header
130  *
131  * This will trim off any optional parameters after the type/subtype.
132  */
133 static void normalize_content_type_header(char *content_type)
134 {
135  char *params = strchr(content_type, ';');
136 
137  if (params) {
138  *params-- = 0;
139  while (params > content_type && (*params == ' ' || *params == '\t')) {
140  *params-- = 0;
141  }
142  }
143 }
144 
145 /*!
146  * \internal
147  * \brief Update the name of the file backing a \c bucket_file
148  * \param preferred_file_name The preferred name of the backing file
149  */
150 static void bucket_file_update_path(struct ast_bucket_file *bucket_file,
151  const char *preferred_file_name)
152 {
153  char *ext;
154 
155  if (!ast_strlen_zero(preferred_file_name) && strcmp(bucket_file->path, preferred_file_name)) {
156  /* Use the preferred file name if available */
157 
158  rename(bucket_file->path, preferred_file_name);
159  ast_copy_string(bucket_file->path, preferred_file_name,
160  sizeof(bucket_file->path));
161  } else if (!strchr(bucket_file->path, '.') && (ext = strrchr(ast_sorcery_object_get_id(bucket_file), '.'))) {
162  /* If we don't have a file extension and were provided one in the URI, use it */
163  char found_ext[32];
164  char new_path[PATH_MAX + sizeof(found_ext)];
165 
166  ast_bucket_file_metadata_set(bucket_file, "ext", ext);
167 
168  /* Don't pass '.' while checking for supported extension */
169  if (!ast_get_format_for_file_ext(ext + 1)) {
170  /* If the file extension passed in the URI isn't supported check for the
171  * extension based on the MIME type passed in the Content-Type header before
172  * giving up.
173  * If a match is found then retrieve the extension from the supported list
174  * corresponding to the mime-type and use that to rename the file */
175  struct ast_bucket_metadata *header = ast_bucket_file_metadata_get(bucket_file, "content-type");
176  if (header) {
177  char *mime_type = ast_strdup(header->value);
178  if (mime_type) {
180  if (!ast_strlen_zero(mime_type)) {
181  if (ast_get_extension_for_mime_type(mime_type, found_ext, sizeof(found_ext))) {
182  ext = found_ext;
183  }
184  }
185  ast_free(mime_type);
186  }
187  ao2_ref(header, -1);
188  }
189  }
190 
191  snprintf(new_path, sizeof(new_path), "%s%s", bucket_file->path, ext);
192  rename(bucket_file->path, new_path);
193  ast_copy_string(bucket_file->path, new_path, sizeof(bucket_file->path));
194  }
195 }
196 
197 int ast_media_cache_retrieve(const char *uri, const char *preferred_file_name,
198  char *file_path, size_t len)
199 {
200  struct ast_bucket_file *bucket_file;
201  char *ext;
202  SCOPED_AO2LOCK(media_lock, media_cache);
203 
204  if (ast_strlen_zero(uri)) {
205  return -1;
206  }
207 
208  /* First, retrieve from the ao2 cache here. If we find a bucket_file
209  * matching the requested URI, ask the appropriate backend if it is
210  * stale. If not; return it.
211  */
212  bucket_file = ao2_find(media_cache, uri, OBJ_SEARCH_KEY | OBJ_NOLOCK);
213  if (bucket_file) {
214  if (!ast_bucket_file_is_stale(bucket_file)
215  && ast_file_is_readable(bucket_file->path)) {
216  ast_copy_string(file_path, bucket_file->path, len);
217  if ((ext = strrchr(file_path, '.'))) {
218  *ext = '\0';
219  }
220  ao2_ref(bucket_file, -1);
221 
222  ast_debug(5, "Returning media at local file: %s\n", file_path);
223  return 0;
224  }
225 
226  /* Stale! Remove the item completely, as we're going to replace it next */
227  ao2_unlink_flags(media_cache, bucket_file, OBJ_NOLOCK);
228  ast_bucket_file_delete(bucket_file);
229  ao2_ref(bucket_file, -1);
230  }
231 
232  /* Either this is new or the resource is stale; do a full retrieve
233  * from the appropriate bucket_file backend
234  */
235  bucket_file = ast_bucket_file_retrieve(uri);
236  if (!bucket_file) {
237  ast_debug(2, "Failed to obtain media at '%s'\n", uri);
238  return -1;
239  }
240 
241  /* We can manipulate the 'immutable' bucket_file here, as we haven't
242  * let anyone know of its existence yet
243  */
244  bucket_file_update_path(bucket_file, preferred_file_name);
245  media_cache_item_sync_to_astdb(bucket_file);
246  ast_copy_string(file_path, bucket_file->path, len);
247  if ((ext = strrchr(file_path, '.'))) {
248  *ext = '\0';
249  }
250  ao2_link_flags(media_cache, bucket_file, OBJ_NOLOCK);
251  ao2_ref(bucket_file, -1);
252 
253  ast_debug(5, "Returning media at local file: %s\n", file_path);
254 
255  return 0;
256 }
257 
258 int ast_media_cache_retrieve_metadata(const char *uri, const char *key,
259  char *value, size_t len)
260 {
261  struct ast_bucket_file *bucket_file;
262  struct ast_bucket_metadata *metadata;
263 
264  if (ast_strlen_zero(uri) || ast_strlen_zero(key) || !value) {
265  return -1;
266  }
267 
268  bucket_file = ao2_find(media_cache, uri, OBJ_SEARCH_KEY);
269  if (!bucket_file) {
270  return -1;
271  }
272 
273  metadata = ao2_find(bucket_file->metadata, key, OBJ_SEARCH_KEY);
274  if (!metadata) {
275  ao2_ref(bucket_file, -1);
276  return -1;
277  }
278  ast_copy_string(value, metadata->value, len);
279 
280  ao2_ref(metadata, -1);
281  ao2_ref(bucket_file, -1);
282  return 0;
283 }
284 
285 int ast_media_cache_create_or_update(const char *uri, const char *file_path,
286  struct ast_variable *metadata)
287 {
288  struct ast_bucket_file *bucket_file;
289  struct ast_variable *it_metadata;
290  struct stat st;
291  char tmp[128];
292  char *ext;
293  char *file_path_ptr;
294  int created = 0;
295  SCOPED_AO2LOCK(media_lock, media_cache);
296 
297  if (ast_strlen_zero(file_path) || ast_strlen_zero(uri)) {
298  return -1;
299  }
300  file_path_ptr = ast_strdupa(file_path);
301 
302  if (stat(file_path, &st)) {
303  ast_log(LOG_WARNING, "Unable to obtain information for file %s for URI %s\n",
304  file_path, uri);
305  return -1;
306  }
307 
308  bucket_file = ao2_find(media_cache, uri, OBJ_SEARCH_KEY | OBJ_NOLOCK);
309  if (bucket_file) {
310  struct ast_bucket_file *clone;
311 
312  clone = ast_bucket_file_clone(bucket_file);
313  if (!clone) {
314  ao2_ref(bucket_file, -1);
315  return -1;
316  }
317 
318  /* Remove the old bucket_file. We'll replace it if we succeed below. */
319  ao2_unlink_flags(media_cache, bucket_file, OBJ_NOLOCK);
320  ao2_ref(bucket_file, -1);
321 
322  bucket_file = clone;
323  } else {
324  bucket_file = ast_bucket_file_alloc(uri);
325  if (!bucket_file) {
326  ast_log(LOG_WARNING, "Failed to create file storage for %s and %s\n",
327  uri, file_path);
328  return -1;
329  }
330  created = 1;
331  }
332 
333  strcpy(bucket_file->path, file_path);
334  bucket_file->created.tv_sec = st.st_ctime;
335  bucket_file->modified.tv_sec = st.st_mtime;
336 
337  snprintf(tmp, sizeof(tmp), "%ld", (long)st.st_atime);
338  ast_bucket_file_metadata_set(bucket_file, "accessed", tmp);
339 
340  snprintf(tmp, sizeof(tmp), "%jd", (intmax_t)st.st_size);
341  ast_bucket_file_metadata_set(bucket_file, "size", tmp);
342 
343  ext = strrchr(file_path_ptr, '.');
344  if (ext) {
345  ast_bucket_file_metadata_set(bucket_file, "ext", ext + 1);
346  }
347 
348  for (it_metadata = metadata; it_metadata; it_metadata = it_metadata->next) {
349  ast_bucket_file_metadata_set(bucket_file, it_metadata->name, it_metadata->value);
350  }
351 
352  if (created && ast_bucket_file_create(bucket_file)) {
353  ast_log(LOG_WARNING, "Failed to create media for %s\n", uri);
354  ao2_ref(bucket_file, -1);
355  return -1;
356  }
357  media_cache_item_sync_to_astdb(bucket_file);
358 
359  ao2_link_flags(media_cache, bucket_file, OBJ_NOLOCK);
360  ao2_ref(bucket_file, -1);
361  return 0;
362 }
363 
364 int ast_media_cache_delete(const char *uri)
365 {
366  struct ast_bucket_file *bucket_file;
367  int res;
368 
369  if (ast_strlen_zero(uri)) {
370  return -1;
371  }
372 
373  bucket_file = ao2_find(media_cache, uri, OBJ_SEARCH_KEY | OBJ_UNLINK);
374  if (!bucket_file) {
375  return -1;
376  }
377 
378  res = ast_bucket_file_delete(bucket_file);
379  media_cache_item_del_from_astdb(bucket_file);
380 
381  ao2_ref(bucket_file, -1);
382 
383  return res;
384 }
385 
386 /*!
387  * \internal
388  * \brief Remove a media cache item from the AstDB
389  * \param uri The unique URI that represents the item in the cache
390  * \param hash The hash key for the item in the AstDB
391  */
392 static void media_cache_remove_from_astdb(const char *uri, const char *hash)
393 {
395  ast_db_deltree(hash, NULL);
396 }
397 
398 /*!
399  * \internal
400  * \brief Create an item in the media cache from entries in the AstDB
401  * \param uri The unique URI that represents the item in the cache
402  * \param hash The hash key for the item in the AstDB
403  * \retval 0 success
404  * \retval -1 failure
405  */
406 static int media_cache_item_populate_from_astdb(const char *uri, const char *hash)
407 {
408  struct ast_bucket_file *bucket_file;
409  struct ast_db_entry *db_tree;
410  struct ast_db_entry *db_entry;
411  struct stat st;
412 
413  bucket_file = ast_bucket_file_alloc(uri);
414  if (!bucket_file) {
415  return -1;
416  }
417 
418  db_tree = ast_db_gettree(hash, NULL);
419  for (db_entry = db_tree; db_entry; db_entry = db_entry->next) {
420  const char *key = strchr(db_entry->key + 1, '/');
421 
422  if (ast_strlen_zero(key)) {
423  continue;
424  }
425  key++;
426 
427  if (!strcasecmp(key, "path")) {
428  strcpy(bucket_file->path, db_entry->data);
429 
430  if (stat(bucket_file->path, &st)) {
431  ast_log(LOG_WARNING, "Unable to obtain information for file %s for URI %s\n",
432  bucket_file->path, uri);
433  ao2_ref(bucket_file, -1);
434  ast_db_freetree(db_tree);
435  return -1;
436  }
437  } else {
438  ast_bucket_file_metadata_set(bucket_file, key, db_entry->data);
439  }
440  }
441  ast_db_freetree(db_tree);
442 
443  if (ast_strlen_zero(bucket_file->path)) {
444  ao2_ref(bucket_file, -1);
445  ast_log(LOG_WARNING, "Failed to restore media cache item for '%s' from AstDB: no 'path' specified\n",
446  uri);
447  return -1;
448  }
449 
450  ao2_link(media_cache, bucket_file);
451  ao2_ref(bucket_file, -1);
452 
453  return 0;
454 }
455 
456 /*!
457  * \internal
458  * \brief Populate the media cache from entries in the AstDB
459  */
461 {
462  struct ast_db_entry *db_entry;
463  struct ast_db_entry *db_tree;
464 
465  db_tree = ast_db_gettree(AST_DB_FAMILY, NULL);
466  for (db_entry = db_tree; db_entry; db_entry = db_entry->next) {
467  if (media_cache_item_populate_from_astdb(db_entry->key + AST_DB_FAMILY_LEN, db_entry->data)) {
468  media_cache_remove_from_astdb(db_entry->key, db_entry->data);
469  }
470  }
471  ast_db_freetree(db_tree);
472 }
473 
474 /*!
475  * \internal
476  * \brief ao2 callback function for \ref media_cache_handle_show_all
477  */
478 static int media_cache_prnt_summary(void *obj, void *arg, int flags)
479 {
480 #define FORMAT_ROW "%-40s\n\t%-40s\n"
481  struct ast_bucket_file *bucket_file = obj;
482  struct ast_cli_args *a = arg;
483 
484  ast_cli(a->fd, FORMAT_ROW, ast_sorcery_object_get_id(bucket_file), bucket_file->path);
485 
486 #undef FORMAT_ROW
487  return CMP_MATCH;
488 }
489 
490 static char *media_cache_handle_show_all(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
491 {
492  switch (cmd) {
493  case CLI_INIT:
494  e->command = "media cache show all";
495  e->usage =
496  "Usage: media cache show all\n"
497  " Display a summary of all current items in the media cache.\n";
498  return NULL;
499  case CLI_GENERATE:
500  return NULL;
501  }
502 
503  if (a->argc != 4) {
504  return CLI_SHOWUSAGE;
505  }
506 
507  ast_cli(a->fd, "URI\n\tLocal File\n");
508  ast_cli(a->fd, "---------------\n");
510 
511  return CLI_SUCCESS;
512 }
513 
514 /*!
515  * \internal
516  * \brief CLI tab completion function for URIs
517  */
518 static char *cli_complete_uri(const char *word)
519 {
520  struct ast_bucket_file *bucket_file;
521  struct ao2_iterator it_media_items;
522  int wordlen = strlen(word);
523 
524  it_media_items = ao2_iterator_init(media_cache, 0);
525  while ((bucket_file = ao2_iterator_next(&it_media_items))) {
526  if (!strncasecmp(word, ast_sorcery_object_get_id(bucket_file), wordlen)) {
528  }
529  ao2_ref(bucket_file, -1);
530  }
531  ao2_iterator_destroy(&it_media_items);
532 
533  return NULL;
534 }
535 
536 static char *media_cache_handle_show_item(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
537 {
538 #define FORMAT_ROW "\t%20s: %-40.40s\n"
539  struct ast_bucket_file *bucket_file;
540  struct ao2_iterator it_metadata;
541  struct ast_bucket_metadata *metadata;
542 
543  switch (cmd) {
544  case CLI_INIT:
545  e->command = "media cache show";
546  e->usage =
547  "Usage: media cache show <uri>\n"
548  " Display all information about a particular item in the media cache.\n";
549  return NULL;
550  case CLI_GENERATE:
551  return a->pos == e->args ? cli_complete_uri(a->word) : NULL;
552  }
553 
554  if (a->argc != 4) {
555  return CLI_SHOWUSAGE;
556  }
557 
558  bucket_file = ao2_find(media_cache, a->argv[3], OBJ_SEARCH_KEY);
559  if (!bucket_file) {
560  ast_cli(a->fd, "Unable to find '%s' in the media cache\n", a->argv[3]);
561  return CLI_SUCCESS;
562  }
563 
564  ast_cli(a->fd, "URI: %s\n", ast_sorcery_object_get_id(bucket_file));
565  ast_cli(a->fd, "%s\n", "----------------------------------------");
566  ast_cli(a->fd, FORMAT_ROW, "Path", bucket_file->path);
567 
568  it_metadata = ao2_iterator_init(bucket_file->metadata, 0);
569  while ((metadata = ao2_iterator_next(&it_metadata))) {
570  ast_cli(a->fd, FORMAT_ROW, metadata->name, metadata->value);
571  ao2_ref(metadata, -1);
572  }
573  ao2_iterator_destroy(&it_metadata);
574 
575  ao2_ref(bucket_file, -1);
576 #undef FORMAT_ROW
577  return CLI_SUCCESS;
578 }
579 
580 static char *media_cache_handle_delete_item(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
581 {
582  switch (cmd) {
583  case CLI_INIT:
584  e->command = "media cache delete";
585  e->usage =
586  "Usage: media cache delete <uri>\n"
587  " Delete an item from the media cache.\n\n"
588  " Note that this will also remove any local storage of the media associated\n"
589  " with the URI, and will inform the backend supporting the URI scheme that\n"
590  " it should remove the item.\n";
591  return NULL;
592  case CLI_GENERATE:
593  return a->pos == e->args ? cli_complete_uri(a->word) : NULL;
594  }
595 
596  if (a->argc != 4) {
597  return CLI_SHOWUSAGE;
598  }
599 
600  if (ast_media_cache_delete(a->argv[3])) {
601  ast_cli(a->fd, "Unable to delete '%s'\n", a->argv[3]);
602  } else {
603  ast_cli(a->fd, "Deleted '%s' from the media cache\n", a->argv[3]);
604  }
605 
606  return CLI_SUCCESS;
607 }
608 
609 static char *media_cache_handle_refresh_item(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
610 {
611  char file_path[PATH_MAX];
612 
613  switch (cmd) {
614  case CLI_INIT:
615  e->command = "media cache refresh";
616  e->usage =
617  "Usage: media cache refresh <uri>\n"
618  " Ask for a refresh of a particular URI.\n\n"
619  " If the item does not already exist in the media cache, the item will be\n"
620  " populated from the backend supporting the URI scheme.\n";
621  return NULL;
622  case CLI_GENERATE:
623  return a->pos == e->args ? cli_complete_uri(a->word) : NULL;
624  }
625 
626  if (a->argc != 4) {
627  return CLI_SHOWUSAGE;
628  }
629 
630  if (ast_media_cache_retrieve(a->argv[3], NULL, file_path, sizeof(file_path))) {
631  ast_cli(a->fd, "Unable to refresh '%s'\n", a->argv[3]);
632  } else {
633  ast_cli(a->fd, "Refreshed '%s' to local storage '%s'\n", a->argv[3], file_path);
634  }
635 
636  return CLI_SUCCESS;
637 }
638 
639 static char *media_cache_handle_create_item(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
640 {
641  switch (cmd) {
642  case CLI_INIT:
643  e->command = "media cache create";
644  e->usage =
645  "Usage: media cache create <uri> <file>\n"
646  " Create an item in the media cache by associating a local media file with\n"
647  " some URI.\n";
648  return NULL;
649  case CLI_GENERATE:
650  return NULL;
651  }
652 
653  if (a->argc != 5) {
654  return CLI_SHOWUSAGE;
655  }
656 
657  if (ast_media_cache_create_or_update(a->argv[3], a->argv[4], NULL)) {
658  ast_cli(a->fd, "Unable to create '%s' associated with local file '%s'\n",
659  a->argv[3], a->argv[4]);
660  } else {
661  ast_cli(a->fd, "Created '%s' for '%s' in the media cache\n",
662  a->argv[3], a->argv[4]);
663  }
664 
665  return CLI_SUCCESS;
666 }
667 
668 static struct ast_cli_entry cli_media_cache[] = {
669  AST_CLI_DEFINE(media_cache_handle_show_all, "Show all items in the media cache"),
670  AST_CLI_DEFINE(media_cache_handle_show_item, "Show a single item in the media cache"),
671  AST_CLI_DEFINE(media_cache_handle_delete_item, "Remove an item from the media cache"),
672  AST_CLI_DEFINE(media_cache_handle_refresh_item, "Refresh an item in the media cache"),
673  AST_CLI_DEFINE(media_cache_handle_create_item, "Create an item in the media cache"),
674 };
675 
676 /*!
677  * \internal
678  * \brief Shutdown the media cache
679  */
680 static void media_cache_shutdown(void)
681 {
682  ao2_cleanup(media_cache);
683  media_cache = NULL;
684 
685  ast_cli_unregister_multiple(cli_media_cache, ARRAY_LEN(cli_media_cache));
686 }
687 
689 {
691 
694  if (!media_cache) {
695  return -1;
696  }
697 
698  if (ast_cli_register_multiple(cli_media_cache, ARRAY_LEN(cli_media_cache))) {
699  return -1;
700  }
701 
703 
704  return 0;
705 }
struct ast_bucket_metadata * ast_bucket_file_metadata_get(struct ast_bucket_file *file, const char *name)
Retrieve a metadata attribute from a file.
Definition: bucket.c:359
struct ast_variable * next
static void media_cache_remove_from_astdb(const char *uri, const char *hash)
Definition: media_cache.c:392
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:197
static int media_cache_item_populate_from_astdb(const char *uri, const char *hash)
Definition: media_cache.c:406
Asterisk main include file. File version handling, generic pbx functions.
int ast_media_cache_create_or_update(const char *uri, const char *file_path, struct ast_variable *metadata)
Create/update a cached media item.
Definition: media_cache.c:285
#define ARRAY_LEN(a)
Definition: isdn_lib.c:42
int ast_media_cache_retrieve_metadata(const char *uri, const char *key, char *value, size_t len)
Retrieve metadata from an item in the cache.
Definition: media_cache.c:258
#define AO2_BUCKETS
Definition: media_cache.c:48
int ast_bucket_file_metadata_set(struct ast_bucket_file *file, const char *name, const char *value)
Set a metadata attribute on a file to a specific value.
Definition: bucket.c:334
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
The arg parameter is a search key, but is not an object.
Definition: astobj2.h:1105
static int metadata_sync_to_astdb(void *obj, void *arg, int flags)
Definition: media_cache.c:81
#define AST_DB_FAMILY
Definition: media_cache.c:42
descriptor for a cli entry.
Definition: cli.h:171
const int argc
Definition: cli.h:160
#define LOG_WARNING
Definition: logger.h:274
static void bucket_file_update_path(struct ast_bucket_file *bucket_file, const char *preferred_file_name)
Definition: media_cache.c:150
int ast_file_is_readable(const char *filename)
Test that a file exists and is readable by the effective user.
Definition: main/utils.c:2855
void ast_db_freetree(struct ast_db_entry *entry)
Free structure created by ast_db_gettree()
Definition: main/db.c:598
#define ao2_callback(c, flags, cb_fn, arg)
Definition: astobj2.h:1716
struct ast_bucket_file * ast_bucket_file_retrieve(const char *uri)
Retrieve a bucket file.
Definition: bucket.c:815
static int tmp()
Definition: bt_open.c:389
static char * media_cache_handle_delete_item(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: media_cache.c:580
Structure for variables, used for configurations and for channel variables.
int ast_sorcery_object_id_compare(void *obj, void *arg, int flags)
ao2 object comparator based on sorcery id.
Definition: sorcery.c:2459
static struct ao2_container * media_cache
Definition: media_cache.c:51
Definition: cli.h:152
Assume that the ao2_container is already locked.
Definition: astobj2.h:1067
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:422
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
static struct ast_cli_entry cli_media_cache[]
Definition: media_cache.c:668
#define ao2_link_flags(container, obj, flags)
Definition: astobj2.h:1572
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:243
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
static void media_cache_shutdown(void)
Definition: media_cache.c:680
#define NULL
Definition: resample.c:96
struct ast_bucket_file * ast_bucket_file_alloc(const char *uri)
Allocate a new bucket file.
Definition: bucket.c:663
int value
Definition: syslog.c:37
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
const char * ext
Definition: http.c:147
struct timeval modified
When this file was last modified.
Definition: bucket.h:91
Bucket File API.
static char * media_cache_handle_show_all(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: media_cache.c:490
int args
This gets set in ast_cli_register()
Definition: cli.h:185
#define ast_strlen_zero(foo)
Definition: strings.h:52
#define AST_DB_FAMILY_LEN
Definition: media_cache.c:45
Configuration File Parser.
int ast_media_cache_exists(const char *uri)
Check if an item exists in the cache.
Definition: media_cache.c:53
const char * value
Value of the attribute.
Definition: bucket.h:51
struct timeval created
When this file was created.
Definition: bucket.h:89
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
struct ast_db_entry * next
Definition: astdb.h:32
int ast_register_cleanup(void(*func)(void))
Register a function to be executed before Asterisk gracefully exits.
Definition: clicompat.c:19
const int fd
Definition: cli.h:159
static int media_cache_prnt_summary(void *obj, void *arg, int flags)
Definition: media_cache.c:478
struct ast_bucket_file * ast_bucket_file_clone(struct ast_bucket_file *file)
Clone a bucket file.
Definition: bucket.c:810
const char * name
Name of the attribute.
Definition: bucket.h:49
#define ao2_ref(o, delta)
Definition: astobj2.h:464
int ast_bucket_file_delete(struct ast_bucket_file *file)
Delete a bucket file from backend storage.
Definition: bucket.c:844
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
int ast_bucket_file_is_stale(struct ast_bucket_file *file)
Retrieve whether or not the backing datastore views the bucket file as stale.
Definition: bucket.c:824
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
Definition: sorcery.c:2312
static void media_cache_item_del_from_astdb(struct ast_bucket_file *bucket_file)
Definition: media_cache.c:114
void ast_bucket_file_metadata_callback(struct ast_bucket_file *file, ao2_callback_fn cb, void *arg)
Execute a callback function on the metadata associated with a file.
Definition: bucket.c:364
Bucket file structure, contains reference to file and information about it.
Definition: bucket.h:78
void ast_sha1_hash(char *output, const char *input)
Produces SHA1 hash based on input string.
Definition: main/utils.c:264
const char *const * argv
Definition: cli.h:161
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Definition: astobj2.h:1310
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:531
#define CLI_SHOWUSAGE
Definition: cli.h:45
static void media_cache_item_sync_to_astdb(struct ast_bucket_file *bucket_file)
Definition: media_cache.c:96
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
An in-memory media cache.
#define ao2_iterator_next(iter)
Definition: astobj2.h:1933
struct ao2_container * metadata
Container of metadata attributes about file.
Definition: bucket.h:93
Definition: astdb.h:31
#define SCOPED_AO2LOCK(varname, obj)
scoped lock specialization for ao2 mutexes.
Definition: lock.h:602
char data[0]
Definition: astdb.h:34
#define ast_free(a)
Definition: astmm.h:182
char * command
Definition: cli.h:186
Bucket metadata structure, AO2 key value pair.
Definition: bucket.h:47
int ast_media_cache_init(void)
Initialize the media cache.
Definition: media_cache.c:688
const char * word
Definition: cli.h:163
#define ao2_find(container, arg, flags)
Definition: astobj2.h:1756
struct ast_format * ast_get_format_for_file_ext(const char *file_ext)
Get the ast_format associated with the given file extension.
Definition: file.c:1938
static char * cli_complete_uri(const char *word)
Definition: media_cache.c:518
const char * usage
Definition: cli.h:177
#define CLI_SUCCESS
Definition: cli.h:44
static void normalize_content_type_header(char *content_type)
Definition: media_cache.c:133
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1841
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
Standard Command Line Interface.
int ast_db_del(const char *family, const char *key)
Delete entry in astdb.
Definition: main/db.c:429
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
static char * media_cache_handle_create_item(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: media_cache.c:639
const int pos
Definition: cli.h:164
int ast_sorcery_object_id_hash(const void *obj, int flags)
ao2 object hasher based on sorcery id.
Definition: sorcery.c:2470
#define ao2_unlink_flags(container, obj, flags)
Definition: astobj2.h:1622
int ast_db_put(const char *family, const char *key, const char *value)
Store value addressed by family/key.
Definition: main/db.c:327
static char * media_cache_handle_show_item(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: media_cache.c:536
#define PATH_MAX
Definition: asterisk.h:40
static char * media_cache_handle_refresh_item(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: media_cache.c:609
#define FORMAT_ROW
Generic container type.
int ast_cli_completion_add(char *value)
Add a result to a request for completion options.
Definition: main/cli.c:2726
static void media_cache_populate_from_astdb(void)
Definition: media_cache.c:460
int ast_media_cache_retrieve(const char *uri, const char *preferred_file_name, char *file_path, size_t len)
Retrieve an item from the cache.
Definition: media_cache.c:197
Persistant data storage (akin to *doze registry)
int ast_get_extension_for_mime_type(const char *mime_type, char *buffer, size_t capacity)
Get a suitable filename extension for the given MIME type.
Definition: file.c:1951
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
int ast_media_cache_delete(const char *uri)
Remove an item from the media cache.
Definition: media_cache.c:364
char path[PATH_MAX]
Local path to this file.
Definition: bucket.h:95
int ast_bucket_file_create(struct ast_bucket_file *file)
Create a new bucket file in backend storage.
Definition: bucket.c:725
int ast_db_deltree(const char *family, const char *keytree)
Delete one or more entries in astdb.
Definition: main/db.c:457
short word
char * key
Definition: astdb.h:33
static struct test_val a
#define ao2_link(container, obj)
Definition: astobj2.h:1549