#include "asterisk.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <errno.h>
#include "asterisk/file.h"
#include "asterisk/logger.h"
#include "asterisk/channel.h"
#include "asterisk/audiohook.h"
#include "asterisk/features.h"
#include "asterisk/options.h"
#include "asterisk/app.h"
#include "asterisk/utils.h"
#include "asterisk/say.h"
#include "asterisk/pbx.h"
#include "asterisk/translate.h"
#include "asterisk/module.h"
#include "asterisk/lock.h"
Go to the source code of this file.
Data Structures | |
struct | chanspy_ds |
struct | chanspy_translation_helper |
Defines | |
#define | AST_NAME_STRLEN 256 |
Enumerations | |
enum | { OPTION_QUIET = (1 << 0), OPTION_BRIDGED = (1 << 1), OPTION_VOLUME = (1 << 2), OPTION_GROUP = (1 << 3), OPTION_RECORD = (1 << 4), OPTION_WHISPER = (1 << 5), OPTION_PRIVATE = (1 << 6) } |
enum | { OPT_ARG_VOLUME = 0, OPT_ARG_GROUP, OPT_ARG_RECORD, OPT_ARG_ARRAY_SIZE } |
Functions | |
static void | __reg_module (void) |
static void | __unreg_module (void) |
static int | channel_spy (struct ast_channel *chan, struct chanspy_ds *spyee_chanspy_ds, int *volfactor, int fd, const struct ast_flags *flags) |
static void | chanspy_ds_chan_fixup (void *data, struct ast_channel *old_chan, struct ast_channel *new_chan) |
static void | chanspy_ds_destroy (void *data) |
static struct chanspy_ds * | chanspy_ds_free (struct chanspy_ds *chanspy_ds) |
static int | chanspy_exec (struct ast_channel *chan, void *data) |
static int | common_exec (struct ast_channel *chan, const struct ast_flags *flags, int volfactor, const int fd, const char *mygroup, const char *spec, const char *exten, const char *context) |
static int | extenspy_exec (struct ast_channel *chan, void *data) |
static int | load_module (void) |
static struct chanspy_ds * | next_channel (struct ast_channel *chan, const struct ast_channel *last, const char *spec, const char *exten, const char *context, struct chanspy_ds *chanspy_ds) |
static struct chanspy_ds * | setup_chanspy_ds (struct ast_channel *chan, struct chanspy_ds *chanspy_ds) |
static void * | spy_alloc (struct ast_channel *chan, void *data) |
static int | spy_generate (struct ast_channel *chan, void *data, int len, int samples) |
static void | spy_release (struct ast_channel *chan, void *data) |
static int | start_spying (struct ast_channel *chan, const char *spychan_name, struct ast_audiohook *audiohook) |
static int | unload_module (void) |
Variables | |
static struct ast_module_info | __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT | AST_MODFLAG_BUILDSUM, .description = "Listen to the audio of an active channel" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "361d7bb937402d51e4658efb5b4d76e4" , .load = load_module, .unload = unload_module, } |
static const char * | app_chan = "ChanSpy" |
static const char * | app_ext = "ExtenSpy" |
static const struct ast_module_info * | ast_module_info = &__mod_info |
static struct ast_datastore_info | chanspy_ds_info |
enum { ... } | chanspy_opt_args |
enum { ... } | chanspy_opt_flags |
static const char * | desc_chan |
static const char * | desc_ext |
static int | next_unique_id_to_use = 0 |
static struct ast_app_option | spy_opts [128] = { [ 'q' ] = { .flag = OPTION_QUIET }, [ 'b' ] = { .flag = OPTION_BRIDGED }, [ 'w' ] = { .flag = OPTION_WHISPER }, [ 'W' ] = { .flag = OPTION_PRIVATE }, [ 'v' ] = { .flag = OPTION_VOLUME , .arg_index = OPT_ARG_VOLUME + 1 }, [ 'g' ] = { .flag = OPTION_GROUP , .arg_index = OPT_ARG_GROUP + 1 }, [ 'r' ] = { .flag = OPTION_RECORD , .arg_index = OPT_ARG_RECORD + 1 },} |
static struct ast_generator | spygen |
static const char * | tdesc = "Listen to a channel, and optionally whisper into it" |
Joshua Colp <jcolp@digium.com>
Russell Bryant <russell@digium.com>
Definition in file app_chanspy.c.
#define AST_NAME_STRLEN 256 |
anonymous enum |
OPTION_QUIET | |
OPTION_BRIDGED | |
OPTION_VOLUME | |
OPTION_GROUP | |
OPTION_RECORD | |
OPTION_WHISPER | |
OPTION_PRIVATE |
Definition at line 123 of file app_chanspy.c.
00123 { 00124 OPTION_QUIET = (1 << 0), /* Quiet, no announcement */ 00125 OPTION_BRIDGED = (1 << 1), /* Only look at bridged calls */ 00126 OPTION_VOLUME = (1 << 2), /* Specify initial volume */ 00127 OPTION_GROUP = (1 << 3), /* Only look at channels in group */ 00128 OPTION_RECORD = (1 << 4), 00129 OPTION_WHISPER = (1 << 5), 00130 OPTION_PRIVATE = (1 << 6), /* Private Whisper mode */ 00131 } chanspy_opt_flags;
anonymous enum |
Definition at line 133 of file app_chanspy.c.
00133 { 00134 OPT_ARG_VOLUME = 0, 00135 OPT_ARG_GROUP, 00136 OPT_ARG_RECORD, 00137 OPT_ARG_ARRAY_SIZE, 00138 } chanspy_opt_args;
static void __reg_module | ( | void | ) | [static] |
Definition at line 887 of file app_chanspy.c.
static void __unreg_module | ( | void | ) | [static] |
Definition at line 887 of file app_chanspy.c.
static int channel_spy | ( | struct ast_channel * | chan, | |
struct chanspy_ds * | spyee_chanspy_ds, | |||
int * | volfactor, | |||
int | fd, | |||
const struct ast_flags * | flags | |||
) | [static] |
Definition at line 236 of file app_chanspy.c.
References ast_activate_generator(), ast_audiohook_destroy(), ast_audiohook_detach(), AST_AUDIOHOOK_DIRECTION_WRITE, ast_audiohook_init(), ast_audiohook_lock, AST_AUDIOHOOK_STATUS_RUNNING, AST_AUDIOHOOK_TYPE_SPY, AST_AUDIOHOOK_TYPE_WHISPER, ast_audiohook_unlock, ast_audiohook_write_frame(), ast_channel_lock, ast_channel_start_silence_generator(), ast_channel_stop_silence_generator(), ast_channel_trylock, ast_channel_unlock, ast_check_hangup(), ast_deactivate_generator(), AST_FRAME_DTMF, AST_FRAME_VOICE, ast_frfree, ast_mutex_lock(), ast_mutex_unlock(), ast_read(), ast_strdupa, ast_strlen_zero(), ast_test_flag, ast_verbose(), ast_waitfor(), chanspy_ds::chan, DEADLOCK_AVOIDANCE, f, ast_channel::flags, chanspy_ds::lock, ast_channel::name, name, OPTION_PRIVATE, option_verbose, OPTION_WHISPER, spygen, start_spying(), VERBOSE_PREFIX_2, and VERBOSE_PREFIX_3.
Referenced by common_exec().
00238 { 00239 struct chanspy_translation_helper csth; 00240 int running = 0, res, x = 0; 00241 char inp[24] = {0}; 00242 char *name; 00243 struct ast_frame *f; 00244 struct ast_silence_generator *silgen = NULL; 00245 struct ast_channel *spyee = NULL; 00246 const char *spyer_name; 00247 00248 ast_channel_lock(chan); 00249 spyer_name = ast_strdupa(chan->name); 00250 ast_channel_unlock(chan); 00251 00252 ast_mutex_lock(&spyee_chanspy_ds->lock); 00253 while ((spyee = spyee_chanspy_ds->chan) && ast_channel_trylock(spyee)) { 00254 /* avoid a deadlock here, just in case spyee is masqueraded and 00255 * chanspy_ds_chan_fixup() is called with the channel locked */ 00256 DEADLOCK_AVOIDANCE(&spyee_chanspy_ds->lock); 00257 } 00258 ast_mutex_unlock(&spyee_chanspy_ds->lock); 00259 00260 if (!spyee) 00261 return 0; 00262 00263 /* We now hold the channel lock on spyee */ 00264 00265 if (ast_check_hangup(chan) || ast_check_hangup(spyee)) { 00266 ast_channel_unlock(spyee); 00267 return 0; 00268 } 00269 00270 name = ast_strdupa(spyee->name); 00271 if (option_verbose >= 2) 00272 ast_verbose(VERBOSE_PREFIX_2 "Spying on channel %s\n", name); 00273 00274 memset(&csth, 0, sizeof(csth)); 00275 00276 ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy"); 00277 00278 if (start_spying(spyee, spyer_name, &csth.spy_audiohook)) { 00279 ast_audiohook_destroy(&csth.spy_audiohook); 00280 ast_channel_unlock(spyee); 00281 return 0; 00282 } 00283 00284 if (ast_test_flag(flags, OPTION_WHISPER)) { 00285 ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy"); 00286 start_spying(spyee, spyer_name, &csth.whisper_audiohook); 00287 } 00288 00289 ast_channel_unlock(spyee); 00290 spyee = NULL; 00291 00292 csth.volfactor = *volfactor; 00293 00294 if (csth.volfactor) { 00295 csth.spy_audiohook.options.read_volume = csth.volfactor; 00296 csth.spy_audiohook.options.write_volume = csth.volfactor; 00297 } 00298 00299 csth.fd = fd; 00300 00301 if (ast_test_flag(flags, OPTION_PRIVATE)) 00302 silgen = ast_channel_start_silence_generator(chan); 00303 else 00304 ast_activate_generator(chan, &spygen, &csth); 00305 00306 /* We can no longer rely on 'spyee' being an actual channel; 00307 it can be hung up and freed out from under us. However, the 00308 channel destructor will put NULL into our csth.spy.chan 00309 field when that happens, so that is our signal that the spyee 00310 channel has gone away. 00311 */ 00312 00313 /* Note: it is very important that the ast_waitfor() be the first 00314 condition in this expression, so that if we wait for some period 00315 of time before receiving a frame from our spying channel, we check 00316 for hangup on the spied-on channel _after_ knowing that a frame 00317 has arrived, since the spied-on channel could have gone away while 00318 we were waiting 00319 */ 00320 while ((res = ast_waitfor(chan, -1) > -1) && csth.spy_audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) { 00321 if (!(f = ast_read(chan)) || ast_check_hangup(chan)) { 00322 running = -1; 00323 break; 00324 } 00325 00326 if (ast_test_flag(flags, OPTION_WHISPER) && (f->frametype == AST_FRAME_VOICE)) { 00327 ast_audiohook_lock(&csth.whisper_audiohook); 00328 ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f); 00329 ast_audiohook_unlock(&csth.whisper_audiohook); 00330 ast_frfree(f); 00331 continue; 00332 } 00333 00334 res = (f->frametype == AST_FRAME_DTMF) ? f->subclass : 0; 00335 ast_frfree(f); 00336 if (!res) 00337 continue; 00338 00339 if (x == sizeof(inp)) 00340 x = 0; 00341 00342 if (res < 0) { 00343 running = -1; 00344 break; 00345 } 00346 00347 if (res == '*') { 00348 running = 0; 00349 break; 00350 } else if (res == '#') { 00351 if (!ast_strlen_zero(inp)) { 00352 running = atoi(inp); 00353 break; 00354 } 00355 00356 (*volfactor)++; 00357 if (*volfactor > 4) 00358 *volfactor = -4; 00359 if (option_verbose > 2) 00360 ast_verbose(VERBOSE_PREFIX_3 "Setting spy volume on %s to %d\n", chan->name, *volfactor); 00361 csth.volfactor = *volfactor; 00362 csth.spy_audiohook.options.read_volume = csth.volfactor; 00363 csth.spy_audiohook.options.write_volume = csth.volfactor; 00364 } else if (res >= '0' && res <= '9') { 00365 inp[x++] = res; 00366 } 00367 } 00368 00369 if (ast_test_flag(flags, OPTION_PRIVATE)) 00370 ast_channel_stop_silence_generator(chan, silgen); 00371 else 00372 ast_deactivate_generator(chan); 00373 00374 if (ast_test_flag(flags, OPTION_WHISPER)) { 00375 ast_audiohook_lock(&csth.whisper_audiohook); 00376 ast_audiohook_detach(&csth.whisper_audiohook); 00377 ast_audiohook_unlock(&csth.whisper_audiohook); 00378 ast_audiohook_destroy(&csth.whisper_audiohook); 00379 } 00380 00381 ast_audiohook_lock(&csth.spy_audiohook); 00382 ast_audiohook_detach(&csth.spy_audiohook); 00383 ast_audiohook_unlock(&csth.spy_audiohook); 00384 ast_audiohook_destroy(&csth.spy_audiohook); 00385 00386 if (option_verbose >= 2) 00387 ast_verbose(VERBOSE_PREFIX_2 "Done Spying on channel %s\n", name); 00388 00389 return running; 00390 }
static void chanspy_ds_chan_fixup | ( | void * | data, | |
struct ast_channel * | old_chan, | |||
struct ast_channel * | new_chan | |||
) | [static] |
Definition at line 409 of file app_chanspy.c.
References ast_mutex_lock(), ast_mutex_unlock(), chanspy_ds::chan, and chanspy_ds::lock.
00410 { 00411 struct chanspy_ds *chanspy_ds = data; 00412 00413 ast_mutex_lock(&chanspy_ds->lock); 00414 chanspy_ds->chan = new_chan; 00415 ast_mutex_unlock(&chanspy_ds->lock); 00416 }
static void chanspy_ds_destroy | ( | void * | data | ) | [static] |
Definition at line 396 of file app_chanspy.c.
References ast_mutex_lock(), ast_mutex_unlock(), chanspy_ds::chan, and chanspy_ds::lock.
Referenced by chanspy_ds_free().
00397 { 00398 struct chanspy_ds *chanspy_ds = data; 00399 00400 /* Setting chan to be NULL is an atomic operation, but we don't want this 00401 * value to change while this lock is held. The lock is held elsewhere 00402 * while it performs non-atomic operations with this channel pointer */ 00403 00404 ast_mutex_lock(&chanspy_ds->lock); 00405 chanspy_ds->chan = NULL; 00406 ast_mutex_unlock(&chanspy_ds->lock); 00407 }
static struct chanspy_ds* chanspy_ds_free | ( | struct chanspy_ds * | chanspy_ds | ) | [static] |
Definition at line 424 of file app_chanspy.c.
References ast_channel_datastore_find(), ast_channel_datastore_free(), ast_channel_datastore_remove(), ast_channel_trylock, ast_channel_unlock, ast_mutex_lock(), ast_mutex_unlock(), chanspy_ds::chan, chanspy_ds_destroy(), chanspy_ds_info, ast_datastore::data, DEADLOCK_AVOIDANCE, chanspy_ds::lock, and chanspy_ds::unique_id.
Referenced by common_exec(), and setup_chanspy_ds().
00425 { 00426 struct ast_channel *chan; 00427 00428 if (!chanspy_ds) { 00429 return NULL; 00430 } 00431 00432 ast_mutex_lock(&chanspy_ds->lock); 00433 while ((chan = chanspy_ds->chan)) { 00434 struct ast_datastore *datastore; 00435 00436 if (ast_channel_trylock(chan)) { 00437 DEADLOCK_AVOIDANCE(&chanspy_ds->lock); 00438 continue; 00439 } 00440 if ((datastore = ast_channel_datastore_find(chan, &chanspy_ds_info, chanspy_ds->unique_id))) { 00441 ast_channel_datastore_remove(chan, datastore); 00442 /* chanspy_ds->chan is NULL after this call */ 00443 chanspy_ds_destroy(datastore->data); 00444 datastore->data = NULL; 00445 ast_channel_datastore_free(datastore); 00446 } 00447 ast_channel_unlock(chan); 00448 break; 00449 } 00450 ast_mutex_unlock(&chanspy_ds->lock); 00451 00452 return NULL; 00453 }
static int chanspy_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 697 of file app_chanspy.c.
References ast_app_parse_options(), ast_app_separate_args(), ast_clear_flag, ast_config_AST_MONITOR_DIR, AST_FLAGS_ALL, AST_FORMAT_SLINEAR, ast_log(), ast_module_user_add, ast_module_user_remove, ast_set_flag, ast_set_write_format(), ast_strdupa, ast_strlen_zero(), ast_test_flag, common_exec(), ast_flags::flags, LOG_ERROR, LOG_NOTICE, LOG_WARNING, OPT_ARG_ARRAY_SIZE, OPT_ARG_GROUP, OPT_ARG_RECORD, OPT_ARG_VOLUME, OPTION_GROUP, OPTION_PRIVATE, OPTION_RECORD, OPTION_VOLUME, OPTION_WHISPER, spy_opts, and ast_channel::writeformat.
Referenced by load_module().
00698 { 00699 struct ast_module_user *u; 00700 char *options = NULL; 00701 char *spec = NULL; 00702 char *argv[2]; 00703 char *mygroup = NULL; 00704 char *recbase = NULL; 00705 int fd = 0; 00706 struct ast_flags flags; 00707 int oldwf = 0; 00708 int argc = 0; 00709 int volfactor = 0; 00710 int res; 00711 00712 data = ast_strdupa(data); 00713 00714 u = ast_module_user_add(chan); 00715 00716 if ((argc = ast_app_separate_args(data, '|', argv, sizeof(argv) / sizeof(argv[0])))) { 00717 spec = argv[0]; 00718 if (argc > 1) 00719 options = argv[1]; 00720 00721 if (ast_strlen_zero(spec) || !strcmp(spec, "all")) 00722 spec = NULL; 00723 } 00724 00725 if (options) { 00726 char *opts[OPT_ARG_ARRAY_SIZE]; 00727 00728 ast_app_parse_options(spy_opts, &flags, opts, options); 00729 if (ast_test_flag(&flags, OPTION_GROUP)) 00730 mygroup = opts[OPT_ARG_GROUP]; 00731 00732 if (ast_test_flag(&flags, OPTION_RECORD) && 00733 !(recbase = opts[OPT_ARG_RECORD])) 00734 recbase = "chanspy"; 00735 00736 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) { 00737 int vol; 00738 00739 if ((sscanf(opts[OPT_ARG_VOLUME], "%30d", &vol) != 1) || (vol > 4) || (vol < -4)) 00740 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n"); 00741 else 00742 volfactor = vol; 00743 } 00744 00745 if (ast_test_flag(&flags, OPTION_PRIVATE)) 00746 ast_set_flag(&flags, OPTION_WHISPER); 00747 } else 00748 ast_clear_flag(&flags, AST_FLAGS_ALL); 00749 00750 oldwf = chan->writeformat; 00751 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) { 00752 ast_log(LOG_ERROR, "Could Not Set Write Format.\n"); 00753 ast_module_user_remove(u); 00754 return -1; 00755 } 00756 00757 if (recbase) { 00758 char filename[PATH_MAX]; 00759 00760 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL)); 00761 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644)) <= 0) { 00762 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename); 00763 fd = 0; 00764 } 00765 } 00766 00767 res = common_exec(chan, &flags, volfactor, fd, mygroup, spec, NULL, NULL); 00768 00769 if (fd) 00770 close(fd); 00771 00772 if (oldwf && ast_set_write_format(chan, oldwf) < 0) 00773 ast_log(LOG_ERROR, "Could Not Set Write Format.\n"); 00774 00775 ast_module_user_remove(u); 00776 00777 return res; 00778 }
static int common_exec | ( | struct ast_channel * | chan, | |
const struct ast_flags * | flags, | |||
int | volfactor, | |||
const int | fd, | |||
const char * | mygroup, | |||
const char * | spec, | |||
const char * | exten, | |||
const char * | context | |||
) | [static] |
Definition at line 513 of file app_chanspy.c.
References ast_channel::_state, ast_answer(), ast_app_separate_args(), ast_atomic_fetchadd_int(), ast_bridged_channel(), ast_channel_lock, ast_channel_setoption(), ast_channel_unlock, ast_check_hangup(), ast_clear_flag, ast_copy_string(), ast_fileexists(), AST_FLAG_SPYING, ast_get_channel_by_name_prefix_locked(), ast_mutex_destroy(), ast_mutex_init(), ast_mutex_lock(), ast_mutex_unlock(), AST_NAME_STRLEN, AST_OPTION_TXGAIN, ast_say_character_str(), ast_say_digits(), ast_set_flag, AST_STATE_UP, ast_streamfile(), ast_test_flag, ast_waitfordigit(), ast_waitstream(), chanspy_ds::chan, channel_spy(), chanspy_ds_free(), ast_channel::flags, group, chanspy_ds::lock, ast_channel::next, next_channel(), OPTION_BRIDGED, OPTION_QUIET, pbx_builtin_getvar_helper(), s, setup_chanspy_ds(), and chanspy_ds::unique_id.
Referenced by chanspy_exec(), and extenspy_exec().
00516 { 00517 char nameprefix[AST_NAME_STRLEN]; 00518 char peer_name[AST_NAME_STRLEN + 5]; 00519 signed char zero_volume = 0; 00520 int waitms; 00521 int res; 00522 char *ptr; 00523 int num; 00524 int num_spyed_upon = 1; 00525 struct chanspy_ds chanspy_ds = { 0, }; 00526 00527 ast_mutex_init(&chanspy_ds.lock); 00528 00529 snprintf(chanspy_ds.unique_id, sizeof(chanspy_ds.unique_id), "%d", ast_atomic_fetchadd_int(&next_unique_id_to_use, +1)); 00530 00531 if (chan->_state != AST_STATE_UP) 00532 ast_answer(chan); 00533 00534 ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */ 00535 00536 waitms = 100; 00537 00538 for (;;) { 00539 struct chanspy_ds *peer_chanspy_ds = NULL, *next_chanspy_ds = NULL; 00540 struct ast_channel *prev = NULL, *peer = NULL; 00541 00542 if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) { 00543 res = ast_streamfile(chan, "beep", chan->language); 00544 if (!res) 00545 res = ast_waitstream(chan, ""); 00546 else if (res < 0) { 00547 ast_clear_flag(chan, AST_FLAG_SPYING); 00548 break; 00549 } 00550 } 00551 00552 res = ast_waitfordigit(chan, waitms); 00553 if (res < 0) { 00554 ast_clear_flag(chan, AST_FLAG_SPYING); 00555 break; 00556 } 00557 00558 /* reset for the next loop around, unless overridden later */ 00559 waitms = 100; 00560 num_spyed_upon = 0; 00561 00562 for (peer_chanspy_ds = next_channel(chan, prev, spec, exten, context, &chanspy_ds); 00563 peer_chanspy_ds; 00564 chanspy_ds_free(peer_chanspy_ds), prev = peer, 00565 peer_chanspy_ds = next_chanspy_ds ? next_chanspy_ds : 00566 next_channel(chan, prev, spec, exten, context, &chanspy_ds), next_chanspy_ds = NULL) { 00567 const char *group; 00568 int igrp = !mygroup; 00569 char *groups[25]; 00570 int num_groups = 0; 00571 char dup_group[512]; 00572 int x; 00573 char *s; 00574 00575 peer = peer_chanspy_ds->chan; 00576 00577 ast_mutex_unlock(&peer_chanspy_ds->lock); 00578 00579 if (peer == prev) { 00580 ast_channel_unlock(peer); 00581 chanspy_ds_free(peer_chanspy_ds); 00582 break; 00583 } 00584 00585 if (ast_check_hangup(chan)) { 00586 ast_channel_unlock(peer); 00587 chanspy_ds_free(peer_chanspy_ds); 00588 break; 00589 } 00590 00591 if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer)) { 00592 ast_channel_unlock(peer); 00593 continue; 00594 } 00595 00596 if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING)) { 00597 ast_channel_unlock(peer); 00598 continue; 00599 } 00600 00601 if (mygroup) { 00602 if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) { 00603 ast_copy_string(dup_group, group, sizeof(dup_group)); 00604 num_groups = ast_app_separate_args(dup_group, ':', groups, 00605 sizeof(groups) / sizeof(groups[0])); 00606 } 00607 00608 for (x = 0; x < num_groups; x++) { 00609 if (!strcmp(mygroup, groups[x])) { 00610 igrp = 1; 00611 break; 00612 } 00613 } 00614 } 00615 00616 if (!igrp) { 00617 ast_channel_unlock(peer); 00618 continue; 00619 } 00620 00621 strcpy(peer_name, "spy-"); 00622 strncat(peer_name, peer->name, AST_NAME_STRLEN - 4 - 1); 00623 ptr = strchr(peer_name, '/'); 00624 *ptr++ = '\0'; 00625 00626 for (s = peer_name; s < ptr; s++) 00627 *s = tolower(*s); 00628 00629 /* We have to unlock the peer channel here to avoid a deadlock. 00630 * So, when we need to dereference it again, we have to lock the 00631 * datastore and get the pointer from there to see if the channel 00632 * is still valid. */ 00633 ast_channel_unlock(peer); 00634 00635 if (!ast_test_flag(flags, OPTION_QUIET)) { 00636 if (ast_fileexists(peer_name, NULL, NULL) > 0) { 00637 res = ast_streamfile(chan, peer_name, chan->language); 00638 if (!res) 00639 res = ast_waitstream(chan, ""); 00640 if (res) { 00641 chanspy_ds_free(peer_chanspy_ds); 00642 break; 00643 } 00644 } else 00645 res = ast_say_character_str(chan, peer_name, "", chan->language); 00646 if ((num = atoi(ptr))) 00647 ast_say_digits(chan, atoi(ptr), "", chan->language); 00648 } 00649 00650 res = channel_spy(chan, peer_chanspy_ds, &volfactor, fd, flags); 00651 num_spyed_upon++; 00652 00653 if (res == -1) { 00654 chanspy_ds_free(peer_chanspy_ds); 00655 break; 00656 } else if (res > 1 && spec) { 00657 struct ast_channel *next; 00658 00659 snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res); 00660 00661 if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) { 00662 peer_chanspy_ds = chanspy_ds_free(peer_chanspy_ds); 00663 next_chanspy_ds = setup_chanspy_ds(next, &chanspy_ds); 00664 } else { 00665 /* stay on this channel, if it is still valid */ 00666 00667 ast_mutex_lock(&peer_chanspy_ds->lock); 00668 if (peer_chanspy_ds->chan) { 00669 ast_channel_lock(peer_chanspy_ds->chan); 00670 next_chanspy_ds = peer_chanspy_ds; 00671 peer_chanspy_ds = NULL; 00672 } else { 00673 /* the channel is gone */ 00674 ast_mutex_unlock(&peer_chanspy_ds->lock); 00675 next_chanspy_ds = NULL; 00676 } 00677 } 00678 00679 peer = NULL; 00680 } 00681 } 00682 if (res == -1 || ast_check_hangup(chan)) 00683 break; 00684 } 00685 00686 ast_clear_flag(chan, AST_FLAG_SPYING); 00687 00688 ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0); 00689 00690 ast_mutex_lock(&chanspy_ds.lock); 00691 ast_mutex_unlock(&chanspy_ds.lock); 00692 ast_mutex_destroy(&chanspy_ds.lock); 00693 00694 return res; 00695 }
static int extenspy_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 780 of file app_chanspy.c.
References ast_app_parse_options(), ast_app_separate_args(), ast_clear_flag, ast_config_AST_MONITOR_DIR, AST_FLAGS_ALL, AST_FORMAT_SLINEAR, ast_log(), ast_module_user_add, ast_module_user_remove, ast_set_flag, ast_set_write_format(), ast_strdupa, ast_strlen_zero(), ast_test_flag, common_exec(), ast_channel::context, context, exten, ast_flags::flags, LOG_ERROR, LOG_NOTICE, LOG_WARNING, OPT_ARG_ARRAY_SIZE, OPT_ARG_GROUP, OPT_ARG_RECORD, OPT_ARG_VOLUME, OPTION_GROUP, OPTION_PRIVATE, OPTION_RECORD, OPTION_VOLUME, OPTION_WHISPER, spy_opts, and ast_channel::writeformat.
Referenced by load_module().
00781 { 00782 struct ast_module_user *u; 00783 char *options = NULL; 00784 char *exten = NULL; 00785 char *context = NULL; 00786 char *argv[2]; 00787 char *mygroup = NULL; 00788 char *recbase = NULL; 00789 int fd = 0; 00790 struct ast_flags flags; 00791 int oldwf = 0; 00792 int argc = 0; 00793 int volfactor = 0; 00794 int res; 00795 00796 data = ast_strdupa(data); 00797 00798 u = ast_module_user_add(chan); 00799 00800 if ((argc = ast_app_separate_args(data, '|', argv, sizeof(argv) / sizeof(argv[0])))) { 00801 context = argv[0]; 00802 if (!ast_strlen_zero(argv[0])) 00803 exten = strsep(&context, "@"); 00804 if (ast_strlen_zero(context)) 00805 context = ast_strdupa(chan->context); 00806 if (argc > 1) 00807 options = argv[1]; 00808 } 00809 00810 if (options) { 00811 char *opts[OPT_ARG_ARRAY_SIZE]; 00812 00813 ast_app_parse_options(spy_opts, &flags, opts, options); 00814 if (ast_test_flag(&flags, OPTION_GROUP)) 00815 mygroup = opts[OPT_ARG_GROUP]; 00816 00817 if (ast_test_flag(&flags, OPTION_RECORD) && 00818 !(recbase = opts[OPT_ARG_RECORD])) 00819 recbase = "chanspy"; 00820 00821 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) { 00822 int vol; 00823 00824 if ((sscanf(opts[OPT_ARG_VOLUME], "%30d", &vol) != 1) || (vol > 4) || (vol < -4)) 00825 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n"); 00826 else 00827 volfactor = vol; 00828 } 00829 00830 if (ast_test_flag(&flags, OPTION_PRIVATE)) 00831 ast_set_flag(&flags, OPTION_WHISPER); 00832 } else 00833 ast_clear_flag(&flags, AST_FLAGS_ALL); 00834 00835 oldwf = chan->writeformat; 00836 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) { 00837 ast_log(LOG_ERROR, "Could Not Set Write Format.\n"); 00838 ast_module_user_remove(u); 00839 return -1; 00840 } 00841 00842 if (recbase) { 00843 char filename[PATH_MAX]; 00844 00845 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL)); 00846 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644)) <= 0) { 00847 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename); 00848 fd = 0; 00849 } 00850 } 00851 00852 res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, exten, context); 00853 00854 if (fd) 00855 close(fd); 00856 00857 if (oldwf && ast_set_write_format(chan, oldwf) < 0) 00858 ast_log(LOG_ERROR, "Could Not Set Write Format.\n"); 00859 00860 ast_module_user_remove(u); 00861 00862 return res; 00863 }
static int load_module | ( | void | ) | [static] |
Definition at line 877 of file app_chanspy.c.
References ast_register_application(), chanspy_exec(), and extenspy_exec().
00878 { 00879 int res = 0; 00880 00881 res |= ast_register_application(app_chan, chanspy_exec, tdesc, desc_chan); 00882 res |= ast_register_application(app_ext, extenspy_exec, tdesc, desc_ext); 00883 00884 return res; 00885 }
static struct chanspy_ds* next_channel | ( | struct ast_channel * | chan, | |
const struct ast_channel * | last, | |||
const char * | spec, | |||
const char * | exten, | |||
const char * | context, | |||
struct chanspy_ds * | chanspy_ds | |||
) | [static] |
Definition at line 476 of file app_chanspy.c.
References AST_CHANNEL_NAME, ast_channel_unlock, ast_channel_walk_locked(), ast_walk_channel_by_exten_locked(), ast_walk_channel_by_name_prefix_locked(), dahdi_chan_name, dahdi_chan_name_len, last, and setup_chanspy_ds().
Referenced by common_exec().
00479 { 00480 struct ast_channel *this; 00481 char channel_name[AST_CHANNEL_NAME]; 00482 static size_t PSEUDO_CHAN_LEN = 0; 00483 00484 if (!PSEUDO_CHAN_LEN) { 00485 PSEUDO_CHAN_LEN = *dahdi_chan_name_len + strlen("/pseudo"); 00486 } 00487 00488 redo: 00489 if (spec) 00490 this = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec)); 00491 else if (exten) 00492 this = ast_walk_channel_by_exten_locked(last, exten, context); 00493 else 00494 this = ast_channel_walk_locked(last); 00495 00496 if (!this) 00497 return NULL; 00498 00499 snprintf(channel_name, AST_CHANNEL_NAME, "%s/pseudo", dahdi_chan_name); 00500 if (!strncmp(this->name, channel_name, PSEUDO_CHAN_LEN)) { 00501 last = this; 00502 ast_channel_unlock(this); 00503 goto redo; 00504 } else if (this == chan) { 00505 last = this; 00506 ast_channel_unlock(this); 00507 goto redo; 00508 } 00509 00510 return setup_chanspy_ds(this, chanspy_ds); 00511 }
static struct chanspy_ds* setup_chanspy_ds | ( | struct ast_channel * | chan, | |
struct chanspy_ds * | chanspy_ds | |||
) | [static] |
Definition at line 456 of file app_chanspy.c.
References ast_channel_datastore_add(), ast_channel_datastore_alloc(), ast_channel_unlock, ast_mutex_lock(), ast_mutex_unlock(), chanspy_ds::chan, chanspy_ds_free(), chanspy_ds_info, ast_datastore::data, chanspy_ds::lock, and chanspy_ds::unique_id.
Referenced by common_exec(), and next_channel().
00457 { 00458 struct ast_datastore *datastore = NULL; 00459 00460 ast_mutex_lock(&chanspy_ds->lock); 00461 00462 if (!(datastore = ast_channel_datastore_alloc(&chanspy_ds_info, chanspy_ds->unique_id))) { 00463 ast_mutex_unlock(&chanspy_ds->lock); 00464 chanspy_ds = chanspy_ds_free(chanspy_ds); 00465 ast_channel_unlock(chan); 00466 return NULL; 00467 } 00468 00469 chanspy_ds->chan = chan; 00470 datastore->data = chanspy_ds; 00471 ast_channel_datastore_add(chan, datastore); 00472 00473 return chanspy_ds; 00474 }
static void* spy_alloc | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 160 of file app_chanspy.c.
00161 { 00162 /* just store the data pointer in the channel structure */ 00163 return data; 00164 }
static int spy_generate | ( | struct ast_channel * | chan, | |
void * | data, | |||
int | len, | |||
int | samples | |||
) | [static] |
Definition at line 171 of file app_chanspy.c.
References AST_AUDIOHOOK_DIRECTION_BOTH, ast_audiohook_lock, ast_audiohook_read_frame(), AST_AUDIOHOOK_STATUS_RUNNING, ast_audiohook_unlock, AST_FORMAT_SLINEAR, ast_frfree, AST_LIST_NEXT, ast_log(), ast_write(), ast_frame::data, ast_frame::datalen, errno, f, chanspy_translation_helper::fd, LOG_WARNING, chanspy_translation_helper::spy_audiohook, and ast_audiohook::status.
00172 { 00173 struct chanspy_translation_helper *csth = data; 00174 struct ast_frame *f, *cur; 00175 00176 ast_audiohook_lock(&csth->spy_audiohook); 00177 if (csth->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) { 00178 ast_audiohook_unlock(&csth->spy_audiohook); 00179 return -1; 00180 } 00181 00182 f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR); 00183 00184 ast_audiohook_unlock(&csth->spy_audiohook); 00185 00186 if (!f) 00187 return 0; 00188 00189 for (cur = f; cur; cur = AST_LIST_NEXT(cur, frame_list)) { 00190 if (ast_write(chan, cur)) { 00191 ast_frfree(f); 00192 return -1; 00193 } 00194 00195 if (csth->fd) { 00196 if (write(csth->fd, cur->data, cur->datalen) < 0) { 00197 ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno)); 00198 } 00199 } 00200 } 00201 00202 ast_frfree(f); 00203 00204 return 0; 00205 }
static void spy_release | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
static int start_spying | ( | struct ast_channel * | chan, | |
const char * | spychan_name, | |||
struct ast_audiohook * | audiohook | |||
) | [static] |
Definition at line 213 of file app_chanspy.c.
References ast_audiohook_attach(), AST_AUDIOHOOK_SMALL_QUEUE, AST_AUDIOHOOK_TRIGGER_SYNC, ast_bridged_channel(), AST_FLAG_NBRIDGE, ast_log(), ast_set_flag, ast_softhangup(), AST_SOFTHANGUP_UNBRIDGE, ast_test_flag, LOG_NOTICE, and ast_channel::name.
Referenced by channel_spy().
00214 { 00215 int res; 00216 struct ast_channel *peer; 00217 00218 ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, chan->name); 00219 00220 ast_set_flag(audiohook, AST_AUDIOHOOK_TRIGGER_SYNC | AST_AUDIOHOOK_SMALL_QUEUE); 00221 00222 res = ast_audiohook_attach(chan, audiohook); 00223 00224 if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan))) { 00225 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE); 00226 } 00227 return res; 00228 }
static int unload_module | ( | void | ) | [static] |
Definition at line 865 of file app_chanspy.c.
References ast_module_user_hangup_all, and ast_unregister_application().
00866 { 00867 int res = 0; 00868 00869 res |= ast_unregister_application(app_chan); 00870 res |= ast_unregister_application(app_ext); 00871 00872 ast_module_user_hangup_all(); 00873 00874 return res; 00875 }
struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT | AST_MODFLAG_BUILDSUM, .description = "Listen to the audio of an active channel" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "361d7bb937402d51e4658efb5b4d76e4" , .load = load_module, .unload = unload_module, } [static] |
Definition at line 887 of file app_chanspy.c.
const char* app_chan = "ChanSpy" [static] |
Definition at line 64 of file app_chanspy.c.
const char* app_ext = "ExtenSpy" [static] |
Definition at line 95 of file app_chanspy.c.
const struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 887 of file app_chanspy.c.
struct ast_datastore_info chanspy_ds_info [static] |
Initial value:
{ .type = "chanspy", .destroy = chanspy_ds_destroy, .chan_fixup = chanspy_ds_chan_fixup, }
Definition at line 418 of file app_chanspy.c.
Referenced by chanspy_ds_free(), and setup_chanspy_ds().
enum { ... } chanspy_opt_args |
enum { ... } chanspy_opt_flags |
const char* desc_chan [static] |
Definition at line 65 of file app_chanspy.c.
const char* desc_ext [static] |
Definition at line 96 of file app_chanspy.c.
int next_unique_id_to_use = 0 [static] |
Definition at line 150 of file app_chanspy.c.
struct ast_app_option spy_opts[128] = { [ 'q' ] = { .flag = OPTION_QUIET }, [ 'b' ] = { .flag = OPTION_BRIDGED }, [ 'w' ] = { .flag = OPTION_WHISPER }, [ 'W' ] = { .flag = OPTION_PRIVATE }, [ 'v' ] = { .flag = OPTION_VOLUME , .arg_index = OPT_ARG_VOLUME + 1 }, [ 'g' ] = { .flag = OPTION_GROUP , .arg_index = OPT_ARG_GROUP + 1 }, [ 'r' ] = { .flag = OPTION_RECORD , .arg_index = OPT_ARG_RECORD + 1 },} [static] |
struct ast_generator spygen [static] |
Initial value:
{ .alloc = spy_alloc, .release = spy_release, .generate = spy_generate, }
Definition at line 207 of file app_chanspy.c.
Referenced by channel_spy().
const char* tdesc = "Listen to a channel, and optionally whisper into it" [static] |
Definition at line 63 of file app_chanspy.c.