#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include "asterisk.h"
#include "asterisk/file.h"
#include "asterisk/logger.h"
#include "asterisk/channel.h"
#include "asterisk/chanspy.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_translation_helper |
Defines | |
#define | ALL_DONE(u, ret) LOCAL_USER_REMOVE(u); return ret; |
#define | AST_NAME_STRLEN 256 |
#define | get_volfactor(x) x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0 |
Enumerations | |
enum | { OPTION_QUIET = (1 << 0), OPTION_BRIDGED = (1 << 1), OPTION_VOLUME = (1 << 2), OPTION_GROUP = (1 << 3), OPTION_RECORD = (1 << 4) } |
enum | { OPT_ARG_VOLUME = 0, OPT_ARG_GROUP, OPT_ARG_RECORD, OPT_ARG_ARRAY_SIZE } |
Functions | |
AST_APP_OPTIONS (chanspy_opts,{AST_APP_OPTION('q', OPTION_QUIET), AST_APP_OPTION('b', OPTION_BRIDGED), AST_APP_OPTION_ARG('v', OPTION_VOLUME, OPT_ARG_VOLUME), AST_APP_OPTION_ARG('g', OPTION_GROUP, OPT_ARG_GROUP), AST_APP_OPTION_ARG('r', OPTION_RECORD, OPT_ARG_RECORD),}) | |
AST_MUTEX_DEFINE_STATIC (modlock) | |
static int | channel_spy (struct ast_channel *chan, struct ast_channel *spyee, int *volfactor, int fd) |
static int | chanspy_exec (struct ast_channel *chan, void *data) |
char * | description (void) |
Provides a description of the module. | |
char * | key () |
Returns the ASTERISK_GPL_KEY. | |
int | load_module (void) |
Initialize the module. | |
static struct ast_channel * | local_channel_walk (struct ast_channel *chan) |
static struct ast_channel * | local_get_channel_begin_name (char *name) |
static void | set_volume (struct ast_channel *chan, struct chanspy_translation_helper *csth) |
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, struct ast_channel *spychan, struct ast_channel_spy *spy) |
int | unload_module (void) |
Cleanup all module structures, sockets, etc. | |
int | usecount (void) |
Provides a usecount. | |
Variables | |
static const char * | app = "ChanSpy" |
enum { ... } | chanspy_opt_args |
struct { | |
int alarm | |
char * description | |
unsigned int event_log:1 | |
enum queue_result id | |
char * name | |
char * name | |
char * name | |
rtpPayloadType payloadType | |
unsigned int queue_log:1 | |
char * subtype | |
char * text | |
char * type | |
int val | |
} | chanspy_opt_flags |
static const char * | chanspy_spy_type = "ChanSpy" |
static const char * | desc |
LOCAL_USER_DECL | |
static struct ast_generator | spygen |
STANDARD_LOCAL_USER | |
static const char * | synopsis = "Listen to the audio of an active channel\n" |
static signed char | volfactor_map [] |
Definition in file app_chanspy.c.
|
Definition at line 53 of file app_chanspy.c. Referenced by _while_exec(), and execif_exec(). |
|
Definition at line 52 of file app_chanspy.c. Referenced by chanspy_exec(). |
|
Definition at line 54 of file app_chanspy.c. Referenced by mixmonitor_exec(). |
|
Definition at line 84 of file app_chanspy.c. 00084 { 00085 OPTION_QUIET = (1 << 0), /* Quiet, no announcement */ 00086 OPTION_BRIDGED = (1 << 1), /* Only look at bridged calls */ 00087 OPTION_VOLUME = (1 << 2), /* Specify initial volume */ 00088 OPTION_GROUP = (1 << 3), /* Only look at channels in group */ 00089 OPTION_RECORD = (1 << 4), /* Record */ 00090 } chanspy_opt_flags;
|
|
Definition at line 92 of file app_chanspy.c. 00092 { 00093 OPT_ARG_VOLUME = 0, 00094 OPT_ARG_GROUP, 00095 OPT_ARG_RECORD, 00096 OPT_ARG_ARRAY_SIZE, 00097 } chanspy_opt_args;
|
|
|
|
|
|
Definition at line 237 of file app_chanspy.c. References ast_activate_generator(), ast_channel_spy_free(), ast_channel_spy_remove(), ast_check_hangup(), ast_clear_flag, ast_deactivate_generator(), AST_FORMAT_SLINEAR, AST_FRAME_DTMF, ast_frfree(), ast_mutex_init(), ast_mutex_lock(), ast_mutex_unlock(), ast_read(), ast_set_flag, ast_strdupa, ast_strlen_zero(), ast_verbose(), ast_waitfor(), CHANSPY_DONE, CHANSPY_FORMAT_AUDIO, CHANSPY_MIXAUDIO, CHANSPY_READ_VOLADJUST, CHANSPY_RUNNING, CHANSPY_TRIGGER_NONE, CHANSPY_WRITE_VOLADJUST, ast_frame::frametype, ast_channel::name, name, option_verbose, set_volume(), spygen, start_spying(), ast_frame::subclass, VERBOSE_PREFIX_2, and VERBOSE_PREFIX_3. 00238 { 00239 struct chanspy_translation_helper csth; 00240 int running = 0, res = 0, x = 0; 00241 char inp[24] = "", *name = NULL; 00242 struct ast_frame *f = NULL; 00243 00244 if ((chan && ast_check_hangup(chan)) || (spyee && ast_check_hangup(spyee))) 00245 return 0; 00246 00247 name = ast_strdupa(spyee->name); 00248 if (option_verbose > 1) 00249 ast_verbose(VERBOSE_PREFIX_2 "Spying on channel %s\n", name); 00250 00251 memset(&csth, 0, sizeof(csth)); 00252 ast_set_flag(&csth.spy, CHANSPY_FORMAT_AUDIO); 00253 ast_set_flag(&csth.spy, CHANSPY_TRIGGER_NONE); 00254 ast_set_flag(&csth.spy, CHANSPY_MIXAUDIO); 00255 csth.spy.type = chanspy_spy_type; 00256 csth.spy.status = CHANSPY_RUNNING; 00257 csth.spy.read_queue.format = AST_FORMAT_SLINEAR; 00258 csth.spy.write_queue.format = AST_FORMAT_SLINEAR; 00259 ast_mutex_init(&csth.spy.lock); 00260 csth.volfactor = *volfactor; 00261 set_volume(chan, &csth); 00262 if (csth.volfactor) { 00263 ast_set_flag(&csth.spy, CHANSPY_READ_VOLADJUST); 00264 csth.spy.read_vol_adjustment = csth.volfactor; 00265 ast_set_flag(&csth.spy, CHANSPY_WRITE_VOLADJUST); 00266 csth.spy.write_vol_adjustment = csth.volfactor; 00267 } 00268 csth.fd = fd; 00269 00270 if (start_spying(spyee, chan, &csth.spy)) { 00271 ast_channel_spy_free(&csth.spy); 00272 return 0; 00273 } 00274 00275 ast_activate_generator(chan, &spygen, &csth); 00276 00277 while (csth.spy.status == CHANSPY_RUNNING && 00278 (res = ast_waitfor(chan, -1) > -1)) { 00279 00280 /* Read in frame from channel, break out if no frame */ 00281 if (!(f = ast_read(chan))) 00282 break; 00283 00284 /* Now if this is DTMF then we have to handle it as such, otherwise just skip it */ 00285 res = 0; 00286 if (f->frametype == AST_FRAME_DTMF) 00287 res = f->subclass; 00288 ast_frfree(f); 00289 if (!res) 00290 continue; 00291 00292 if (x == sizeof(inp)) 00293 x = 0; 00294 00295 if (res < 0) { 00296 running = -1; 00297 break; 00298 } 00299 00300 /* Process DTMF digits */ 00301 if (res == '#') { 00302 if (!ast_strlen_zero(inp)) { 00303 running = x ? atoi(inp) : -1; 00304 break; 00305 } else { 00306 (*volfactor)++; 00307 if (*volfactor > 4) 00308 *volfactor = -1; 00309 if (option_verbose > 2) 00310 ast_verbose(VERBOSE_PREFIX_3 "Setting spy volume on %s to %d\n", chan->name, *volfactor); 00311 csth.volfactor = *volfactor; 00312 set_volume(chan, &csth); 00313 if (csth.volfactor) { 00314 ast_set_flag(&csth.spy, CHANSPY_READ_VOLADJUST); 00315 csth.spy.read_vol_adjustment = csth.volfactor; 00316 ast_set_flag(&csth.spy, CHANSPY_WRITE_VOLADJUST); 00317 csth.spy.write_vol_adjustment = csth.volfactor; 00318 } else { 00319 ast_clear_flag(&csth.spy, CHANSPY_READ_VOLADJUST); 00320 ast_clear_flag(&csth.spy, CHANSPY_WRITE_VOLADJUST); 00321 } 00322 } 00323 } else if (res == '*') { 00324 break; 00325 } else if (res >= 48 && res <= 57) { 00326 inp[x++] = res; 00327 } 00328 } 00329 00330 ast_deactivate_generator(chan); 00331 00332 ast_mutex_lock(&csth.spy.lock); 00333 if (csth.spy.chan) { 00334 csth.spy.status = CHANSPY_DONE; 00335 ast_mutex_lock(&csth.spy.chan->lock); 00336 ast_channel_spy_remove(csth.spy.chan, &csth.spy); 00337 ast_mutex_unlock(&csth.spy.chan->lock); 00338 } 00339 ast_mutex_unlock(&csth.spy.lock); 00340 00341 if (option_verbose > 1) 00342 ast_verbose(VERBOSE_PREFIX_2 "Done Spying on channel %s\n", name); 00343 00344 ast_channel_spy_free(&csth.spy); 00345 00346 return running; 00347 }
|
|
Definition at line 349 of file app_chanspy.c. References ast_answer(), ast_app_parse_options(), ast_app_separate_args(), ast_clear_flag, ast_config_AST_MONITOR_DIR, AST_FLAG_SPYING, AST_FORMAT_SLINEAR, ast_log(), AST_NAME_STRLEN, ast_set_flag, ast_set_read_format(), ast_set_write_format(), ast_strdupa, ast_streamfile(), ast_strlen_zero(), ast_test_flag, ast_waitstream(), ast_flags::flags, ast_channel::language, LOCAL_USER_ADD, LOCAL_USER_REMOVE, LOG_ERROR, LOG_NOTICE, LOG_WARNING, name, OPT_ARG_ARRAY_SIZE, OPT_ARG_GROUP, OPT_ARG_RECORD, OPT_ARG_VOLUME, OPTION_BRIDGED, OPTION_GROUP, OPTION_QUIET, OPTION_RECORD, OPTION_VOLUME, ast_channel::readformat, and ast_channel::writeformat. Referenced by load_module(). 00350 { 00351 struct localuser *u; 00352 struct ast_channel *peer=NULL, *prev=NULL; 00353 char name[AST_NAME_STRLEN], 00354 peer_name[AST_NAME_STRLEN + 5], 00355 *args, 00356 *ptr = NULL, 00357 *options = NULL, 00358 *spec = NULL, 00359 *argv[5], 00360 *mygroup = NULL, 00361 *recbase = NULL; 00362 int res = -1, 00363 volfactor = 0, 00364 silent = 0, 00365 argc = 0, 00366 bronly = 0, 00367 chosen = 0, 00368 count=0, 00369 waitms = 100, 00370 num = 0, 00371 oldrf = 0, 00372 oldwf = 0, 00373 fd = 0; 00374 struct ast_flags flags; 00375 signed char zero_volume = 0; 00376 00377 if (!(args = ast_strdupa((char *)data))) { 00378 ast_log(LOG_ERROR, "Out of memory!\n"); 00379 return -1; 00380 } 00381 00382 LOCAL_USER_ADD(u); 00383 00384 oldrf = chan->readformat; 00385 oldwf = chan->writeformat; 00386 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) { 00387 ast_log(LOG_ERROR, "Could Not Set Read Format.\n"); 00388 LOCAL_USER_REMOVE(u); 00389 return -1; 00390 } 00391 00392 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) { 00393 ast_log(LOG_ERROR, "Could Not Set Write Format.\n"); 00394 LOCAL_USER_REMOVE(u); 00395 return -1; 00396 } 00397 00398 ast_answer(chan); 00399 00400 ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */ 00401 00402 if ((argc = ast_app_separate_args(args, '|', argv, sizeof(argv) / sizeof(argv[0])))) { 00403 spec = argv[0]; 00404 if ( argc > 1) { 00405 options = argv[1]; 00406 } 00407 if (ast_strlen_zero(spec) || !strcmp(spec, "all")) { 00408 spec = NULL; 00409 } 00410 } 00411 00412 if (options) { 00413 char *opts[OPT_ARG_ARRAY_SIZE]; 00414 ast_app_parse_options(chanspy_opts, &flags, opts, options); 00415 if (ast_test_flag(&flags, OPTION_GROUP)) { 00416 mygroup = opts[OPT_ARG_GROUP]; 00417 } 00418 if (ast_test_flag(&flags, OPTION_RECORD)) { 00419 if (!(recbase = opts[OPT_ARG_RECORD])) { 00420 recbase = "chanspy"; 00421 } 00422 } 00423 silent = ast_test_flag(&flags, OPTION_QUIET); 00424 bronly = ast_test_flag(&flags, OPTION_BRIDGED); 00425 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) { 00426 int vol; 00427 00428 if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4)) 00429 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n"); 00430 else 00431 volfactor = vol; 00432 } 00433 } 00434 00435 if (recbase) { 00436 char filename[512]; 00437 snprintf(filename,sizeof(filename),"%s/%s.%d.raw",ast_config_AST_MONITOR_DIR, recbase, (int)time(NULL)); 00438 if ((fd = open(filename, O_CREAT | O_WRONLY, O_TRUNC, 0644)) <= 0) { 00439 ast_log(LOG_WARNING, "Cannot open %s for recording\n", filename); 00440 fd = 0; 00441 } 00442 } 00443 00444 for(;;) { 00445 if (!silent) { 00446 res = ast_streamfile(chan, "beep", chan->language); 00447 if (!res) 00448 res = ast_waitstream(chan, ""); 00449 if (res < 0) { 00450 ast_clear_flag(chan, AST_FLAG_SPYING); 00451 break; 00452 } 00453 } 00454 00455 count = 0; 00456 res = ast_waitfordigit(chan, waitms); 00457 if (res < 0) { 00458 ast_clear_flag(chan, AST_FLAG_SPYING); 00459 break; 00460 } 00461 00462 peer = local_channel_walk(NULL); 00463 prev=NULL; 00464 while(peer) { 00465 if (peer != chan) { 00466 char *group = NULL; 00467 int igrp = 1; 00468 00469 if (peer == prev && !chosen) { 00470 break; 00471 } 00472 chosen = 0; 00473 group = pbx_builtin_getvar_helper(peer, "SPYGROUP"); 00474 if (mygroup) { 00475 if (!group || strcmp(mygroup, group)) { 00476 igrp = 0; 00477 } 00478 } 00479 00480 if (igrp && (!spec || ((strlen(spec) <= strlen(peer->name) && 00481 !strncasecmp(peer->name, spec, strlen(spec)))))) { 00482 if (peer && (!bronly || ast_bridged_channel(peer)) && 00483 !ast_check_hangup(peer) && !ast_test_flag(peer, AST_FLAG_SPYING)) { 00484 int x = 0; 00485 strncpy(peer_name, "spy-", 5); 00486 strncpy(peer_name + strlen(peer_name), peer->name, AST_NAME_STRLEN); 00487 ptr = strchr(peer_name, '/'); 00488 *ptr = '\0'; 00489 ptr++; 00490 for (x = 0 ; x < strlen(peer_name) ; x++) { 00491 if (peer_name[x] == '/') { 00492 break; 00493 } 00494 peer_name[x] = tolower(peer_name[x]); 00495 } 00496 00497 if (!silent) { 00498 if (ast_fileexists(peer_name, NULL, NULL) != -1) { 00499 res = ast_streamfile(chan, peer_name, chan->language); 00500 if (!res) 00501 res = ast_waitstream(chan, ""); 00502 if (res) 00503 break; 00504 } else 00505 res = ast_say_character_str(chan, peer_name, "", chan->language); 00506 if ((num=atoi(ptr))) 00507 ast_say_digits(chan, atoi(ptr), "", chan->language); 00508 } 00509 count++; 00510 prev = peer; 00511 res = channel_spy(chan, peer, &volfactor, fd); 00512 if (res == -1) { 00513 break; 00514 } else if (res > 1 && spec) { 00515 snprintf(name, AST_NAME_STRLEN, "%s/%d", spec, res); 00516 if ((peer = local_get_channel_begin_name(name))) { 00517 chosen = 1; 00518 } 00519 continue; 00520 } 00521 } 00522 } 00523 } 00524 if ((peer = local_channel_walk(peer)) == NULL) { 00525 break; 00526 } 00527 } 00528 waitms = count ? 100 : 5000; 00529 } 00530 00531 00532 if (fd > 0) { 00533 close(fd); 00534 } 00535 00536 if (oldrf && ast_set_read_format(chan, oldrf) < 0) { 00537 ast_log(LOG_ERROR, "Could Not Set Read Format.\n"); 00538 } 00539 00540 if (oldwf && ast_set_write_format(chan, oldwf) < 0) { 00541 ast_log(LOG_ERROR, "Could Not Set Write Format.\n"); 00542 } 00543 00544 ast_clear_flag(chan, AST_FLAG_SPYING); 00545 00546 ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0); 00547 00548 ALL_DONE(u, res); 00549 }
|
|
Provides a description of the module.
Definition at line 567 of file app_chanspy.c. 00568 { 00569 return (char *) synopsis; 00570 }
|
|
Returns the ASTERISK_GPL_KEY. This returns the ASTERISK_GPL_KEY, signifiying that you agree to the terms of the GPL stated in the ASTERISK_GPL_KEY. Your module will not load if it does not return the EXACT message:
char *key(void) { return ASTERISK_GPL_KEY; }
Definition at line 579 of file app_chanspy.c. References ASTERISK_GPL_KEY. 00580 { 00581 return ASTERISK_GPL_KEY; 00582 }
|
|
Initialize the module. Initialize the Agents module. This function is being called by Asterisk when loading the module. Among other thing it registers applications, cli commands and reads the cofiguration file.
Definition at line 562 of file app_chanspy.c. References ast_register_application(), and chanspy_exec(). 00563 { 00564 return ast_register_application(app, chanspy_exec, synopsis, desc); 00565 }
|
|
Definition at line 117 of file app_chanspy.c. References ast_channel_walk_locked(), ast_mutex_lock(), ast_mutex_unlock(), and ast_channel::lock. Referenced by local_get_channel_begin_name(). 00118 { 00119 struct ast_channel *ret; 00120 ast_mutex_lock(&modlock); 00121 if ((ret = ast_channel_walk_locked(chan))) { 00122 ast_mutex_unlock(&ret->lock); 00123 } 00124 ast_mutex_unlock(&modlock); 00125 return ret; 00126 }
|
|
Definition at line 128 of file app_chanspy.c. References ast_mutex_lock(), ast_mutex_unlock(), local_channel_walk(), and ast_channel::name. 00129 { 00130 struct ast_channel *chan, *ret = NULL; 00131 ast_mutex_lock(&modlock); 00132 chan = local_channel_walk(NULL); 00133 while (chan) { 00134 if (!strncmp(chan->name, name, strlen(name)) && strncmp(chan->name, "Zap/pseudo", 10)) { 00135 ret = chan; 00136 break; 00137 } 00138 chan = local_channel_walk(chan); 00139 } 00140 ast_mutex_unlock(&modlock); 00141 00142 return ret; 00143 }
|
|
Definition at line 229 of file app_chanspy.c. References ast_channel_setoption(), AST_OPTION_TXGAIN, and chanspy_translation_helper::volfactor. Referenced by channel_spy(). 00230 { 00231 signed char volume_adjust = volfactor_map[csth->volfactor + 4]; 00232 00233 if (!ast_channel_setoption(chan, AST_OPTION_TXGAIN, &volume_adjust, sizeof(volume_adjust), 0)) 00234 csth->volfactor = 0; 00235 }
|
|
Definition at line 145 of file app_chanspy.c. 00146 { 00147 /* just store the data pointer in the channel structure */ 00148 return data; 00149 }
|
|
Definition at line 156 of file app_chanspy.c. References ast_channel_spy_read_frame(), ast_frfree(), ast_mutex_lock(), ast_mutex_unlock(), ast_write(), CHANSPY_RUNNING, ast_frame::data, ast_frame::datalen, chanspy_translation_helper::fd, ast_channel_spy::lock, chanspy_translation_helper::spy, and ast_channel_spy::status. 00157 { 00158 struct chanspy_translation_helper *csth = data; 00159 struct ast_frame *f; 00160 00161 if (csth->spy.status != CHANSPY_RUNNING) 00162 /* Channel is already gone more than likely */ 00163 return -1; 00164 00165 ast_mutex_lock(&csth->spy.lock); 00166 f = ast_channel_spy_read_frame(&csth->spy, samples); 00167 ast_mutex_unlock(&csth->spy.lock); 00168 00169 if (!f) 00170 return 0; 00171 00172 if (ast_write(chan, f)) { 00173 ast_frfree(f); 00174 return -1; 00175 } 00176 00177 if (csth->fd) 00178 write(csth->fd, f->data, f->datalen); 00179 00180 ast_frfree(f); 00181 00182 return 0; 00183 }
|
|
Definition at line 151 of file app_chanspy.c.
|
|
Definition at line 192 of file app_chanspy.c. References ast_bridged_channel(), ast_channel_spy_add(), AST_FLAG_NBRIDGE, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), ast_softhangup(), AST_SOFTHANGUP_UNBRIDGE, ast_test_flag, ast_channel::lock, LOG_NOTICE, and ast_channel::name. Referenced by channel_spy(). 00193 { 00194 int res; 00195 struct ast_channel *peer; 00196 00197 ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan->name, chan->name); 00198 00199 ast_mutex_lock(&chan->lock); 00200 res = ast_channel_spy_add(chan, spy); 00201 ast_mutex_unlock(&chan->lock); 00202 00203 if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan))) { 00204 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE); 00205 } 00206 00207 return res; 00208 }
|
|
Cleanup all module structures, sockets, etc. This is called at exit. Any registrations and memory allocations need to be unregistered and free'd here. Nothing else will do these for you (until exit).
Definition at line 551 of file app_chanspy.c. References ast_unregister_application(), and STANDARD_HANGUP_LOCALUSERS. 00552 { 00553 int res; 00554 00555 res = ast_unregister_application(app); 00556 00557 STANDARD_HANGUP_LOCALUSERS; 00558 00559 return res; 00560 }
|
|
Provides a usecount. This function will be called by various parts of asterisk. Basically, all it has to do is to return a usecount when called. You will need to maintain your usecount within the module somewhere. The usecount should be how many channels provided by this module are in use.
Definition at line 572 of file app_chanspy.c. References STANDARD_USECOUNT. 00573 { 00574 int res; 00575 STANDARD_USECOUNT(res); 00576 return res; 00577 }
|
|
Definition at line 57 of file app_chanspy.c. |
|
|
|
|
|
Definition at line 82 of file app_chanspy.c. |
|
Definition at line 58 of file app_chanspy.c. |
|
Definition at line 108 of file app_chanspy.c. |
|
Initial value: { .alloc = spy_alloc, .release = spy_release, .generate = spy_generate, } Definition at line 186 of file app_chanspy.c. Referenced by channel_spy(). |
|
Definition at line 107 of file app_chanspy.c. |
|
Definition at line 56 of file app_chanspy.c. |
|
Definition at line 213 of file app_chanspy.c. |