2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2005 Anthony Minessale II (anthmct@yahoo.com)
5 * Copyright (C) 2005 - 2008, Digium, Inc.
7 * A license has been granted to Digium (via disclaimer) for the use of
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.
23 * \brief ChanSpy: Listen in on any channel.
25 * \author Anthony Minessale II <anthmct@yahoo.com>
26 * \author Joshua Colp <jcolp@digium.com>
27 * \author Russell Bryant <russell@digium.com>
29 * \ingroup applications
34 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
42 #include "asterisk/file.h"
43 #include "asterisk/logger.h"
44 #include "asterisk/channel.h"
45 #include "asterisk/audiohook.h"
46 #include "asterisk/features.h"
47 #include "asterisk/options.h"
48 #include "asterisk/app.h"
49 #include "asterisk/utils.h"
50 #include "asterisk/say.h"
51 #include "asterisk/pbx.h"
52 #include "asterisk/translate.h"
53 #include "asterisk/module.h"
54 #include "asterisk/lock.h"
56 #define AST_NAME_STRLEN 256
58 /* "Zap/pseudo" is ten characters.
59 * "DAHDI/pseudo" is twelve characters.
62 static const char *tdesc
= "Listen to a channel, and optionally whisper into it";
63 static const char *app_chan
= "ChanSpy";
64 static const char *desc_chan
=
65 " ChanSpy([chanprefix][|options]): This application is used to listen to the\n"
66 "audio from an Asterisk channel. This includes the audio coming in and\n"
67 "out of the channel being spied on. If the 'chanprefix' parameter is specified,\n"
68 "only channels beginning with this string will be spied upon.\n"
69 " While spying, the following actions may be performed:\n"
70 " - Dialing # cycles the volume level.\n"
71 " - Dialing * will stop spying and look for another channel to spy on.\n"
72 " - Dialing a series of digits followed by # builds a channel name to append\n"
73 " to 'chanprefix'. For example, executing ChanSpy(Agent) and then dialing\n"
74 " the digits '1234#' while spying will begin spying on the channel\n"
77 " b - Only spy on channels involved in a bridged call.\n"
78 " g(grp) - Match only channels where their ${SPYGROUP} variable is set to\n"
79 " contain 'grp' in an optional : delimited list.\n"
80 " q - Don't play a beep when beginning to spy on a channel, or speak the\n"
81 " selected channel name.\n"
82 " r[(basename)] - Record the session to the monitor spool directory. An\n"
83 " optional base for the filename may be specified. The\n"
84 " default is 'chanspy'.\n"
85 " v([value]) - Adjust the initial volume in the range from -4 to 4. A\n"
86 " negative value refers to a quieter setting.\n"
87 " w - Enable 'whisper' mode, so the spying channel can talk to\n"
88 " the spied-on channel.\n"
89 " W - Enable 'private whisper' mode, so the spying channel can\n"
90 " talk to the spied-on channel but cannot listen to that\n"
94 static const char *app_ext
= "ExtenSpy";
95 static const char *desc_ext
=
96 " ExtenSpy(exten[@context][|options]): This application is used to listen to the\n"
97 "audio from an Asterisk channel. This includes the audio coming in and\n"
98 "out of the channel being spied on. Only channels created by outgoing calls for the\n"
99 "specified extension will be selected for spying. If the optional context is not\n"
100 "supplied, the current channel's context will be used.\n"
101 " While spying, the following actions may be performed:\n"
102 " - Dialing # cycles the volume level.\n"
103 " - Dialing * will stop spying and look for another channel to spy on.\n"
105 " b - Only spy on channels involved in a bridged call.\n"
106 " g(grp) - Match only channels where their ${SPYGROUP} variable is set to\n"
107 " contain 'grp' in an optional : delimited list.\n"
108 " q - Don't play a beep when beginning to spy on a channel, or speak the\n"
109 " selected channel name.\n"
110 " r[(basename)] - Record the session to the monitor spool directory. An\n"
111 " optional base for the filename may be specified. The\n"
112 " default is 'chanspy'.\n"
113 " v([value]) - Adjust the initial volume in the range from -4 to 4. A\n"
114 " negative value refers to a quieter setting.\n"
115 " w - Enable 'whisper' mode, so the spying channel can talk to\n"
116 " the spied-on channel.\n"
117 " W - Enable 'private whisper' mode, so the spying channel can\n"
118 " talk to the spied-on channel but cannot listen to that\n"
123 OPTION_QUIET
= (1 << 0), /* Quiet, no announcement */
124 OPTION_BRIDGED
= (1 << 1), /* Only look at bridged calls */
125 OPTION_VOLUME
= (1 << 2), /* Specify initial volume */
126 OPTION_GROUP
= (1 << 3), /* Only look at channels in group */
127 OPTION_RECORD
= (1 << 4),
128 OPTION_WHISPER
= (1 << 5),
129 OPTION_PRIVATE
= (1 << 6), /* Private Whisper mode */
139 AST_APP_OPTIONS(spy_opts
, {
140 AST_APP_OPTION('q', OPTION_QUIET
),
141 AST_APP_OPTION('b', OPTION_BRIDGED
),
142 AST_APP_OPTION('w', OPTION_WHISPER
),
143 AST_APP_OPTION('W', OPTION_PRIVATE
),
144 AST_APP_OPTION_ARG('v', OPTION_VOLUME
, OPT_ARG_VOLUME
),
145 AST_APP_OPTION_ARG('g', OPTION_GROUP
, OPT_ARG_GROUP
),
146 AST_APP_OPTION_ARG('r', OPTION_RECORD
, OPT_ARG_RECORD
),
149 static int next_unique_id_to_use
= 0;
151 struct chanspy_translation_helper
{
153 struct ast_audiohook spy_audiohook
;
154 struct ast_audiohook whisper_audiohook
;
159 static void *spy_alloc(struct ast_channel
*chan
, void *data
)
161 /* just store the data pointer in the channel structure */
165 static void spy_release(struct ast_channel
*chan
, void *data
)
170 static int spy_generate(struct ast_channel
*chan
, void *data
, int len
, int samples
)
172 struct chanspy_translation_helper
*csth
= data
;
175 ast_audiohook_lock(&csth
->spy_audiohook
);
176 if (csth
->spy_audiohook
.status
!= AST_AUDIOHOOK_STATUS_RUNNING
) {
177 ast_audiohook_unlock(&csth
->spy_audiohook
);
181 f
= ast_audiohook_read_frame(&csth
->spy_audiohook
, samples
, AST_AUDIOHOOK_DIRECTION_BOTH
, AST_FORMAT_SLINEAR
);
183 ast_audiohook_unlock(&csth
->spy_audiohook
);
188 if (ast_write(chan
, f
)) {
194 write(csth
->fd
, f
->data
, f
->datalen
);
201 static struct ast_generator spygen
= {
203 .release
= spy_release
,
204 .generate
= spy_generate
,
207 static int start_spying(struct ast_channel
*chan
, const char *spychan_name
, struct ast_audiohook
*audiohook
)
210 struct ast_channel
*peer
;
212 ast_log(LOG_NOTICE
, "Attaching %s to %s\n", spychan_name
, chan
->name
);
214 res
= ast_audiohook_attach(chan
, audiohook
);
216 if (!res
&& ast_test_flag(chan
, AST_FLAG_NBRIDGE
) && (peer
= ast_bridged_channel(chan
))) {
217 ast_softhangup(peer
, AST_SOFTHANGUP_UNBRIDGE
);
223 struct ast_channel
*chan
;
228 static int channel_spy(struct ast_channel
*chan
, struct chanspy_ds
*spyee_chanspy_ds
,
229 int *volfactor
, int fd
, const struct ast_flags
*flags
)
231 struct chanspy_translation_helper csth
;
232 int running
= 0, res
, x
= 0;
236 struct ast_silence_generator
*silgen
= NULL
;
237 struct ast_channel
*spyee
= NULL
;
238 const char *spyer_name
;
240 ast_channel_lock(chan
);
241 spyer_name
= ast_strdupa(chan
->name
);
242 ast_channel_unlock(chan
);
244 ast_mutex_lock(&spyee_chanspy_ds
->lock
);
245 if (spyee_chanspy_ds
->chan
) {
246 spyee
= spyee_chanspy_ds
->chan
;
247 ast_channel_lock(spyee
);
249 ast_mutex_unlock(&spyee_chanspy_ds
->lock
);
254 /* We now hold the channel lock on spyee */
256 if (ast_check_hangup(chan
) || ast_check_hangup(spyee
)) {
257 ast_channel_unlock(spyee
);
261 name
= ast_strdupa(spyee
->name
);
262 if (option_verbose
>= 2)
263 ast_verbose(VERBOSE_PREFIX_2
"Spying on channel %s\n", name
);
265 memset(&csth
, 0, sizeof(csth
));
267 ast_audiohook_init(&csth
.spy_audiohook
, AST_AUDIOHOOK_TYPE_SPY
, "ChanSpy");
269 if (start_spying(spyee
, spyer_name
, &csth
.spy_audiohook
)) {
270 ast_audiohook_destroy(&csth
.spy_audiohook
);
271 ast_channel_unlock(spyee
);
275 if (ast_test_flag(flags
, OPTION_WHISPER
)) {
276 ast_audiohook_init(&csth
.whisper_audiohook
, AST_AUDIOHOOK_TYPE_WHISPER
, "ChanSpy");
277 start_spying(spyee
, spyer_name
, &csth
.whisper_audiohook
);
280 ast_channel_unlock(spyee
);
283 csth
.volfactor
= *volfactor
;
285 if (csth
.volfactor
) {
286 csth
.spy_audiohook
.options
.read_volume
= csth
.volfactor
;
287 csth
.spy_audiohook
.options
.write_volume
= csth
.volfactor
;
292 if (ast_test_flag(flags
, OPTION_PRIVATE
))
293 silgen
= ast_channel_start_silence_generator(chan
);
295 ast_activate_generator(chan
, &spygen
, &csth
);
297 /* We can no longer rely on 'spyee' being an actual channel;
298 it can be hung up and freed out from under us. However, the
299 channel destructor will put NULL into our csth.spy.chan
300 field when that happens, so that is our signal that the spyee
301 channel has gone away.
304 /* Note: it is very important that the ast_waitfor() be the first
305 condition in this expression, so that if we wait for some period
306 of time before receiving a frame from our spying channel, we check
307 for hangup on the spied-on channel _after_ knowing that a frame
308 has arrived, since the spied-on channel could have gone away while
311 while ((res
= ast_waitfor(chan
, -1) > -1) && csth
.spy_audiohook
.status
== AST_AUDIOHOOK_STATUS_RUNNING
) {
312 if (!(f
= ast_read(chan
)) || ast_check_hangup(chan
)) {
317 if (ast_test_flag(flags
, OPTION_WHISPER
) && (f
->frametype
== AST_FRAME_VOICE
)) {
318 ast_audiohook_lock(&csth
.whisper_audiohook
);
319 ast_audiohook_write_frame(&csth
.whisper_audiohook
, AST_AUDIOHOOK_DIRECTION_WRITE
, f
);
320 ast_audiohook_unlock(&csth
.whisper_audiohook
);
325 res
= (f
->frametype
== AST_FRAME_DTMF
) ? f
->subclass
: 0;
330 if (x
== sizeof(inp
))
341 } else if (res
== '#') {
342 if (!ast_strlen_zero(inp
)) {
350 if (option_verbose
> 2)
351 ast_verbose(VERBOSE_PREFIX_3
"Setting spy volume on %s to %d\n", chan
->name
, *volfactor
);
352 csth
.volfactor
= *volfactor
;
353 csth
.spy_audiohook
.options
.read_volume
= csth
.volfactor
;
354 csth
.spy_audiohook
.options
.write_volume
= csth
.volfactor
;
355 } else if (res
>= '0' && res
<= '9') {
360 if (ast_test_flag(flags
, OPTION_PRIVATE
))
361 ast_channel_stop_silence_generator(chan
, silgen
);
363 ast_deactivate_generator(chan
);
365 if (ast_test_flag(flags
, OPTION_WHISPER
)) {
366 ast_audiohook_lock(&csth
.whisper_audiohook
);
367 ast_audiohook_detach(&csth
.whisper_audiohook
);
368 ast_audiohook_unlock(&csth
.whisper_audiohook
);
369 ast_audiohook_destroy(&csth
.whisper_audiohook
);
372 ast_audiohook_lock(&csth
.spy_audiohook
);
373 ast_audiohook_detach(&csth
.spy_audiohook
);
374 ast_audiohook_unlock(&csth
.spy_audiohook
);
375 ast_audiohook_destroy(&csth
.spy_audiohook
);
377 if (option_verbose
>= 2)
378 ast_verbose(VERBOSE_PREFIX_2
"Done Spying on channel %s\n", name
);
384 * \note This relies on the embedded lock to be recursive, as it may be called
385 * due to a call to chanspy_ds_free with the lock held there.
387 static void chanspy_ds_destroy(void *data
)
389 struct chanspy_ds
*chanspy_ds
= data
;
391 /* Setting chan to be NULL is an atomic operation, but we don't want this
392 * value to change while this lock is held. The lock is held elsewhere
393 * while it performs non-atomic operations with this channel pointer */
395 ast_mutex_lock(&chanspy_ds
->lock
);
396 chanspy_ds
->chan
= NULL
;
397 ast_mutex_unlock(&chanspy_ds
->lock
);
400 static void chanspy_ds_chan_fixup(void *data
, struct ast_channel
*old_chan
, struct ast_channel
*new_chan
)
402 struct chanspy_ds
*chanspy_ds
= data
;
404 ast_mutex_lock(&chanspy_ds
->lock
);
405 chanspy_ds
->chan
= new_chan
;
406 ast_mutex_unlock(&chanspy_ds
->lock
);
409 static const struct ast_datastore_info chanspy_ds_info
= {
411 .destroy
= chanspy_ds_destroy
,
412 .chan_fixup
= chanspy_ds_chan_fixup
,
415 static struct chanspy_ds
*chanspy_ds_free(struct chanspy_ds
*chanspy_ds
)
420 ast_mutex_lock(&chanspy_ds
->lock
);
421 if (chanspy_ds
->chan
) {
422 struct ast_datastore
*datastore
;
423 struct ast_channel
*chan
;
425 chan
= chanspy_ds
->chan
;
427 ast_channel_lock(chan
);
428 if ((datastore
= ast_channel_datastore_find(chan
, &chanspy_ds_info
, chanspy_ds
->unique_id
))) {
429 ast_channel_datastore_remove(chan
, datastore
);
430 /* chanspy_ds->chan is NULL after this call */
431 chanspy_ds_destroy(datastore
->data
);
432 datastore
->data
= NULL
;
433 ast_channel_datastore_free(datastore
);
435 ast_channel_unlock(chan
);
437 ast_mutex_unlock(&chanspy_ds
->lock
);
442 /*! \note Returns the channel in the chanspy_ds locked as well as the chanspy_ds locked */
443 static struct chanspy_ds
*setup_chanspy_ds(struct ast_channel
*chan
, struct chanspy_ds
*chanspy_ds
)
445 struct ast_datastore
*datastore
= NULL
;
447 ast_mutex_lock(&chanspy_ds
->lock
);
449 if (!(datastore
= ast_channel_datastore_alloc(&chanspy_ds_info
, chanspy_ds
->unique_id
))) {
450 ast_mutex_unlock(&chanspy_ds
->lock
);
451 chanspy_ds
= chanspy_ds_free(chanspy_ds
);
452 ast_channel_unlock(chan
);
456 chanspy_ds
->chan
= chan
;
457 datastore
->data
= chanspy_ds
;
458 ast_channel_datastore_add(chan
, datastore
);
463 static struct chanspy_ds
*next_channel(struct ast_channel
*chan
,
464 const struct ast_channel
*last
, const char *spec
,
465 const char *exten
, const char *context
, struct chanspy_ds
*chanspy_ds
)
467 struct ast_channel
*this;
468 char channel_name
[AST_CHANNEL_NAME
];
469 static size_t PSEUDO_CHAN_LEN
= 0;
471 if (!PSEUDO_CHAN_LEN
) {
472 PSEUDO_CHAN_LEN
= *dahdi_chan_name_len
+ strlen("/pseudo");
477 this = ast_walk_channel_by_name_prefix_locked(last
, spec
, strlen(spec
));
479 this = ast_walk_channel_by_exten_locked(last
, exten
, context
);
481 this = ast_channel_walk_locked(last
);
486 snprintf(channel_name
, AST_CHANNEL_NAME
, "%s/pseudo", dahdi_chan_name
);
487 if (!strncmp(this->name
, channel_name
, PSEUDO_CHAN_LEN
)) {
489 ast_channel_unlock(this);
491 } else if (this == chan
) {
493 ast_channel_unlock(this);
497 return setup_chanspy_ds(this, chanspy_ds
);
500 static int common_exec(struct ast_channel
*chan
, const struct ast_flags
*flags
,
501 int volfactor
, const int fd
, const char *mygroup
, const char *spec
,
502 const char *exten
, const char *context
)
504 char nameprefix
[AST_NAME_STRLEN
];
505 char peer_name
[AST_NAME_STRLEN
+ 5];
506 signed char zero_volume
= 0;
511 int num_spyed_upon
= 1;
512 struct chanspy_ds chanspy_ds
;
514 ast_mutex_init(&chanspy_ds
.lock
);
516 snprintf(chanspy_ds
.unique_id
, sizeof(chanspy_ds
.unique_id
), "%d", ast_atomic_fetchadd_int(&next_unique_id_to_use
, +1));
518 if (chan
->_state
!= AST_STATE_UP
)
521 ast_set_flag(chan
, AST_FLAG_SPYING
); /* so nobody can spy on us while we are spying */
526 struct chanspy_ds
*peer_chanspy_ds
= NULL
, *next_chanspy_ds
= NULL
;
527 struct ast_channel
*prev
= NULL
, *peer
= NULL
;
529 if (!ast_test_flag(flags
, OPTION_QUIET
) && num_spyed_upon
) {
530 res
= ast_streamfile(chan
, "beep", chan
->language
);
532 res
= ast_waitstream(chan
, "");
534 ast_clear_flag(chan
, AST_FLAG_SPYING
);
539 res
= ast_waitfordigit(chan
, waitms
);
541 ast_clear_flag(chan
, AST_FLAG_SPYING
);
545 /* reset for the next loop around, unless overridden later */
549 for (peer_chanspy_ds
= next_channel(chan
, prev
, spec
, exten
, context
, &chanspy_ds
);
551 chanspy_ds_free(peer_chanspy_ds
), prev
= peer
,
552 peer_chanspy_ds
= next_chanspy_ds
? next_chanspy_ds
:
553 next_channel(chan
, prev
, spec
, exten
, context
, &chanspy_ds
), next_chanspy_ds
= NULL
) {
562 peer
= peer_chanspy_ds
->chan
;
564 ast_mutex_unlock(&peer_chanspy_ds
->lock
);
567 ast_channel_unlock(peer
);
568 chanspy_ds_free(peer_chanspy_ds
);
572 if (ast_check_hangup(chan
)) {
573 ast_channel_unlock(peer
);
574 chanspy_ds_free(peer_chanspy_ds
);
578 if (ast_test_flag(flags
, OPTION_BRIDGED
) && !ast_bridged_channel(peer
)) {
579 ast_channel_unlock(peer
);
583 if (ast_check_hangup(peer
) || ast_test_flag(peer
, AST_FLAG_SPYING
)) {
584 ast_channel_unlock(peer
);
589 if ((group
= pbx_builtin_getvar_helper(peer
, "SPYGROUP"))) {
590 ast_copy_string(dup_group
, group
, sizeof(dup_group
));
591 num_groups
= ast_app_separate_args(dup_group
, ':', groups
,
592 sizeof(groups
) / sizeof(groups
[0]));
595 for (x
= 0; x
< num_groups
; x
++) {
596 if (!strcmp(mygroup
, groups
[x
])) {
604 ast_channel_unlock(peer
);
608 strcpy(peer_name
, "spy-");
609 strncat(peer_name
, peer
->name
, AST_NAME_STRLEN
- 4 - 1);
610 ptr
= strchr(peer_name
, '/');
613 for (s
= peer_name
; s
< ptr
; s
++)
616 /* We have to unlock the peer channel here to avoid a deadlock.
617 * So, when we need to dereference it again, we have to lock the
618 * datastore and get the pointer from there to see if the channel
620 ast_channel_unlock(peer
);
622 if (!ast_test_flag(flags
, OPTION_QUIET
)) {
623 if (ast_fileexists(peer_name
, NULL
, NULL
) != -1) {
624 res
= ast_streamfile(chan
, peer_name
, chan
->language
);
626 res
= ast_waitstream(chan
, "");
628 chanspy_ds_free(peer_chanspy_ds
);
632 res
= ast_say_character_str(chan
, peer_name
, "", chan
->language
);
633 if ((num
= atoi(ptr
)))
634 ast_say_digits(chan
, atoi(ptr
), "", chan
->language
);
637 res
= channel_spy(chan
, peer_chanspy_ds
, &volfactor
, fd
, flags
);
641 chanspy_ds_free(peer_chanspy_ds
);
643 } else if (res
> 1 && spec
) {
644 struct ast_channel
*next
;
646 snprintf(nameprefix
, AST_NAME_STRLEN
, "%s/%d", spec
, res
);
648 if ((next
= ast_get_channel_by_name_prefix_locked(nameprefix
, strlen(nameprefix
)))) {
649 peer_chanspy_ds
= chanspy_ds_free(peer_chanspy_ds
);
650 next_chanspy_ds
= setup_chanspy_ds(next
, &chanspy_ds
);
652 /* stay on this channel, if it is still valid */
654 ast_mutex_lock(&peer_chanspy_ds
->lock
);
655 if (peer_chanspy_ds
->chan
) {
656 ast_channel_lock(peer_chanspy_ds
->chan
);
657 next_chanspy_ds
= peer_chanspy_ds
;
658 peer_chanspy_ds
= NULL
;
660 /* the channel is gone */
661 ast_mutex_unlock(&peer_chanspy_ds
->lock
);
662 next_chanspy_ds
= NULL
;
669 if (res
== -1 || ast_check_hangup(chan
))
673 ast_clear_flag(chan
, AST_FLAG_SPYING
);
675 ast_channel_setoption(chan
, AST_OPTION_TXGAIN
, &zero_volume
, sizeof(zero_volume
), 0);
677 ast_mutex_lock(&chanspy_ds
.lock
);
678 ast_mutex_unlock(&chanspy_ds
.lock
);
679 ast_mutex_destroy(&chanspy_ds
.lock
);
684 static int chanspy_exec(struct ast_channel
*chan
, void *data
)
686 struct ast_module_user
*u
;
687 char *options
= NULL
;
690 char *mygroup
= NULL
;
691 char *recbase
= NULL
;
693 struct ast_flags flags
;
699 data
= ast_strdupa(data
);
701 u
= ast_module_user_add(chan
);
703 if ((argc
= ast_app_separate_args(data
, '|', argv
, sizeof(argv
) / sizeof(argv
[0])))) {
708 if (ast_strlen_zero(spec
) || !strcmp(spec
, "all"))
713 char *opts
[OPT_ARG_ARRAY_SIZE
];
715 ast_app_parse_options(spy_opts
, &flags
, opts
, options
);
716 if (ast_test_flag(&flags
, OPTION_GROUP
))
717 mygroup
= opts
[OPT_ARG_GROUP
];
719 if (ast_test_flag(&flags
, OPTION_RECORD
) &&
720 !(recbase
= opts
[OPT_ARG_RECORD
]))
723 if (ast_test_flag(&flags
, OPTION_VOLUME
) && opts
[OPT_ARG_VOLUME
]) {
726 if ((sscanf(opts
[OPT_ARG_VOLUME
], "%d", &vol
) != 1) || (vol
> 4) || (vol
< -4))
727 ast_log(LOG_NOTICE
, "Volume factor must be a number between -4 and 4\n");
732 if (ast_test_flag(&flags
, OPTION_PRIVATE
))
733 ast_set_flag(&flags
, OPTION_WHISPER
);
735 ast_clear_flag(&flags
, AST_FLAGS_ALL
);
737 oldwf
= chan
->writeformat
;
738 if (ast_set_write_format(chan
, AST_FORMAT_SLINEAR
) < 0) {
739 ast_log(LOG_ERROR
, "Could Not Set Write Format.\n");
740 ast_module_user_remove(u
);
745 char filename
[PATH_MAX
];
747 snprintf(filename
, sizeof(filename
), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR
, recbase
, (int) time(NULL
));
748 if ((fd
= open(filename
, O_CREAT
| O_WRONLY
| O_TRUNC
, 0644)) <= 0) {
749 ast_log(LOG_WARNING
, "Cannot open '%s' for recording\n", filename
);
754 res
= common_exec(chan
, &flags
, volfactor
, fd
, mygroup
, spec
, NULL
, NULL
);
759 if (oldwf
&& ast_set_write_format(chan
, oldwf
) < 0)
760 ast_log(LOG_ERROR
, "Could Not Set Write Format.\n");
762 ast_module_user_remove(u
);
767 static int extenspy_exec(struct ast_channel
*chan
, void *data
)
769 struct ast_module_user
*u
;
770 char *options
= NULL
;
772 char *context
= NULL
;
774 char *mygroup
= NULL
;
775 char *recbase
= NULL
;
777 struct ast_flags flags
;
783 data
= ast_strdupa(data
);
785 u
= ast_module_user_add(chan
);
787 if ((argc
= ast_app_separate_args(data
, '|', argv
, sizeof(argv
) / sizeof(argv
[0])))) {
789 if (!ast_strlen_zero(argv
[0]))
790 exten
= strsep(&context
, "@");
791 if (ast_strlen_zero(context
))
792 context
= ast_strdupa(chan
->context
);
798 char *opts
[OPT_ARG_ARRAY_SIZE
];
800 ast_app_parse_options(spy_opts
, &flags
, opts
, options
);
801 if (ast_test_flag(&flags
, OPTION_GROUP
))
802 mygroup
= opts
[OPT_ARG_GROUP
];
804 if (ast_test_flag(&flags
, OPTION_RECORD
) &&
805 !(recbase
= opts
[OPT_ARG_RECORD
]))
808 if (ast_test_flag(&flags
, OPTION_VOLUME
) && opts
[OPT_ARG_VOLUME
]) {
811 if ((sscanf(opts
[OPT_ARG_VOLUME
], "%d", &vol
) != 1) || (vol
> 4) || (vol
< -4))
812 ast_log(LOG_NOTICE
, "Volume factor must be a number between -4 and 4\n");
817 if (ast_test_flag(&flags
, OPTION_PRIVATE
))
818 ast_set_flag(&flags
, OPTION_WHISPER
);
820 ast_clear_flag(&flags
, AST_FLAGS_ALL
);
822 oldwf
= chan
->writeformat
;
823 if (ast_set_write_format(chan
, AST_FORMAT_SLINEAR
) < 0) {
824 ast_log(LOG_ERROR
, "Could Not Set Write Format.\n");
825 ast_module_user_remove(u
);
830 char filename
[PATH_MAX
];
832 snprintf(filename
, sizeof(filename
), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR
, recbase
, (int) time(NULL
));
833 if ((fd
= open(filename
, O_CREAT
| O_WRONLY
| O_TRUNC
, 0644)) <= 0) {
834 ast_log(LOG_WARNING
, "Cannot open '%s' for recording\n", filename
);
839 res
= common_exec(chan
, &flags
, volfactor
, fd
, mygroup
, NULL
, exten
, context
);
844 if (oldwf
&& ast_set_write_format(chan
, oldwf
) < 0)
845 ast_log(LOG_ERROR
, "Could Not Set Write Format.\n");
847 ast_module_user_remove(u
);
852 static int unload_module(void)
856 res
|= ast_unregister_application(app_chan
);
857 res
|= ast_unregister_application(app_ext
);
859 ast_module_user_hangup_all();
864 static int load_module(void)
868 res
|= ast_register_application(app_chan
, chanspy_exec
, tdesc
, desc_chan
);
869 res
|= ast_register_application(app_ext
, extenspy_exec
, tdesc
, desc_ext
);
874 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY
, "Listen to the audio of an active channel");