ensure that the private structure for pseudo channels is created without 'leaking...
[asterisk-bristuff.git] / apps / app_chanspy.c
blob91e7b4ca7920004054c9f3f5fadc4187e333c2fb
1 /*
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
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>
26 * \author Joshua Colp <jcolp@digium.com>
27 * \author Russell Bryant <russell@digium.com>
29 * \ingroup applications
32 #include "asterisk.h"
34 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <unistd.h>
40 #include <ctype.h>
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"
75 " 'Agent/1234'.\n"
76 " Options:\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"
91 " channel.\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"
104 " Options:\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"
119 " channel.\n"
122 enum {
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 */
130 } chanspy_opt_flags;
132 enum {
133 OPT_ARG_VOLUME = 0,
134 OPT_ARG_GROUP,
135 OPT_ARG_RECORD,
136 OPT_ARG_ARRAY_SIZE,
137 } chanspy_opt_args;
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 {
152 /* spy data */
153 struct ast_audiohook spy_audiohook;
154 struct ast_audiohook whisper_audiohook;
155 int fd;
156 int volfactor;
159 static void *spy_alloc(struct ast_channel *chan, void *data)
161 /* just store the data pointer in the channel structure */
162 return data;
165 static void spy_release(struct ast_channel *chan, void *data)
167 /* nothing to do */
170 static int spy_generate(struct ast_channel *chan, void *data, int len, int samples)
172 struct chanspy_translation_helper *csth = data;
173 struct ast_frame *f;
175 ast_audiohook_lock(&csth->spy_audiohook);
176 if (csth->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
177 ast_audiohook_unlock(&csth->spy_audiohook);
178 return -1;
181 f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR);
183 ast_audiohook_unlock(&csth->spy_audiohook);
185 if (!f)
186 return 0;
188 if (ast_write(chan, f)) {
189 ast_frfree(f);
190 return -1;
193 if (csth->fd)
194 write(csth->fd, f->data, f->datalen);
196 ast_frfree(f);
198 return 0;
201 static struct ast_generator spygen = {
202 .alloc = spy_alloc,
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)
209 int res;
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);
219 return res;
222 struct chanspy_ds {
223 struct ast_channel *chan;
224 char unique_id[20];
225 ast_mutex_t lock;
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;
233 char inp[24] = {0};
234 char *name;
235 struct ast_frame *f;
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);
251 if (!spyee)
252 return 0;
254 /* We now hold the channel lock on spyee */
256 if (ast_check_hangup(chan) || ast_check_hangup(spyee)) {
257 ast_channel_unlock(spyee);
258 return 0;
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);
272 return 0;
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);
281 spyee = NULL;
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;
290 csth.fd = fd;
292 if (ast_test_flag(flags, OPTION_PRIVATE))
293 silgen = ast_channel_start_silence_generator(chan);
294 else
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
309 we were waiting
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)) {
313 running = -1;
314 break;
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);
321 ast_frfree(f);
322 continue;
325 res = (f->frametype == AST_FRAME_DTMF) ? f->subclass : 0;
326 ast_frfree(f);
327 if (!res)
328 continue;
330 if (x == sizeof(inp))
331 x = 0;
333 if (res < 0) {
334 running = -1;
335 break;
338 if (res == '*') {
339 running = 0;
340 break;
341 } else if (res == '#') {
342 if (!ast_strlen_zero(inp)) {
343 running = atoi(inp);
344 break;
347 (*volfactor)++;
348 if (*volfactor > 4)
349 *volfactor = -4;
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') {
356 inp[x++] = res;
360 if (ast_test_flag(flags, OPTION_PRIVATE))
361 ast_channel_stop_silence_generator(chan, silgen);
362 else
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);
380 return running;
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 = {
410 .type = "chanspy",
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)
417 if (!chanspy_ds)
418 return NULL;
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);
439 return NULL;
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);
453 return NULL;
456 chanspy_ds->chan = chan;
457 datastore->data = chanspy_ds;
458 ast_channel_datastore_add(chan, datastore);
460 return chanspy_ds;
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");
475 redo:
476 if (spec)
477 this = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec));
478 else if (exten)
479 this = ast_walk_channel_by_exten_locked(last, exten, context);
480 else
481 this = ast_channel_walk_locked(last);
483 if (!this)
484 return NULL;
486 snprintf(channel_name, AST_CHANNEL_NAME, "%s/pseudo", dahdi_chan_name);
487 if (!strncmp(this->name, channel_name, PSEUDO_CHAN_LEN)) {
488 last = this;
489 ast_channel_unlock(this);
490 goto redo;
491 } else if (this == chan) {
492 last = this;
493 ast_channel_unlock(this);
494 goto redo;
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;
507 int waitms;
508 int res;
509 char *ptr;
510 int num;
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)
519 ast_answer(chan);
521 ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */
523 waitms = 100;
525 for (;;) {
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);
531 if (!res)
532 res = ast_waitstream(chan, "");
533 else if (res < 0) {
534 ast_clear_flag(chan, AST_FLAG_SPYING);
535 break;
539 res = ast_waitfordigit(chan, waitms);
540 if (res < 0) {
541 ast_clear_flag(chan, AST_FLAG_SPYING);
542 break;
545 /* reset for the next loop around, unless overridden later */
546 waitms = 100;
547 num_spyed_upon = 0;
549 for (peer_chanspy_ds = next_channel(chan, prev, spec, exten, context, &chanspy_ds);
550 peer_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) {
554 const char *group;
555 int igrp = !mygroup;
556 char *groups[25];
557 int num_groups = 0;
558 char dup_group[512];
559 int x;
560 char *s;
562 peer = peer_chanspy_ds->chan;
564 ast_mutex_unlock(&peer_chanspy_ds->lock);
566 if (peer == prev) {
567 ast_channel_unlock(peer);
568 chanspy_ds_free(peer_chanspy_ds);
569 break;
572 if (ast_check_hangup(chan)) {
573 ast_channel_unlock(peer);
574 chanspy_ds_free(peer_chanspy_ds);
575 break;
578 if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer)) {
579 ast_channel_unlock(peer);
580 continue;
583 if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING)) {
584 ast_channel_unlock(peer);
585 continue;
588 if (mygroup) {
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])) {
597 igrp = 1;
598 break;
603 if (!igrp) {
604 ast_channel_unlock(peer);
605 continue;
608 strcpy(peer_name, "spy-");
609 strncat(peer_name, peer->name, AST_NAME_STRLEN - 4 - 1);
610 ptr = strchr(peer_name, '/');
611 *ptr++ = '\0';
613 for (s = peer_name; s < ptr; s++)
614 *s = tolower(*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
619 * is still valid. */
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);
625 if (!res)
626 res = ast_waitstream(chan, "");
627 if (res) {
628 chanspy_ds_free(peer_chanspy_ds);
629 break;
631 } else
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);
638 num_spyed_upon++;
640 if (res == -1) {
641 chanspy_ds_free(peer_chanspy_ds);
642 break;
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);
651 } else {
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;
659 } else {
660 /* the channel is gone */
661 ast_mutex_unlock(&peer_chanspy_ds->lock);
662 next_chanspy_ds = NULL;
666 peer = NULL;
669 if (res == -1 || ast_check_hangup(chan))
670 break;
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);
681 return res;
684 static int chanspy_exec(struct ast_channel *chan, void *data)
686 struct ast_module_user *u;
687 char *options = NULL;
688 char *spec = NULL;
689 char *argv[2];
690 char *mygroup = NULL;
691 char *recbase = NULL;
692 int fd = 0;
693 struct ast_flags flags;
694 int oldwf = 0;
695 int argc = 0;
696 int volfactor = 0;
697 int res;
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])))) {
704 spec = argv[0];
705 if (argc > 1)
706 options = argv[1];
708 if (ast_strlen_zero(spec) || !strcmp(spec, "all"))
709 spec = NULL;
712 if (options) {
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]))
721 recbase = "chanspy";
723 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
724 int vol;
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");
728 else
729 volfactor = vol;
732 if (ast_test_flag(&flags, OPTION_PRIVATE))
733 ast_set_flag(&flags, OPTION_WHISPER);
734 } else
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);
741 return -1;
744 if (recbase) {
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);
750 fd = 0;
754 res = common_exec(chan, &flags, volfactor, fd, mygroup, spec, NULL, NULL);
756 if (fd)
757 close(fd);
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);
764 return res;
767 static int extenspy_exec(struct ast_channel *chan, void *data)
769 struct ast_module_user *u;
770 char *options = NULL;
771 char *exten = NULL;
772 char *context = NULL;
773 char *argv[2];
774 char *mygroup = NULL;
775 char *recbase = NULL;
776 int fd = 0;
777 struct ast_flags flags;
778 int oldwf = 0;
779 int argc = 0;
780 int volfactor = 0;
781 int res;
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])))) {
788 context = 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);
793 if (argc > 1)
794 options = argv[1];
797 if (options) {
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]))
806 recbase = "chanspy";
808 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
809 int vol;
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");
813 else
814 volfactor = vol;
817 if (ast_test_flag(&flags, OPTION_PRIVATE))
818 ast_set_flag(&flags, OPTION_WHISPER);
819 } else
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);
826 return -1;
829 if (recbase) {
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);
835 fd = 0;
839 res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, exten, context);
841 if (fd)
842 close(fd);
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);
849 return res;
852 static int unload_module(void)
854 int res = 0;
856 res |= ast_unregister_application(app_chan);
857 res |= ast_unregister_application(app_ext);
859 ast_module_user_hangup_all();
861 return res;
864 static int load_module(void)
866 int res = 0;
868 res |= ast_register_application(app_chan, chanspy_exec, tdesc, desc_chan);
869 res |= ast_register_application(app_ext, extenspy_exec, tdesc, desc_ext);
871 return res;
874 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Listen to the audio of an active channel");