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