sounds_index.c 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) 2013, Digium, Inc.
  5. *
  6. * Kinsey Moore <markster@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. * \brief Sound file format and description index.
  20. */
  21. #include "asterisk.h"
  22. #include <dirent.h>
  23. #include <sys/stat.h>
  24. #include "asterisk/utils.h"
  25. #include "asterisk/lock.h"
  26. #include "asterisk/format.h"
  27. #include "asterisk/format_cap.h"
  28. #include "asterisk/paths.h" /* use ast_config_AST_DATA_DIR */
  29. #include "asterisk/media_index.h"
  30. #include "asterisk/sounds_index.h"
  31. #include "asterisk/file.h"
  32. #include "asterisk/cli.h"
  33. #include "asterisk/_private.h"
  34. #include "asterisk/stasis_message_router.h"
  35. #include "asterisk/stasis_system.h"
  36. /*** MODULEINFO
  37. <support_level>core</support_level>
  38. ***/
  39. /*! \brief The number of buckets to be used for storing language-keyed objects */
  40. #define LANGUAGE_BUCKETS 7
  41. static struct ast_media_index *sounds_index;
  42. static struct stasis_message_router *sounds_system_router;
  43. /*! \brief Get the languages in which sound files are available */
  44. static struct ao2_container *get_languages(void)
  45. {
  46. RAII_VAR(struct ao2_container *, lang_dirs, NULL, ao2_cleanup);
  47. struct dirent* dent;
  48. DIR* srcdir;
  49. RAII_VAR(struct ast_str *, media_dir, ast_str_create(64), ast_free);
  50. RAII_VAR(struct ast_str *, variant_dir, ast_str_create(64), ast_free);
  51. lang_dirs = ast_str_container_alloc(LANGUAGE_BUCKETS);
  52. if (!media_dir || !lang_dirs) {
  53. return NULL;
  54. }
  55. ast_str_set(&media_dir, 0, "%s/sounds", ast_config_AST_DATA_DIR);
  56. srcdir = opendir(ast_str_buffer(media_dir));
  57. if (srcdir == NULL) {
  58. ast_log(LOG_ERROR, "Failed to open %s\n", ast_str_buffer(media_dir));
  59. return NULL;
  60. }
  61. while((dent = readdir(srcdir)) != NULL) {
  62. struct stat st;
  63. if(!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..")) {
  64. continue;
  65. }
  66. ast_str_reset(variant_dir);
  67. ast_str_set(&variant_dir, 0, "%s/%s", ast_str_buffer(media_dir), dent->d_name);
  68. if (stat(ast_str_buffer(variant_dir), &st) < 0) {
  69. ast_log(LOG_ERROR, "Failed to stat %s\n", ast_str_buffer(variant_dir));
  70. continue;
  71. }
  72. if (S_ISDIR(st.st_mode)) {
  73. ast_str_container_add(lang_dirs, dent->d_name);
  74. }
  75. }
  76. closedir(srcdir);
  77. ao2_ref(lang_dirs, +1);
  78. return lang_dirs;
  79. }
  80. /*! \brief Callback to process an individual language directory or subdirectory */
  81. static int update_index_cb(void *obj, void *arg, int flags)
  82. {
  83. char *lang = obj;
  84. struct ast_media_index *index = arg;
  85. if (ast_media_index_update(index, lang)) {
  86. return CMP_MATCH;
  87. }
  88. return 0;
  89. }
  90. AST_MUTEX_DEFINE_STATIC(reload_lock);
  91. int ast_sounds_reindex(void)
  92. {
  93. RAII_VAR(struct ast_str *, sounds_dir, NULL, ast_free);
  94. RAII_VAR(struct ao2_container *, languages, NULL, ao2_cleanup);
  95. RAII_VAR(char *, failed_index, NULL, ao2_cleanup);
  96. RAII_VAR(struct ast_media_index *, new_index, NULL, ao2_cleanup);
  97. struct ast_media_index *old_index;
  98. SCOPED_MUTEX(lock, &reload_lock);
  99. old_index = sounds_index;
  100. languages = get_languages();
  101. sounds_dir = ast_str_create(64);
  102. if (!languages || !sounds_dir) {
  103. return -1;
  104. }
  105. ast_str_set(&sounds_dir, 0, "%s/sounds", ast_config_AST_DATA_DIR);
  106. new_index = ast_media_index_create(ast_str_buffer(sounds_dir));
  107. if (!new_index) {
  108. return -1;
  109. }
  110. failed_index = ao2_callback(languages, 0, update_index_cb, new_index);
  111. if (failed_index) {
  112. return -1;
  113. }
  114. ao2_ref(new_index, +1);
  115. sounds_index = new_index;
  116. ao2_cleanup(old_index);
  117. return 0;
  118. }
  119. static int show_sounds_cb(void *obj, void *arg, int flags)
  120. {
  121. char *name = obj;
  122. struct ast_cli_args *a = arg;
  123. ast_cli(a->fd, "%s\n", name);
  124. return 0;
  125. }
  126. static int show_sound_info_cb(void *obj, void *arg, int flags)
  127. {
  128. char *language = obj;
  129. struct ast_cli_args *a = arg;
  130. struct ast_format format;
  131. int formats_shown = 0;
  132. RAII_VAR(struct ast_media_index *, local_index, ast_sounds_get_index(), ao2_cleanup);
  133. RAII_VAR(struct ast_format_cap *, cap, NULL, ast_format_cap_destroy);
  134. const char *description = ast_media_get_description(local_index, a->argv[3], language);
  135. ast_cli(a->fd, " Language %s:\n", language);
  136. if (!ast_strlen_zero(description)) {
  137. ast_cli(a->fd, " Description: %s\n", description);
  138. }
  139. cap = ast_media_get_format_cap(local_index, a->argv[3], language);
  140. if (cap) {
  141. ast_format_cap_iter_start(cap);
  142. while (!ast_format_cap_iter_next(cap, &format)) {
  143. ast_cli(a->fd, " Format: %s\n", ast_getformatname(&format));
  144. formats_shown = 1;
  145. }
  146. ast_format_cap_iter_end(cap);
  147. }
  148. if (!formats_shown) {
  149. ast_cli(a->fd, " No Formats Available\n");
  150. }
  151. return 0;
  152. }
  153. /*! \brief Show a list of sounds available on the system */
  154. static char *handle_cli_sounds_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  155. {
  156. switch (cmd) {
  157. case CLI_INIT:
  158. e->command = "core show sounds";
  159. e->usage =
  160. "Usage: core show sounds\n"
  161. " Shows a listing of sound files available on the system.\n";
  162. return NULL;
  163. case CLI_GENERATE:
  164. return NULL;
  165. }
  166. if (a->argc == 3) {
  167. RAII_VAR(struct ao2_container *, sound_files, ast_media_get_media(sounds_index), ao2_cleanup);
  168. if (!sound_files) {
  169. return CLI_FAILURE;
  170. }
  171. ast_cli(a->fd, "Available audio files:\n");
  172. ao2_callback(sound_files, OBJ_MULTIPLE | OBJ_NODATA, show_sounds_cb, a);
  173. return CLI_SUCCESS;
  174. }
  175. return CLI_SHOWUSAGE;
  176. }
  177. /*! \brief Show details about a sound available in the system */
  178. static char *handle_cli_sound_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  179. {
  180. switch (cmd) {
  181. case CLI_INIT:
  182. e->command = "core show sound";
  183. e->usage =
  184. "Usage: core show sound [soundid]\n"
  185. " Shows information about the specified sound.\n";
  186. return NULL;
  187. case CLI_GENERATE:
  188. {
  189. int length = strlen(a->word);
  190. int which = 0;
  191. struct ao2_iterator it_sounds;
  192. char *match = NULL;
  193. char *filename;
  194. RAII_VAR(struct ao2_container *, sound_files, ast_media_get_media(sounds_index), ao2_cleanup);
  195. if (!sound_files) {
  196. return NULL;
  197. }
  198. it_sounds = ao2_iterator_init(sound_files, 0);
  199. while ((filename = ao2_iterator_next(&it_sounds))) {
  200. if (!strncasecmp(a->word, filename, length) && ++which > a->n) {
  201. match = ast_strdup(filename);
  202. ao2_ref(filename, -1);
  203. break;
  204. }
  205. ao2_ref(filename, -1);
  206. }
  207. ao2_iterator_destroy(&it_sounds);
  208. return match;
  209. }
  210. }
  211. if (a->argc == 4) {
  212. RAII_VAR(struct ao2_container *, variants, ast_media_get_variants(sounds_index, a->argv[3]), ao2_cleanup);
  213. if (!variants || !ao2_container_count(variants)) {
  214. ast_cli(a->fd, "ERROR: File %s not found in index\n", a->argv[3]);
  215. return CLI_FAILURE;
  216. }
  217. ast_cli(a->fd, "Indexed Information for %s:\n", a->argv[3]);
  218. ao2_callback(variants, OBJ_MULTIPLE | OBJ_NODATA, show_sound_info_cb, a);
  219. return CLI_SUCCESS;
  220. }
  221. return CLI_SHOWUSAGE;
  222. }
  223. /*! \brief Struct for registering CLI commands */
  224. static struct ast_cli_entry cli_sounds[] = {
  225. AST_CLI_DEFINE(handle_cli_sounds_show, "Shows available sounds"),
  226. AST_CLI_DEFINE(handle_cli_sound_show, "Shows details about a specific sound"),
  227. };
  228. static void sounds_cleanup(void)
  229. {
  230. stasis_message_router_unsubscribe_and_join(sounds_system_router);
  231. sounds_system_router = NULL;
  232. ast_cli_unregister_multiple(cli_sounds, ARRAY_LEN(cli_sounds));
  233. ao2_cleanup(sounds_index);
  234. sounds_index = NULL;
  235. }
  236. static void format_update_cb(void *data, struct stasis_subscription *sub,
  237. struct stasis_message *message)
  238. {
  239. ast_sounds_reindex();
  240. }
  241. int ast_sounds_index_init(void)
  242. {
  243. int res = 0;
  244. sounds_index = NULL;
  245. if (ast_sounds_reindex()) {
  246. return -1;
  247. }
  248. res |= ast_cli_register_multiple(cli_sounds, ARRAY_LEN(cli_sounds));
  249. sounds_system_router = stasis_message_router_create(ast_system_topic());
  250. if (!sounds_system_router) {
  251. return -1;
  252. }
  253. res |= stasis_message_router_add(
  254. sounds_system_router,
  255. ast_format_register_type(),
  256. format_update_cb,
  257. NULL);
  258. res |= stasis_message_router_add(
  259. sounds_system_router,
  260. ast_format_unregister_type(),
  261. format_update_cb,
  262. NULL);
  263. if (res) {
  264. return -1;
  265. }
  266. ast_register_atexit(sounds_cleanup);
  267. return 0;
  268. }
  269. struct ast_media_index *ast_sounds_get_index(void)
  270. {
  271. ao2_ref(sounds_index, +1);
  272. return sounds_index;
  273. }