#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"
Include dependency graph for app_chanspy.c:
Go to the source code of this file.
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) |
static void | stop_spying (struct ast_channel *chan, 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 |
enum { ... } | 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(), chanspy_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 252 of file app_chanspy.c. References ast_activate_generator(), ast_check_hangup(), ast_deactivate_generator(), AST_FORMAT_SLINEAR, AST_FRAME_DTMF, ast_frfree(), ast_mutex_destroy(), ast_mutex_init(), ast_read(), ast_set_flag, ast_strdupa, ast_strlen_zero(), ast_verbose(), ast_waitfor(), CHANSPY_FORMAT_AUDIO, CHANSPY_MIXAUDIO, CHANSPY_RUNNING, chanspy_spy_type, CHANSPY_TRIGGER_NONE, ast_frame::frametype, ast_channel_spy::lock, ast_channel::name, name, option_verbose, ast_channel_spy::read_vol_adjustment, set_volume(), chanspy_translation_helper::spy, start_spying(), ast_channel_spy::status, stop_spying(), ast_frame::subclass, VERBOSE_PREFIX_2, VERBOSE_PREFIX_3, chanspy_translation_helper::volfactor, and ast_channel_spy::write_vol_adjustment. Referenced by chanspy_exec(). 00253 { 00254 struct chanspy_translation_helper csth; 00255 int running, res = 0, x = 0; 00256 char inp[24]; 00257 char *name=NULL; 00258 struct ast_frame *f; 00259 00260 running = (chan && !ast_check_hangup(chan) && spyee && !ast_check_hangup(spyee)); 00261 00262 if (running) { 00263 memset(inp, 0, sizeof(inp)); 00264 name = ast_strdupa(spyee->name); 00265 if (option_verbose >= 2) 00266 ast_verbose(VERBOSE_PREFIX_2 "Spying on channel %s\n", name); 00267 00268 memset(&csth, 0, sizeof(csth)); 00269 ast_set_flag(&csth.spy, CHANSPY_FORMAT_AUDIO); 00270 ast_set_flag(&csth.spy, CHANSPY_TRIGGER_NONE); 00271 ast_set_flag(&csth.spy, CHANSPY_MIXAUDIO); 00272 csth.spy.type = chanspy_spy_type; 00273 csth.spy.status = CHANSPY_RUNNING; 00274 csth.spy.read_queue.format = AST_FORMAT_SLINEAR; 00275 csth.spy.write_queue.format = AST_FORMAT_SLINEAR; 00276 ast_mutex_init(&csth.spy.lock); 00277 csth.volfactor = *volfactor; 00278 set_volume(chan, &csth); 00279 csth.spy.read_vol_adjustment = csth.volfactor; 00280 csth.spy.write_vol_adjustment = csth.volfactor; 00281 csth.fd = fd; 00282 00283 if (start_spying(spyee, chan, &csth.spy)) 00284 running = 0; 00285 } 00286 00287 if (running) { 00288 running = 1; 00289 ast_activate_generator(chan, &spygen, &csth); 00290 00291 while (csth.spy.status == CHANSPY_RUNNING && 00292 chan && !ast_check_hangup(chan) && 00293 spyee && 00294 !ast_check_hangup(spyee) && 00295 running == 1 && 00296 (res = ast_waitfor(chan, -1) > -1)) { 00297 if ((f = ast_read(chan))) { 00298 res = 0; 00299 if (f->frametype == AST_FRAME_DTMF) { 00300 res = f->subclass; 00301 } 00302 ast_frfree(f); 00303 if (!res) { 00304 continue; 00305 } 00306 } else { 00307 break; 00308 } 00309 if (x == sizeof(inp)) { 00310 x = 0; 00311 } 00312 if (res < 0) { 00313 running = -1; 00314 } 00315 if (res == 0) { 00316 continue; 00317 } else if (res == '*') { 00318 running = 0; 00319 } else if (res == '#') { 00320 if (!ast_strlen_zero(inp)) { 00321 running = x ? atoi(inp) : -1; 00322 break; 00323 } else { 00324 (*volfactor)++; 00325 if (*volfactor > 4) { 00326 *volfactor = -4; 00327 } 00328 if (option_verbose > 2) { 00329 ast_verbose(VERBOSE_PREFIX_3 "Setting spy volume on %s to %d\n", chan->name, *volfactor); 00330 } 00331 csth.volfactor = *volfactor; 00332 set_volume(chan, &csth); 00333 csth.spy.read_vol_adjustment = csth.volfactor; 00334 csth.spy.write_vol_adjustment = csth.volfactor; 00335 } 00336 } else if (res >= 48 && res <= 57) { 00337 inp[x++] = res; 00338 } 00339 } 00340 ast_deactivate_generator(chan); 00341 stop_spying(spyee, &csth.spy); 00342 00343 if (option_verbose >= 2) { 00344 ast_verbose(VERBOSE_PREFIX_2 "Done Spying on channel %s\n", name); 00345 } 00346 } else { 00347 running = 0; 00348 } 00349 00350 ast_mutex_destroy(&csth.spy.lock); 00351 00352 return running; 00353 }
|
|
Definition at line 355 of file app_chanspy.c. References ALL_DONE, ast_answer(), ast_app_parse_options(), ast_app_separate_args(), ast_bridged_channel(), ast_channel_setoption(), ast_check_hangup(), ast_clear_flag, ast_config_AST_MONITOR_DIR, ast_fileexists(), AST_FLAG_SPYING, AST_FORMAT_SLINEAR, ast_log(), AST_NAME_STRLEN, AST_OPTION_TXGAIN, ast_say_character_str(), ast_say_digits(), ast_set_flag, ast_set_read_format(), ast_set_write_format(), ast_strdupa, ast_streamfile(), ast_strlen_zero(), ast_test_flag, ast_waitfordigit(), ast_waitstream(), channel_spy(), group, ast_channel::language, local_channel_walk(), local_get_channel_begin_name(), LOCAL_USER_ADD, LOCAL_USER_REMOVE, LOG_ERROR, LOG_NOTICE, LOG_WARNING, name, OPT_ARG_ARRAY_SIZE, OPTION_BRIDGED, OPTION_GROUP, OPTION_QUIET, OPTION_RECORD, OPTION_VOLUME, pbx_builtin_getvar_helper(), ast_channel::readformat, and ast_channel::writeformat. Referenced by load_module(). 00356 { 00357 struct localuser *u; 00358 struct ast_channel *peer=NULL, *prev=NULL; 00359 char name[AST_NAME_STRLEN], 00360 peer_name[AST_NAME_STRLEN + 5], 00361 *args, 00362 *ptr = NULL, 00363 *options = NULL, 00364 *spec = NULL, 00365 *argv[5], 00366 *mygroup = NULL, 00367 *recbase = NULL; 00368 int res = -1, 00369 volfactor = 0, 00370 silent = 0, 00371 argc = 0, 00372 bronly = 0, 00373 chosen = 0, 00374 count=0, 00375 waitms = 100, 00376 num = 0, 00377 oldrf = 0, 00378 oldwf = 0, 00379 fd = 0; 00380 struct ast_flags flags; 00381 signed char zero_volume = 0; 00382 00383 if (!(args = ast_strdupa((char *)data))) { 00384 ast_log(LOG_ERROR, "Out of memory!\n"); 00385 return -1; 00386 } 00387 00388 LOCAL_USER_ADD(u); 00389 00390 oldrf = chan->readformat; 00391 oldwf = chan->writeformat; 00392 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) { 00393 ast_log(LOG_ERROR, "Could Not Set Read Format.\n"); 00394 LOCAL_USER_REMOVE(u); 00395 return -1; 00396 } 00397 00398 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) { 00399 ast_log(LOG_ERROR, "Could Not Set Write Format.\n"); 00400 LOCAL_USER_REMOVE(u); 00401 return -1; 00402 } 00403 00404 ast_answer(chan); 00405 00406 ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */ 00407 00408 if ((argc = ast_app_separate_args(args, '|', argv, sizeof(argv) / sizeof(argv[0])))) { 00409 spec = argv[0]; 00410 if ( argc > 1) { 00411 options = argv[1]; 00412 } 00413 if (ast_strlen_zero(spec) || !strcmp(spec, "all")) { 00414 spec = NULL; 00415 } 00416 } 00417 00418 if (options) { 00419 char *opts[OPT_ARG_ARRAY_SIZE]; 00420 ast_app_parse_options(chanspy_opts, &flags, opts, options); 00421 if (ast_test_flag(&flags, OPTION_GROUP)) { 00422 mygroup = opts[1]; 00423 } 00424 if (ast_test_flag(&flags, OPTION_RECORD)) { 00425 if (!(recbase = opts[2])) { 00426 recbase = "chanspy"; 00427 } 00428 } 00429 silent = ast_test_flag(&flags, OPTION_QUIET); 00430 bronly = ast_test_flag(&flags, OPTION_BRIDGED); 00431 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[1]) { 00432 int vol; 00433 00434 if ((sscanf(opts[0], "%d", &vol) != 1) || (vol > 4) || (vol < -4)) 00435 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n"); 00436 else 00437 volfactor = vol; 00438 } 00439 } 00440 00441 if (recbase) { 00442 char filename[512]; 00443 snprintf(filename,sizeof(filename),"%s/%s.%d.raw",ast_config_AST_MONITOR_DIR, recbase, (int)time(NULL)); 00444 if ((fd = open(filename, O_CREAT | O_WRONLY, O_TRUNC, 0644)) <= 0) { 00445 ast_log(LOG_WARNING, "Cannot open %s for recording\n", filename); 00446 fd = 0; 00447 } 00448 } 00449 00450 for(;;) { 00451 if (!silent) { 00452 res = ast_streamfile(chan, "beep", chan->language); 00453 if (!res) 00454 res = ast_waitstream(chan, ""); 00455 if (res < 0) { 00456 ast_clear_flag(chan, AST_FLAG_SPYING); 00457 break; 00458 } 00459 } 00460 00461 count = 0; 00462 res = ast_waitfordigit(chan, waitms); 00463 if (res < 0) { 00464 ast_clear_flag(chan, AST_FLAG_SPYING); 00465 break; 00466 } 00467 00468 peer = local_channel_walk(NULL); 00469 prev=NULL; 00470 while(peer) { 00471 if (peer != chan) { 00472 char *group = NULL; 00473 int igrp = 1; 00474 00475 if (peer == prev && !chosen) { 00476 break; 00477 } 00478 chosen = 0; 00479 group = pbx_builtin_getvar_helper(peer, "SPYGROUP"); 00480 if (mygroup) { 00481 if (!group || strcmp(mygroup, group)) { 00482 igrp = 0; 00483 } 00484 } 00485 00486 if (igrp && (!spec || ((strlen(spec) <= strlen(peer->name) && 00487 !strncasecmp(peer->name, spec, strlen(spec)))))) { 00488 if (peer && (!bronly || ast_bridged_channel(peer)) && 00489 !ast_check_hangup(peer) && !ast_test_flag(peer, AST_FLAG_SPYING)) { 00490 int x = 0; 00491 strncpy(peer_name, "spy-", 5); 00492 strncpy(peer_name + strlen(peer_name), peer->name, AST_NAME_STRLEN); 00493 ptr = strchr(peer_name, '/'); 00494 *ptr = '\0'; 00495 ptr++; 00496 for (x = 0 ; x < strlen(peer_name) ; x++) { 00497 if (peer_name[x] == '/') { 00498 break; 00499 } 00500 peer_name[x] = tolower(peer_name[x]); 00501 } 00502 00503 if (!silent) { 00504 if (ast_fileexists(peer_name, NULL, NULL) != -1) { 00505 res = ast_streamfile(chan, peer_name, chan->language); 00506 if (!res) 00507 res = ast_waitstream(chan, ""); 00508 if (res) 00509 break; 00510 } else 00511 res = ast_say_character_str(chan, peer_name, "", chan->language); 00512 if ((num=atoi(ptr))) 00513 ast_say_digits(chan, atoi(ptr), "", chan->language); 00514 } 00515 count++; 00516 prev = peer; 00517 res = channel_spy(chan, peer, &volfactor, fd); 00518 if (res == -1) { 00519 break; 00520 } else if (res > 1 && spec) { 00521 snprintf(name, AST_NAME_STRLEN, "%s/%d", spec, res); 00522 if ((peer = local_get_channel_begin_name(name))) { 00523 chosen = 1; 00524 } 00525 continue; 00526 } 00527 } 00528 } 00529 } 00530 if ((peer = local_channel_walk(peer)) == NULL) { 00531 break; 00532 } 00533 } 00534 waitms = count ? 100 : 5000; 00535 } 00536 00537 00538 if (fd > 0) { 00539 close(fd); 00540 } 00541 00542 if (oldrf && ast_set_read_format(chan, oldrf) < 0) { 00543 ast_log(LOG_ERROR, "Could Not Set Read Format.\n"); 00544 } 00545 00546 if (oldwf && ast_set_write_format(chan, oldwf) < 0) { 00547 ast_log(LOG_ERROR, "Could Not Set Write Format.\n"); 00548 } 00549 00550 ast_clear_flag(chan, AST_FLAG_SPYING); 00551 00552 ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0); 00553 00554 ALL_DONE(u, res); 00555 }
|
|
Provides a description of the module.
Definition at line 573 of file app_chanspy.c. References synopsis. 00574 { 00575 return (char *) synopsis; 00576 }
|
|
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 585 of file app_chanspy.c. References ASTERISK_GPL_KEY. 00586 { 00587 return ASTERISK_GPL_KEY; 00588 }
|
|
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 568 of file app_chanspy.c. References app, ast_register_application(), chanspy_exec(), desc, and synopsis. 00569 { 00570 return ast_register_application(app, chanspy_exec, synopsis, desc); 00571 }
|
|
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 chanspy_exec(), and 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. Referenced by chanspy_exec(). 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))) { 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 244 of file app_chanspy.c. References ast_channel_setoption(), AST_OPTION_TXGAIN, and chanspy_translation_helper::volfactor. Referenced by channel_spy(). 00245 { 00246 signed char volume_adjust = volfactor_map[csth->volfactor + 4]; 00247 00248 if (!ast_channel_setoption(chan, AST_OPTION_TXGAIN, &volume_adjust, sizeof(volume_adjust), 0)) 00249 csth->volfactor = 0; 00250 }
|
|
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. 00152 {
00153 /* nothing to do */
00154 }
|
|
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 }
|
|
Definition at line 210 of file app_chanspy.c. References ast_channel_spy_remove(), ast_mutex_lock(), ast_mutex_unlock(), CHANSPY_DONE, ast_channel::lock, and ast_channel_spy::status. Referenced by channel_spy(). 00211 { 00212 /* If our status has changed to DONE, then the channel we're spying on is gone.... 00213 DON'T TOUCH IT!!! RUN AWAY!!! */ 00214 if (spy->status == CHANSPY_DONE) 00215 return; 00216 00217 if (!chan) 00218 return; 00219 00220 ast_mutex_lock(&chan->lock); 00221 ast_channel_spy_remove(chan, spy); 00222 ast_mutex_unlock(&chan->lock); 00223 };
|
|
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 557 of file app_chanspy.c. References app, ast_unregister_application(), and STANDARD_HANGUP_LOCALUSERS. 00558 { 00559 int res; 00560 00561 res = ast_unregister_application(app); 00562 00563 STANDARD_HANGUP_LOCALUSERS; 00564 00565 return res; 00566 }
|
|
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 578 of file app_chanspy.c. References STANDARD_USECOUNT. 00579 { 00580 int res; 00581 STANDARD_USECOUNT(res); 00582 return res; 00583 }
|
|
Definition at line 57 of file app_chanspy.c. |
|
|
|
|
|
Definition at line 82 of file app_chanspy.c. Referenced by channel_spy(). |
|
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. |
|
Definition at line 107 of file app_chanspy.c. |
|
Definition at line 56 of file app_chanspy.c. |
|
Definition at line 228 of file app_chanspy.c. |