create the IAX2 processing threads as background threads so they will use smaller...
[asterisk-bristuff.git] / apps / app_chanspy.c
blobcf1e4759d2e9debd6b86b5f0027af1f6ec1cba01
1 /*
2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2005 Anthony Minessale II (anthmct@yahoo.com)
5 * Copyright (C) 2005 - 2006, Digium, Inc.
7 * A license has been granted to Digium (via disclaimer) for the use of
8 * this code.
10 * See http://www.asterisk.org for more information about
11 * the Asterisk project. Please do not directly contact
12 * any of the maintainers of this project for assistance;
13 * the project provides a web site, mailing lists and IRC
14 * channels for your use.
16 * This program is free software, distributed under the terms of
17 * the GNU General Public License Version 2. See the LICENSE file
18 * at the top of the source tree.
21 /*! \file
23 * \brief ChanSpy: Listen in on any channel.
25 * \author Anthony Minessale II <anthmct@yahoo.com>
27 * \ingroup applications
30 #include "asterisk.h"
32 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
34 #include <stdlib.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <ctype.h>
40 #include "asterisk/file.h"
41 #include "asterisk/logger.h"
42 #include "asterisk/channel.h"
43 #include "asterisk/chanspy.h"
44 #include "asterisk/features.h"
45 #include "asterisk/options.h"
46 #include "asterisk/app.h"
47 #include "asterisk/utils.h"
48 #include "asterisk/say.h"
49 #include "asterisk/pbx.h"
50 #include "asterisk/translate.h"
51 #include "asterisk/module.h"
52 #include "asterisk/lock.h"
54 #define AST_NAME_STRLEN 256
56 static const char *tdesc = "Listen to a channel, and optionally whisper into it";
57 static const char *app_chan = "ChanSpy";
58 static const char *desc_chan =
59 " ChanSpy([chanprefix][|options]): This application is used to listen to the\n"
60 "audio from an Asterisk channel. This includes the audio coming in and\n"
61 "out of the channel being spied on. If the 'chanprefix' parameter is specified,\n"
62 "only channels beginning with this string will be spied upon.\n"
63 " While spying, the following actions may be performed:\n"
64 " - Dialing # cycles the volume level.\n"
65 " - Dialing * will stop spying and look for another channel to spy on.\n"
66 " - Dialing a series of digits followed by # builds a channel name to append\n"
67 " to 'chanprefix'. For example, executing ChanSpy(Agent) and then dialing\n"
68 " the digits '1234#' while spying will begin spying on the channel\n"
69 " 'Agent/1234'.\n"
70 " Options:\n"
71 " b - Only spy on channels involved in a bridged call.\n"
72 " g(grp) - Match only channels where their ${SPYGROUP} variable is set to\n"
73 " contain 'grp' in an optional : delimited list.\n"
74 " q - Don't play a beep when beginning to spy on a channel, or speak the\n"
75 " selected channel name.\n"
76 " r[(basename)] - Record the session to the monitor spool directory. An\n"
77 " optional base for the filename may be specified. The\n"
78 " default is 'chanspy'.\n"
79 " v([value]) - Adjust the initial volume in the range from -4 to 4. A\n"
80 " negative value refers to a quieter setting.\n"
81 " w - Enable 'whisper' mode, so the spying channel can talk to\n"
82 " the spied-on channel.\n"
83 " W - Enable 'private whisper' mode, so the spying channel can\n"
84 " talk to the spied-on channel but cannot listen to that\n"
85 " channel.\n"
88 static const char *app_ext = "ExtenSpy";
89 static const char *desc_ext =
90 " ExtenSpy(exten[@context][|options]): This application is used to listen to the\n"
91 "audio from an Asterisk channel. This includes the audio coming in and\n"
92 "out of the channel being spied on. Only channels created by outgoing calls for the\n"
93 "specified extension will be selected for spying. If the optional context is not\n"
94 "supplied, the current channel's context will be used.\n"
95 " While spying, the following actions may be performed:\n"
96 " - Dialing # cycles the volume level.\n"
97 " - Dialing * will stop spying and look for another channel to spy on.\n"
98 " Options:\n"
99 " b - Only spy on channels involved in a bridged call.\n"
100 " g(grp) - Match only channels where their ${SPYGROUP} variable is set to\n"
101 " contain 'grp' in an optional : delimited list.\n"
102 " q - Don't play a beep when beginning to spy on a channel, or speak the\n"
103 " selected channel name.\n"
104 " r[(basename)] - Record the session to the monitor spool directory. An\n"
105 " optional base for the filename may be specified. The\n"
106 " default is 'chanspy'.\n"
107 " v([value]) - Adjust the initial volume in the range from -4 to 4. A\n"
108 " negative value refers to a quieter setting.\n"
109 " w - Enable 'whisper' mode, so the spying channel can talk to\n"
110 " the spied-on channel.\n"
111 " W - Enable 'private whisper' mode, so the spying channel can\n"
112 " talk to the spied-on channel but cannot listen to that\n"
113 " channel.\n"
116 enum {
117 OPTION_QUIET = (1 << 0), /* Quiet, no announcement */
118 OPTION_BRIDGED = (1 << 1), /* Only look at bridged calls */
119 OPTION_VOLUME = (1 << 2), /* Specify initial volume */
120 OPTION_GROUP = (1 << 3), /* Only look at channels in group */
121 OPTION_RECORD = (1 << 4),
122 OPTION_WHISPER = (1 << 5),
123 OPTION_PRIVATE = (1 << 6), /* Private Whisper mode */
124 } chanspy_opt_flags;
126 enum {
127 OPT_ARG_VOLUME = 0,
128 OPT_ARG_GROUP,
129 OPT_ARG_RECORD,
130 OPT_ARG_ARRAY_SIZE,
131 } chanspy_opt_args;
133 AST_APP_OPTIONS(spy_opts, {
134 AST_APP_OPTION('q', OPTION_QUIET),
135 AST_APP_OPTION('b', OPTION_BRIDGED),
136 AST_APP_OPTION('w', OPTION_WHISPER),
137 AST_APP_OPTION('W', OPTION_PRIVATE),
138 AST_APP_OPTION_ARG('v', OPTION_VOLUME, OPT_ARG_VOLUME),
139 AST_APP_OPTION_ARG('g', OPTION_GROUP, OPT_ARG_GROUP),
140 AST_APP_OPTION_ARG('r', OPTION_RECORD, OPT_ARG_RECORD),
144 struct chanspy_translation_helper {
145 /* spy data */
146 struct ast_channel_spy spy;
147 int fd;
148 int volfactor;
151 static void *spy_alloc(struct ast_channel *chan, void *data)
153 /* just store the data pointer in the channel structure */
154 return data;
157 static void spy_release(struct ast_channel *chan, void *data)
159 /* nothing to do */
162 static int spy_generate(struct ast_channel *chan, void *data, int len, int samples)
164 struct chanspy_translation_helper *csth = data;
165 struct ast_frame *f;
167 if (csth->spy.status != CHANSPY_RUNNING)
168 /* Channel is already gone more than likely */
169 return -1;
171 ast_mutex_lock(&csth->spy.lock);
172 f = ast_channel_spy_read_frame(&csth->spy, samples);
173 ast_mutex_unlock(&csth->spy.lock);
175 if (!f)
176 return 0;
178 if (ast_write(chan, f)) {
179 ast_frfree(f);
180 return -1;
183 if (csth->fd)
184 write(csth->fd, f->data, f->datalen);
186 ast_frfree(f);
188 return 0;
191 static struct ast_generator spygen = {
192 .alloc = spy_alloc,
193 .release = spy_release,
194 .generate = spy_generate,
197 static int start_spying(struct ast_channel *chan, struct ast_channel *spychan, struct ast_channel_spy *spy)
199 int res;
200 struct ast_channel *peer;
202 ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan->name, chan->name);
204 ast_channel_lock(chan);
205 res = ast_channel_spy_add(chan, spy);
206 ast_channel_unlock(chan);
208 if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan)))
209 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
211 return res;
214 /* Map 'volume' levels from -4 through +4 into
215 decibel (dB) settings for channel drivers
217 static signed char volfactor_map[] = {
218 -24,
219 -18,
220 -12,
229 /* attempt to set the desired gain adjustment via the channel driver;
230 if successful, clear it out of the csth structure so the
231 generator will not attempt to do the adjustment itself
233 static void set_volume(struct ast_channel *chan, struct chanspy_translation_helper *csth)
235 signed char volume_adjust = volfactor_map[csth->volfactor + 4];
237 if (!ast_channel_setoption(chan, AST_OPTION_TXGAIN, &volume_adjust, sizeof(volume_adjust), 0))
238 csth->volfactor = 0;
239 csth->spy.read_vol_adjustment = csth->volfactor;
240 csth->spy.write_vol_adjustment = csth->volfactor;
243 static int channel_spy(struct ast_channel *chan, struct ast_channel *spyee, int *volfactor, int fd,
244 const struct ast_flags *flags)
246 struct chanspy_translation_helper csth;
247 int running = 0, res, x = 0;
248 char inp[24] = {0};
249 char *name;
250 struct ast_frame *f;
251 struct ast_silence_generator *silgen = NULL;
253 if (ast_check_hangup(chan) || ast_check_hangup(spyee))
254 return 0;
256 name = ast_strdupa(spyee->name);
257 if (option_verbose >= 2)
258 ast_verbose(VERBOSE_PREFIX_2 "Spying on channel %s\n", name);
260 memset(&csth, 0, sizeof(csth));
261 ast_set_flag(&csth.spy, CHANSPY_FORMAT_AUDIO);
262 ast_set_flag(&csth.spy, CHANSPY_TRIGGER_NONE);
263 ast_set_flag(&csth.spy, CHANSPY_MIXAUDIO);
264 csth.spy.type = "ChanSpy";
265 csth.spy.status = CHANSPY_RUNNING;
266 csth.spy.read_queue.format = AST_FORMAT_SLINEAR;
267 csth.spy.write_queue.format = AST_FORMAT_SLINEAR;
268 ast_mutex_init(&csth.spy.lock);
269 csth.volfactor = *volfactor;
270 set_volume(chan, &csth);
271 if (csth.volfactor) {
272 ast_set_flag(&csth.spy, CHANSPY_READ_VOLADJUST);
273 csth.spy.read_vol_adjustment = csth.volfactor;
274 ast_set_flag(&csth.spy, CHANSPY_WRITE_VOLADJUST);
275 csth.spy.write_vol_adjustment = csth.volfactor;
277 csth.fd = fd;
279 if (start_spying(spyee, chan, &csth.spy)) {
280 ast_mutex_destroy(&csth.spy.lock);
281 return 0;
284 if (ast_test_flag(flags, OPTION_WHISPER)) {
285 struct ast_filestream *beepstream;
286 int old_write_format = 0;
288 ast_channel_whisper_start(csth.spy.chan);
289 old_write_format = chan->writeformat;
290 if ((beepstream = ast_openstream_full(chan, "beep", chan->language, 1))) {
291 struct ast_frame *f;
293 while ((f = ast_readframe(beepstream))) {
294 ast_channel_whisper_feed(csth.spy.chan, f);
295 ast_frfree(f);
298 ast_closestream(beepstream);
299 chan->stream = NULL;
301 if (old_write_format)
302 ast_set_write_format(chan, old_write_format);
305 if (ast_test_flag(flags, OPTION_PRIVATE))
306 silgen = ast_channel_start_silence_generator(chan);
307 else
308 ast_activate_generator(chan, &spygen, &csth);
310 /* We can no longer rely on 'spyee' being an actual channel;
311 it can be hung up and freed out from under us. However, the
312 channel destructor will put NULL into our csth.spy.chan
313 field when that happens, so that is our signal that the spyee
314 channel has gone away.
317 /* Note: it is very important that the ast_waitfor() be the first
318 condition in this expression, so that if we wait for some period
319 of time before receiving a frame from our spying channel, we check
320 for hangup on the spied-on channel _after_ knowing that a frame
321 has arrived, since the spied-on channel could have gone away while
322 we were waiting
324 while ((res = ast_waitfor(chan, -1) > -1) &&
325 csth.spy.status == CHANSPY_RUNNING &&
326 csth.spy.chan) {
327 if (!(f = ast_read(chan)) || ast_check_hangup(chan)) {
328 running = -1;
329 break;
332 if (ast_test_flag(flags, OPTION_WHISPER) &&
333 (f->frametype == AST_FRAME_VOICE)) {
334 ast_channel_whisper_feed(csth.spy.chan, f);
335 ast_frfree(f);
336 continue;
339 res = (f->frametype == AST_FRAME_DTMF) ? f->subclass : 0;
340 ast_frfree(f);
341 if (!res)
342 continue;
344 if (x == sizeof(inp))
345 x = 0;
347 if (res < 0) {
348 running = -1;
349 break;
352 if (res == '*') {
353 running = 0;
354 break;
355 } else if (res == '#') {
356 if (!ast_strlen_zero(inp)) {
357 running = atoi(inp);
358 break;
361 (*volfactor)++;
362 if (*volfactor > 4)
363 *volfactor = -4;
364 if (option_verbose > 2)
365 ast_verbose(VERBOSE_PREFIX_3 "Setting spy volume on %s to %d\n", chan->name, *volfactor);
366 csth.volfactor = *volfactor;
367 set_volume(chan, &csth);
368 if (csth.volfactor) {
369 ast_set_flag(&csth.spy, CHANSPY_READ_VOLADJUST);
370 csth.spy.read_vol_adjustment = csth.volfactor;
371 ast_set_flag(&csth.spy, CHANSPY_WRITE_VOLADJUST);
372 csth.spy.write_vol_adjustment = csth.volfactor;
373 } else {
374 ast_clear_flag(&csth.spy, CHANSPY_READ_VOLADJUST);
375 ast_clear_flag(&csth.spy, CHANSPY_WRITE_VOLADJUST);
377 } else if (res >= '0' && res <= '9') {
378 inp[x++] = res;
382 if (ast_test_flag(flags, OPTION_WHISPER) && csth.spy.chan)
383 ast_channel_whisper_stop(csth.spy.chan);
385 if (ast_test_flag(flags, OPTION_PRIVATE))
386 ast_channel_stop_silence_generator(chan, silgen);
387 else
388 ast_deactivate_generator(chan);
390 /* If a channel still exists on our spy structure then we need to remove ourselves */
391 if (csth.spy.chan) {
392 csth.spy.status = CHANSPY_DONE;
393 ast_channel_lock(csth.spy.chan);
394 ast_channel_spy_remove(csth.spy.chan, &csth.spy);
395 ast_channel_unlock(csth.spy.chan);
397 ast_channel_spy_free(&csth.spy);
399 if (option_verbose >= 2)
400 ast_verbose(VERBOSE_PREFIX_2 "Done Spying on channel %s\n", name);
402 return running;
405 static struct ast_channel *next_channel(const struct ast_channel *last, const char *spec,
406 const char *exten, const char *context)
408 struct ast_channel *this;
410 redo:
411 if (spec)
412 this = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec));
413 else if (exten)
414 this = ast_walk_channel_by_exten_locked(last, exten, context);
415 else
416 this = ast_channel_walk_locked(last);
418 if (this) {
419 ast_channel_unlock(this);
420 if (!strncmp(this->name, "Zap/pseudo", 10))
421 goto redo;
424 return this;
427 static int common_exec(struct ast_channel *chan, const struct ast_flags *flags,
428 int volfactor, const int fd, const char *mygroup, const char *spec,
429 const char *exten, const char *context)
431 struct ast_channel *peer, *prev, *next;
432 char nameprefix[AST_NAME_STRLEN];
433 char peer_name[AST_NAME_STRLEN + 5];
434 signed char zero_volume = 0;
435 int waitms;
436 int res;
437 char *ptr;
438 int num;
440 if (chan->_state != AST_STATE_UP)
441 ast_answer(chan);
443 ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */
445 waitms = 100;
447 for (;;) {
448 if (!ast_test_flag(flags, OPTION_QUIET)) {
449 res = ast_streamfile(chan, "beep", chan->language);
450 if (!res)
451 res = ast_waitstream(chan, "");
452 else if (res < 0) {
453 ast_clear_flag(chan, AST_FLAG_SPYING);
454 break;
458 res = ast_waitfordigit(chan, waitms);
459 if (res < 0) {
460 ast_clear_flag(chan, AST_FLAG_SPYING);
461 break;
464 /* reset for the next loop around, unless overridden later */
465 waitms = 100;
466 peer = prev = next = NULL;
468 for (peer = next_channel(peer, spec, exten, context);
469 peer;
470 prev = peer, peer = next ? next : next_channel(peer, spec, exten, context), next = NULL) {
471 const char *group;
472 int igrp = !mygroup;
473 char *groups[25];
474 int num_groups = 0;
475 char *dup_group;
476 int x;
477 char *s;
479 if (peer == prev)
480 break;
482 if (peer == chan)
483 continue;
485 if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer))
486 continue;
488 if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING))
489 continue;
491 if (mygroup) {
492 if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) {
493 dup_group = ast_strdupa(group);
494 num_groups = ast_app_separate_args(dup_group, ':', groups,
495 sizeof(groups) / sizeof(groups[0]));
498 for (x = 0; x < num_groups; x++) {
499 if (!strcmp(mygroup, groups[x])) {
500 igrp = 1;
501 break;
506 if (!igrp)
507 continue;
509 strcpy(peer_name, "spy-");
510 strncat(peer_name, peer->name, AST_NAME_STRLEN);
511 ptr = strchr(peer_name, '/');
512 *ptr++ = '\0';
514 for (s = peer_name; s < ptr; s++)
515 *s = tolower(*s);
517 if (!ast_test_flag(flags, OPTION_QUIET)) {
518 if (ast_fileexists(peer_name, NULL, NULL) != -1) {
519 res = ast_streamfile(chan, peer_name, chan->language);
520 if (!res)
521 res = ast_waitstream(chan, "");
522 if (res)
523 break;
524 } else
525 res = ast_say_character_str(chan, peer_name, "", chan->language);
526 if ((num = atoi(ptr)))
527 ast_say_digits(chan, atoi(ptr), "", chan->language);
530 waitms = 5000;
531 res = channel_spy(chan, peer, &volfactor, fd, flags);
533 if (res == -1) {
534 break;
535 } else if (res > 1 && spec) {
536 snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);
537 if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) {
538 ast_channel_unlock(next);
539 } else {
540 /* stay on this channel */
541 next = peer;
543 peer = NULL;
546 if (res == -1)
547 break;
550 ast_clear_flag(chan, AST_FLAG_SPYING);
552 ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
554 return res;
557 static int chanspy_exec(struct ast_channel *chan, void *data)
559 struct ast_module_user *u;
560 char *options = NULL;
561 char *spec = NULL;
562 char *argv[2];
563 char *mygroup = NULL;
564 char *recbase = NULL;
565 int fd = 0;
566 struct ast_flags flags;
567 int oldwf = 0;
568 int argc = 0;
569 int volfactor = 0;
570 int res;
572 data = ast_strdupa(data);
574 u = ast_module_user_add(chan);
576 if ((argc = ast_app_separate_args(data, '|', argv, sizeof(argv) / sizeof(argv[0])))) {
577 spec = argv[0];
578 if (argc > 1)
579 options = argv[1];
581 if (ast_strlen_zero(spec) || !strcmp(spec, "all"))
582 spec = NULL;
585 if (options) {
586 char *opts[OPT_ARG_ARRAY_SIZE];
588 ast_app_parse_options(spy_opts, &flags, opts, options);
589 if (ast_test_flag(&flags, OPTION_GROUP))
590 mygroup = opts[OPT_ARG_GROUP];
592 if (ast_test_flag(&flags, OPTION_RECORD) &&
593 !(recbase = opts[OPT_ARG_RECORD]))
594 recbase = "chanspy";
596 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
597 int vol;
599 if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
600 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
601 else
602 volfactor = vol;
605 if (ast_test_flag(&flags, OPTION_PRIVATE))
606 ast_set_flag(&flags, OPTION_WHISPER);
609 oldwf = chan->writeformat;
610 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
611 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
612 ast_module_user_remove(u);
613 return -1;
616 if (recbase) {
617 char filename[512];
619 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
620 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644)) <= 0) {
621 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
622 fd = 0;
626 res = common_exec(chan, &flags, volfactor, fd, mygroup, spec, NULL, NULL);
628 if (fd)
629 close(fd);
631 if (oldwf && ast_set_write_format(chan, oldwf) < 0)
632 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
634 ast_module_user_remove(u);
636 return res;
639 static int extenspy_exec(struct ast_channel *chan, void *data)
641 struct ast_module_user *u;
642 char *options = NULL;
643 char *exten = NULL;
644 char *context = NULL;
645 char *argv[2];
646 char *mygroup = NULL;
647 char *recbase = NULL;
648 int fd = 0;
649 struct ast_flags flags;
650 int oldwf = 0;
651 int argc = 0;
652 int volfactor = 0;
653 int res;
655 data = ast_strdupa(data);
657 u = ast_module_user_add(chan);
659 if ((argc = ast_app_separate_args(data, '|', argv, sizeof(argv) / sizeof(argv[0])))) {
660 context = argv[0];
661 if (!ast_strlen_zero(argv[0]))
662 exten = strsep(&context, "@");
663 if (ast_strlen_zero(context))
664 context = ast_strdupa(chan->context);
665 if (argc > 1)
666 options = argv[1];
669 if (options) {
670 char *opts[OPT_ARG_ARRAY_SIZE];
672 ast_app_parse_options(spy_opts, &flags, opts, options);
673 if (ast_test_flag(&flags, OPTION_GROUP))
674 mygroup = opts[OPT_ARG_GROUP];
676 if (ast_test_flag(&flags, OPTION_RECORD) &&
677 !(recbase = opts[OPT_ARG_RECORD]))
678 recbase = "chanspy";
680 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
681 int vol;
683 if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
684 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
685 else
686 volfactor = vol;
689 if (ast_test_flag(&flags, OPTION_PRIVATE))
690 ast_set_flag(&flags, OPTION_WHISPER);
693 oldwf = chan->writeformat;
694 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
695 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
696 ast_module_user_remove(u);
697 return -1;
700 if (recbase) {
701 char filename[512];
703 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
704 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644)) <= 0) {
705 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
706 fd = 0;
710 res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, exten, context);
712 if (fd)
713 close(fd);
715 if (oldwf && ast_set_write_format(chan, oldwf) < 0)
716 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
718 ast_module_user_remove(u);
720 return res;
723 static int unload_module(void)
725 int res = 0;
727 res |= ast_unregister_application(app_chan);
728 res |= ast_unregister_application(app_ext);
730 ast_module_user_hangup_all();
732 return res;
735 static int load_module(void)
737 int res = 0;
739 res |= ast_register_application(app_chan, chanspy_exec, tdesc, desc_chan);
740 res |= ast_register_application(app_ext, extenspy_exec, tdesc, desc_ext);
742 return res;
745 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Listen to the audio of an active channel");