res_stasis_snoop.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  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 Stasis application snoop control support.
  21. *
  22. * \author Joshua Colp <jcolp@digium.com>
  23. */
  24. /*** MODULEINFO
  25. <depend type="module">res_stasis</depend>
  26. <support_level>core</support_level>
  27. ***/
  28. #include "asterisk.h"
  29. ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  30. #include "asterisk/module.h"
  31. #include "asterisk/stasis_app_impl.h"
  32. #include "asterisk/stasis_app_snoop.h"
  33. #include "asterisk/audiohook.h"
  34. #include "asterisk/pbx.h"
  35. #include "asterisk/timing.h"
  36. #include "asterisk/stasis_channels.h"
  37. #include "asterisk/json.h"
  38. #include "asterisk/format_cache.h"
  39. /*! \brief The interval (in milliseconds) that the Snoop timer is triggered, also controls length of audio within frames */
  40. #define SNOOP_INTERVAL 20
  41. /*! \brief Index used to keep Snoop channel names unique */
  42. static unsigned int chan_idx;
  43. /*! \brief Structure which contains all of the snoop information */
  44. struct stasis_app_snoop {
  45. /*! \brief Timer used for waking up Stasis thread */
  46. struct ast_timer *timer;
  47. /*! \brief Audiohook used to spy on the channel */
  48. struct ast_audiohook spy;
  49. /*! \brief Direction for spying */
  50. enum ast_audiohook_direction spy_direction;
  51. /*! \brief Number of samples to be read in when spying */
  52. unsigned int spy_samples;
  53. /*! \brief Format in use by the spy audiohook */
  54. struct ast_format *spy_format;
  55. /*! \brief Audiohook used to whisper on the channel */
  56. struct ast_audiohook whisper;
  57. /*! \brief Direction for whispering */
  58. enum ast_audiohook_direction whisper_direction;
  59. /*! \brief Stasis application and arguments */
  60. struct ast_str *app;
  61. /*! \brief Snoop channel */
  62. struct ast_channel *chan;
  63. /*! \brief Whether the spy capability is active or not */
  64. unsigned int spy_active:1;
  65. /*! \brief Whether the whisper capability is active or not */
  66. unsigned int whisper_active:1;
  67. /*! \brief Uniqueid of the channel this snoop is snooping on */
  68. char uniqueid[AST_MAX_UNIQUEID];
  69. /*! \brief A frame of silence to use when the audiohook returns null */
  70. struct ast_frame silence;
  71. };
  72. /*! \brief Destructor for snoop structure */
  73. static void snoop_destroy(void *obj)
  74. {
  75. struct stasis_app_snoop *snoop = obj;
  76. if (snoop->timer) {
  77. ast_timer_close(snoop->timer);
  78. }
  79. if (snoop->spy_active) {
  80. ast_audiohook_destroy(&snoop->spy);
  81. }
  82. if (snoop->whisper_active) {
  83. ast_audiohook_destroy(&snoop->whisper);
  84. }
  85. if (snoop->silence.data.ptr) {
  86. ast_free(snoop->silence.data.ptr);
  87. snoop->silence.data.ptr = NULL;
  88. }
  89. ast_free(snoop->app);
  90. ast_channel_cleanup(snoop->chan);
  91. }
  92. /*! \internal
  93. * \brief Publish the chanspy message over Stasis-Core
  94. * \param snoop The snoop structure
  95. * \start start If non-zero, the spying is starting. Otherwise, the spyer is
  96. * finishing
  97. */
  98. static void publish_chanspy_message(struct stasis_app_snoop *snoop, int start)
  99. {
  100. RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
  101. RAII_VAR(struct ast_multi_channel_blob *, payload, NULL, ao2_cleanup);
  102. RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
  103. RAII_VAR(struct ast_channel_snapshot *, snoop_snapshot, NULL, ao2_cleanup);
  104. RAII_VAR(struct ast_channel_snapshot *, spyee_snapshot, NULL, ao2_cleanup);
  105. struct stasis_message_type *type = start ? ast_channel_chanspy_start_type(): ast_channel_chanspy_stop_type();
  106. blob = ast_json_null();
  107. if (!blob || !type) {
  108. return;
  109. }
  110. payload = ast_multi_channel_blob_create(blob);
  111. if (!payload) {
  112. return;
  113. }
  114. snoop_snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(snoop->chan));
  115. if (!snoop_snapshot) {
  116. return;
  117. }
  118. ast_multi_channel_blob_add_channel(payload, "spyer_channel", snoop_snapshot);
  119. spyee_snapshot = ast_channel_snapshot_get_latest(snoop->uniqueid);
  120. if (spyee_snapshot) {
  121. ast_multi_channel_blob_add_channel(payload, "spyee_channel", spyee_snapshot);
  122. }
  123. message = stasis_message_create(type, payload);
  124. if (!message) {
  125. return;
  126. }
  127. stasis_publish(ast_channel_topic(snoop->chan), message);
  128. }
  129. /*! \brief Callback function for writing to a Snoop whisper audiohook */
  130. static int snoop_write(struct ast_channel *chan, struct ast_frame *frame)
  131. {
  132. struct stasis_app_snoop *snoop = ast_channel_tech_pvt(chan);
  133. if (!snoop->whisper_active) {
  134. return 0;
  135. }
  136. ast_audiohook_lock(&snoop->whisper);
  137. if (snoop->whisper_direction == AST_AUDIOHOOK_DIRECTION_BOTH) {
  138. ast_audiohook_write_frame(&snoop->whisper, AST_AUDIOHOOK_DIRECTION_READ, frame);
  139. ast_audiohook_write_frame(&snoop->whisper, AST_AUDIOHOOK_DIRECTION_WRITE, frame);
  140. } else {
  141. ast_audiohook_write_frame(&snoop->whisper, snoop->whisper_direction, frame);
  142. }
  143. ast_audiohook_unlock(&snoop->whisper);
  144. return 0;
  145. }
  146. /*! \brief Callback function for reading from a Snoop channel */
  147. static struct ast_frame *snoop_read(struct ast_channel *chan)
  148. {
  149. struct stasis_app_snoop *snoop = ast_channel_tech_pvt(chan);
  150. struct ast_frame *frame = NULL;
  151. /* If we fail to ack the timer OR if any active audiohooks are done hangup */
  152. if ((ast_timer_ack(snoop->timer, 1) < 0) ||
  153. (snoop->spy_active && snoop->spy.status != AST_AUDIOHOOK_STATUS_RUNNING) ||
  154. (snoop->whisper_active && snoop->whisper.status != AST_AUDIOHOOK_STATUS_RUNNING)) {
  155. return NULL;
  156. }
  157. /* Only get audio from the spy audiohook if it is active */
  158. if (!snoop->spy_active) {
  159. return &ast_null_frame;
  160. }
  161. ast_audiohook_lock(&snoop->spy);
  162. if (snoop->spy_direction != AST_AUDIOHOOK_DIRECTION_BOTH) {
  163. /*
  164. * When a singular direction is chosen frames are still written to the
  165. * opposing direction's queue. Those frames must be read so the queue
  166. * does not continue to grow, however since they are not needed for the
  167. * selected direction they can be dropped.
  168. */
  169. enum ast_audiohook_direction opposing_direction =
  170. snoop->spy_direction == AST_AUDIOHOOK_DIRECTION_READ ?
  171. AST_AUDIOHOOK_DIRECTION_WRITE : AST_AUDIOHOOK_DIRECTION_READ;
  172. ast_frame_dtor(ast_audiohook_read_frame(&snoop->spy, snoop->spy_samples,
  173. opposing_direction, snoop->spy_format));
  174. }
  175. frame = ast_audiohook_read_frame(&snoop->spy, snoop->spy_samples, snoop->spy_direction, snoop->spy_format);
  176. ast_audiohook_unlock(&snoop->spy);
  177. return frame ? frame : &snoop->silence;
  178. }
  179. /*! \brief Callback function for hanging up a Snoop channel */
  180. static int snoop_hangup(struct ast_channel *chan)
  181. {
  182. struct stasis_app_snoop *snoop = ast_channel_tech_pvt(chan);
  183. if (snoop->spy_active) {
  184. ast_audiohook_lock(&snoop->spy);
  185. ast_audiohook_detach(&snoop->spy);
  186. ast_audiohook_unlock(&snoop->spy);
  187. }
  188. if (snoop->whisper_active) {
  189. ast_audiohook_lock(&snoop->whisper);
  190. ast_audiohook_detach(&snoop->whisper);
  191. ast_audiohook_unlock(&snoop->whisper);
  192. }
  193. publish_chanspy_message(snoop, 0);
  194. ao2_cleanup(snoop);
  195. ast_channel_tech_pvt_set(chan, NULL);
  196. return 0;
  197. }
  198. static int snoop_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
  199. {
  200. struct stasis_app_snoop *snoop = ast_channel_tech_pvt(oldchan);
  201. if (snoop->chan != oldchan) {
  202. return -1;
  203. }
  204. ast_channel_unref(snoop->chan);
  205. ast_channel_ref(newchan);
  206. snoop->chan = newchan;
  207. return 0;
  208. }
  209. /*! \brief Channel interface declaration */
  210. static struct ast_channel_tech snoop_tech = {
  211. .type = "Snoop",
  212. .description = "Snoop Channel Driver",
  213. .write = snoop_write,
  214. .read = snoop_read,
  215. .hangup = snoop_hangup,
  216. .fixup = snoop_fixup,
  217. };
  218. /*! \brief Thread used for running the Stasis application */
  219. static void *snoop_stasis_thread(void *obj)
  220. {
  221. RAII_VAR(struct stasis_app_snoop *, snoop, obj, ao2_cleanup);
  222. struct ast_app *stasis = pbx_findapp("Stasis");
  223. if (!stasis) {
  224. ast_hangup(snoop->chan);
  225. return NULL;
  226. }
  227. pbx_exec(snoop->chan, stasis, ast_str_buffer(snoop->app));
  228. ast_hangup(snoop->chan);
  229. return NULL;
  230. }
  231. /*! \brief Internal helper function which sets up and attaches a snoop audiohook */
  232. static int snoop_setup_audiohook(struct ast_channel *chan, enum ast_audiohook_type type, enum stasis_app_snoop_direction requested_direction,
  233. enum ast_audiohook_direction *direction, struct ast_audiohook *audiohook)
  234. {
  235. ast_audiohook_init(audiohook, type, "Snoop", 0);
  236. if (requested_direction == STASIS_SNOOP_DIRECTION_OUT) {
  237. *direction = AST_AUDIOHOOK_DIRECTION_WRITE;
  238. } else if (requested_direction == STASIS_SNOOP_DIRECTION_IN) {
  239. *direction = AST_AUDIOHOOK_DIRECTION_READ;
  240. } else if (requested_direction == STASIS_SNOOP_DIRECTION_BOTH) {
  241. *direction = AST_AUDIOHOOK_DIRECTION_BOTH;
  242. } else {
  243. return -1;
  244. }
  245. return ast_audiohook_attach(chan, audiohook);
  246. }
  247. /*! \brief Helper function which gets the format for a Snoop channel based on the channel being snooped on */
  248. static void snoop_determine_format(struct ast_channel *chan, struct stasis_app_snoop *snoop)
  249. {
  250. SCOPED_CHANNELLOCK(lock, chan);
  251. unsigned int rate = MAX(ast_format_get_sample_rate(ast_channel_rawwriteformat(chan)),
  252. ast_format_get_sample_rate(ast_channel_rawreadformat(chan)));
  253. snoop->spy_format = ast_format_cache_get_slin_by_rate(rate);
  254. }
  255. struct ast_channel *stasis_app_control_snoop(struct ast_channel *chan,
  256. enum stasis_app_snoop_direction spy, enum stasis_app_snoop_direction whisper,
  257. const char *app, const char *app_args, const char *snoop_id)
  258. {
  259. RAII_VAR(struct stasis_app_snoop *, snoop, NULL, ao2_cleanup);
  260. struct ast_format_cap *caps;
  261. pthread_t thread;
  262. struct ast_assigned_ids assignedids = {
  263. .uniqueid = snoop_id,
  264. };
  265. if (spy == STASIS_SNOOP_DIRECTION_NONE &&
  266. whisper == STASIS_SNOOP_DIRECTION_NONE) {
  267. return NULL;
  268. }
  269. snoop = ao2_alloc_options(sizeof(*snoop), snoop_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK);
  270. if (!snoop) {
  271. return NULL;
  272. }
  273. /* Allocate a buffer to store the Stasis application and arguments in */
  274. snoop->app = ast_str_create(64);
  275. if (!snoop->app) {
  276. return NULL;
  277. }
  278. ast_str_set(&snoop->app, 0, "%s", app);
  279. if (!ast_strlen_zero(app_args)) {
  280. ast_str_append(&snoop->app, 0, ",%s", app_args);
  281. }
  282. /* Set up a timer for the Snoop channel so it wakes up at a specific interval */
  283. snoop->timer = ast_timer_open();
  284. if (!snoop->timer) {
  285. return NULL;
  286. }
  287. ast_timer_set_rate(snoop->timer, 1000 / SNOOP_INTERVAL);
  288. /* Determine which signed linear format should be used */
  289. snoop_determine_format(chan, snoop);
  290. /* Allocate a Snoop channel and set up various parameters */
  291. snoop->chan = ast_channel_alloc(1, AST_STATE_UP, "", "", "", "", "", &assignedids, NULL, 0, "Snoop/%s-%08x", ast_channel_uniqueid(chan),
  292. (unsigned)ast_atomic_fetchadd_int((int *)&chan_idx, +1));
  293. if (!snoop->chan) {
  294. return NULL;
  295. }
  296. ast_copy_string(snoop->uniqueid, ast_channel_uniqueid(chan), sizeof(snoop->uniqueid));
  297. /* To keep the channel valid on the Snoop structure until it is destroyed we bump the ref up here */
  298. ast_channel_ref(snoop->chan);
  299. ast_channel_tech_set(snoop->chan, &snoop_tech);
  300. ao2_ref(snoop, +1);
  301. ast_channel_tech_pvt_set(snoop->chan, snoop);
  302. ast_channel_set_fd(snoop->chan, 0, ast_timer_fd(snoop->timer));
  303. /* The format on the Snoop channel will be this signed linear format, and it will never change */
  304. caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
  305. if (!caps) {
  306. ast_channel_unlock(snoop->chan);
  307. ast_hangup(snoop->chan);
  308. return NULL;
  309. }
  310. ast_format_cap_append(caps, snoop->spy_format, 0);
  311. ast_channel_nativeformats_set(snoop->chan, caps);
  312. ao2_ref(caps, -1);
  313. ast_channel_set_writeformat(snoop->chan, snoop->spy_format);
  314. ast_channel_set_rawwriteformat(snoop->chan, snoop->spy_format);
  315. ast_channel_set_readformat(snoop->chan, snoop->spy_format);
  316. ast_channel_set_rawreadformat(snoop->chan, snoop->spy_format);
  317. ast_channel_unlock(snoop->chan);
  318. if (spy != STASIS_SNOOP_DIRECTION_NONE) {
  319. if (snoop_setup_audiohook(chan, AST_AUDIOHOOK_TYPE_SPY, spy, &snoop->spy_direction, &snoop->spy)) {
  320. ast_hangup(snoop->chan);
  321. return NULL;
  322. }
  323. snoop->spy_samples = ast_format_get_sample_rate(snoop->spy_format) / (1000 / SNOOP_INTERVAL);
  324. snoop->spy_active = 1;
  325. snoop->silence.frametype = AST_FRAME_VOICE,
  326. snoop->silence.datalen = snoop->spy_samples * sizeof(uint16_t),
  327. snoop->silence.samples = snoop->spy_samples,
  328. snoop->silence.mallocd = 0,
  329. snoop->silence.offset = 0,
  330. snoop->silence.src = __PRETTY_FUNCTION__,
  331. snoop->silence.subclass.format = snoop->spy_format,
  332. snoop->silence.data.ptr = ast_calloc(snoop->spy_samples, sizeof(uint16_t));
  333. if (!snoop->silence.data.ptr) {
  334. ast_hangup(snoop->chan);
  335. return NULL;
  336. }
  337. }
  338. /* If whispering is enabled set up the audiohook */
  339. if (whisper != STASIS_SNOOP_DIRECTION_NONE) {
  340. if (snoop_setup_audiohook(chan, AST_AUDIOHOOK_TYPE_WHISPER, whisper, &snoop->whisper_direction, &snoop->whisper)) {
  341. ast_hangup(snoop->chan);
  342. return NULL;
  343. }
  344. snoop->whisper_active = 1;
  345. }
  346. /* Create the thread which services the Snoop channel */
  347. ao2_ref(snoop, +1);
  348. if (ast_pthread_create_detached_background(&thread, NULL, snoop_stasis_thread, snoop)) {
  349. ao2_cleanup(snoop);
  350. /* No other thread is servicing this channel so we can immediately hang it up */
  351. ast_hangup(snoop->chan);
  352. return NULL;
  353. }
  354. publish_chanspy_message(snoop, 1);
  355. /* The caller of this has a reference as well */
  356. return ast_channel_ref(snoop->chan);
  357. }
  358. static int load_module(void)
  359. {
  360. return AST_MODULE_LOAD_SUCCESS;
  361. }
  362. static int unload_module(void)
  363. {
  364. return 0;
  365. }
  366. AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Stasis application snoop support",
  367. .support_level = AST_MODULE_SUPPORT_CORE,
  368. .load = load_module,
  369. .unload = unload_module,
  370. );