app_ices.c 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) 1999 - 2005, Digium, Inc.
  5. *
  6. * Mark Spencer <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. *
  20. * \brief Stream to an icecast server via ICES (see contrib/asterisk-ices.xml)
  21. *
  22. * \author Mark Spencer <markster@digium.com>
  23. *
  24. * \extref ICES - http://www.icecast.org/ices.php
  25. *
  26. * \ingroup applications
  27. */
  28. /*** MODULEINFO
  29. <depend>working_fork</depend>
  30. ***/
  31. #include "asterisk.h"
  32. ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  33. #include <signal.h>
  34. #include <fcntl.h>
  35. #include <sys/time.h>
  36. #ifdef HAVE_CAP
  37. #include <sys/capability.h>
  38. #endif /* HAVE_CAP */
  39. #include "asterisk/paths.h" /* use ast_config_AST_CONFIG_DIR */
  40. #include "asterisk/lock.h"
  41. #include "asterisk/file.h"
  42. #include "asterisk/channel.h"
  43. #include "asterisk/frame.h"
  44. #include "asterisk/pbx.h"
  45. #include "asterisk/module.h"
  46. #include "asterisk/translate.h"
  47. #define path_BIN "/usr/bin/"
  48. #define path_LOCAL "/usr/local/bin/"
  49. static char *app = "ICES";
  50. static char *synopsis = "Encode and stream using 'ices'";
  51. static char *descrip =
  52. " ICES(config.xml) Streams to an icecast server using ices\n"
  53. "(available separately). A configuration file must be supplied\n"
  54. "for ices (see contrib/asterisk-ices.xml). \n"
  55. "\n"
  56. "- ICES version 2 cient and server required.\n";
  57. static int icesencode(char *filename, int fd)
  58. {
  59. int res;
  60. int x;
  61. sigset_t fullset, oldset;
  62. #ifdef HAVE_CAP
  63. cap_t cap;
  64. #endif
  65. sigfillset(&fullset);
  66. pthread_sigmask(SIG_BLOCK, &fullset, &oldset);
  67. res = fork();
  68. if (res < 0)
  69. ast_log(LOG_WARNING, "Fork failed\n");
  70. if (res) {
  71. pthread_sigmask(SIG_SETMASK, &oldset, NULL);
  72. return res;
  73. }
  74. /* Stop ignoring PIPE */
  75. signal(SIGPIPE, SIG_DFL);
  76. pthread_sigmask(SIG_UNBLOCK, &fullset, NULL);
  77. #ifdef HAVE_CAP
  78. cap = cap_from_text("cap_net_admin-eip");
  79. if (cap_set_proc(cap)) {
  80. /* Careful with order! Logging cannot happen after we close FDs */
  81. ast_log(LOG_WARNING, "Unable to remove capabilities.\n");
  82. }
  83. cap_free(cap);
  84. #endif
  85. if (ast_opt_high_priority)
  86. ast_set_priority(0);
  87. dup2(fd, STDIN_FILENO);
  88. for (x=STDERR_FILENO + 1;x<1024;x++) {
  89. if ((x != STDIN_FILENO) && (x != STDOUT_FILENO))
  90. close(x);
  91. }
  92. /* Most commonly installed in /usr/local/bin
  93. * But many places has it in /usr/bin
  94. * As a last-ditch effort, try to use PATH
  95. */
  96. execl(path_LOCAL "ices2", "ices", filename, (char *)NULL);
  97. execl(path_BIN "ices2", "ices", filename, (char *)NULL);
  98. execlp("ices2", "ices", filename, (char *)NULL);
  99. ast_debug(1, "Couldn't find ices version 2, attempting to use ices version 1.");
  100. execl(path_LOCAL "ices", "ices", filename, (char *)NULL);
  101. execl(path_BIN "ices", "ices", filename, (char *)NULL);
  102. execlp("ices", "ices", filename, (char *)NULL);
  103. ast_log(LOG_WARNING, "Execute of ices failed, could not find command.\n");
  104. close(fd);
  105. _exit(0);
  106. }
  107. static int ices_exec(struct ast_channel *chan, void *data)
  108. {
  109. int res = 0;
  110. int fds[2];
  111. int ms = -1;
  112. int pid = -1;
  113. int flags;
  114. int oreadformat;
  115. struct timeval last;
  116. struct ast_frame *f;
  117. char filename[256]="";
  118. char *c;
  119. if (ast_strlen_zero(data)) {
  120. ast_log(LOG_WARNING, "ICES requires an argument (configfile.xml)\n");
  121. return -1;
  122. }
  123. last = ast_tv(0, 0);
  124. if (pipe(fds)) {
  125. ast_log(LOG_WARNING, "Unable to create pipe\n");
  126. return -1;
  127. }
  128. flags = fcntl(fds[1], F_GETFL);
  129. fcntl(fds[1], F_SETFL, flags | O_NONBLOCK);
  130. ast_stopstream(chan);
  131. if (chan->_state != AST_STATE_UP)
  132. res = ast_answer(chan);
  133. if (res) {
  134. close(fds[0]);
  135. close(fds[1]);
  136. ast_log(LOG_WARNING, "Answer failed!\n");
  137. return -1;
  138. }
  139. oreadformat = chan->readformat;
  140. res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
  141. if (res < 0) {
  142. close(fds[0]);
  143. close(fds[1]);
  144. ast_log(LOG_WARNING, "Unable to set write format to signed linear\n");
  145. return -1;
  146. }
  147. if (((char *)data)[0] == '/')
  148. ast_copy_string(filename, (char *) data, sizeof(filename));
  149. else
  150. snprintf(filename, sizeof(filename), "%s/%s", ast_config_AST_CONFIG_DIR, (char *)data);
  151. /* Placeholder for options */
  152. c = strchr(filename, '|');
  153. if (c)
  154. *c = '\0';
  155. res = icesencode(filename, fds[0]);
  156. if (res >= 0) {
  157. pid = res;
  158. for (;;) {
  159. /* Wait for audio, and stream */
  160. ms = ast_waitfor(chan, -1);
  161. if (ms < 0) {
  162. ast_debug(1, "Hangup detected\n");
  163. res = -1;
  164. break;
  165. }
  166. f = ast_read(chan);
  167. if (!f) {
  168. ast_debug(1, "Null frame == hangup() detected\n");
  169. res = -1;
  170. break;
  171. }
  172. if (f->frametype == AST_FRAME_VOICE) {
  173. res = write(fds[1], f->data, f->datalen);
  174. if (res < 0) {
  175. if (errno != EAGAIN) {
  176. ast_log(LOG_WARNING, "Write failed to pipe: %s\n", strerror(errno));
  177. res = -1;
  178. ast_frfree(f);
  179. break;
  180. }
  181. }
  182. }
  183. ast_frfree(f);
  184. }
  185. }
  186. close(fds[0]);
  187. close(fds[1]);
  188. if (pid > -1)
  189. kill(pid, SIGKILL);
  190. if (!res && oreadformat)
  191. ast_set_read_format(chan, oreadformat);
  192. return res;
  193. }
  194. static int unload_module(void)
  195. {
  196. return ast_unregister_application(app);
  197. }
  198. static int load_module(void)
  199. {
  200. return ast_register_application(app, ices_exec, synopsis, descrip);
  201. }
  202. AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Encode and Stream via icecast and ices");