/*! * \brief Stop monitoring channel * \param chan * \param need_lock * Stop the recording, close any open streams, mix in/out channels if required * \return Always 0 */ int AST_OPTIONAL_API_NAME(ast_monitor_stop)(struct ast_channel *chan, int need_lock) { int delfiles = 0; LOCK_IF_NEEDED(chan, need_lock); if (ast_channel_monitor(chan)) { char filename[ FILENAME_MAX ]; if (ast_channel_monitor(chan)->read_stream) { ast_closestream(ast_channel_monitor(chan)->read_stream); } if (ast_channel_monitor(chan)->write_stream) { ast_closestream(ast_channel_monitor(chan)->write_stream); } if (ast_channel_monitor(chan)->filename_changed && !ast_strlen_zero(ast_channel_monitor(chan)->filename_base)) { if (ast_channel_monitor(chan)->read_stream) { if (ast_fileexists(ast_channel_monitor(chan)->read_filename,NULL,NULL) > 0) { snprintf(filename, FILENAME_MAX, "%s-in", ast_channel_monitor(chan)->filename_base); if (ast_fileexists(filename, NULL, NULL) > 0) { ast_filedelete(filename, NULL); } ast_filerename(ast_channel_monitor(chan)->read_filename, filename, ast_channel_monitor(chan)->format); } else { ast_log(LOG_WARNING, "File %s not found\n", ast_channel_monitor(chan)->read_filename); } } if (ast_channel_monitor(chan)->write_stream) { if (ast_fileexists(ast_channel_monitor(chan)->write_filename,NULL,NULL) > 0) { snprintf(filename, FILENAME_MAX, "%s-out", ast_channel_monitor(chan)->filename_base); if (ast_fileexists(filename, NULL, NULL) > 0) { ast_filedelete(filename, NULL); } ast_filerename(ast_channel_monitor(chan)->write_filename, filename, ast_channel_monitor(chan)->format); } else { ast_log(LOG_WARNING, "File %s not found\n", ast_channel_monitor(chan)->write_filename); } } } if (ast_channel_monitor(chan)->joinfiles && !ast_strlen_zero(ast_channel_monitor(chan)->filename_base)) { char tmp[1024]; char tmp2[1024]; const char *format = !strcasecmp(ast_channel_monitor(chan)->format,"wav49") ? "WAV" : ast_channel_monitor(chan)->format; char *fname_base = ast_channel_monitor(chan)->filename_base; const char *execute, *execute_args; /* at this point, fname_base really is the full path */ /* Set the execute application */ execute = pbx_builtin_getvar_helper(chan, "MONITOR_EXEC"); if (ast_strlen_zero(execute)) { #ifdef HAVE_SOXMIX execute = "nice -n 19 soxmix"; #else execute = "nice -n 19 sox -m"; #endif format = get_soxmix_format(format); delfiles = 1; } execute_args = pbx_builtin_getvar_helper(chan, "MONITOR_EXEC_ARGS"); if (ast_strlen_zero(execute_args)) { execute_args = ""; } snprintf(tmp, sizeof(tmp), "%s \"%s-in.%s\" \"%s-out.%s\" \"%s.%s\" %s &", execute, fname_base, format, fname_base, format, fname_base, format,execute_args); if (delfiles) { snprintf(tmp2,sizeof(tmp2), "( %s& rm -f \"%s-\"* ) &",tmp, fname_base); /* remove legs when done mixing */ ast_copy_string(tmp, tmp2, sizeof(tmp)); } ast_debug(1,"monitor executing %s\n",tmp); if (ast_safe_system(tmp) == -1) ast_log(LOG_WARNING, "Execute of %s failed.\n",tmp); } ast_free(ast_channel_monitor(chan)->format); ast_free(ast_channel_monitor(chan)); ast_channel_monitor_set(chan, NULL); ast_manager_event(chan, EVENT_FLAG_CALL, "MonitorStop", "Channel: %s\r\n" "Uniqueid: %s\r\n", ast_channel_name(chan), ast_channel_uniqueid(chan) ); pbx_builtin_setvar_helper(chan, "MONITORED", NULL); } pbx_builtin_setvar_helper(chan, "AUTO_MONITOR", NULL); UNLOCK_IF_NEEDED(chan, need_lock); return 0; }
static int common_exec(struct ast_channel *chan, struct ast_flags *flags, int volfactor, const int fd, struct spy_dtmf_options *user_options, const char *mygroup, const char *myenforced, const char *spec, const char *exten, const char *context, const char *mailbox, const char *name_context) { char nameprefix[AST_NAME_STRLEN]; char peer_name[AST_NAME_STRLEN + 5]; char exitcontext[AST_MAX_CONTEXT] = ""; signed char zero_volume = 0; int waitms; int res; char *ptr; int num; int num_spyed_upon = 1; struct ast_channel_iterator *iter = NULL; if (ast_test_flag(flags, OPTION_EXIT)) { const char *c; ast_channel_lock(chan); if ((c = pbx_builtin_getvar_helper(chan, "SPY_EXIT_CONTEXT"))) { ast_copy_string(exitcontext, c, sizeof(exitcontext)); } else if (!ast_strlen_zero(chan->macrocontext)) { ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext)); } else { ast_copy_string(exitcontext, chan->context, sizeof(exitcontext)); } ast_channel_unlock(chan); } if (chan->_state != AST_STATE_UP) ast_answer(chan); ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */ waitms = 100; for (;;) { struct ast_autochan *autochan = NULL, *next_autochan = NULL; struct ast_channel *prev = NULL; if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) { res = ast_streamfile(chan, "beep", chan->language); if (!res) res = ast_waitstream(chan, ""); else if (res < 0) { ast_clear_flag(chan, AST_FLAG_SPYING); break; } if (!ast_strlen_zero(exitcontext)) { char tmp[2]; tmp[0] = res; tmp[1] = '\0'; if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) goto exit; else ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext); } } /* Set up the iterator we'll be using during this call */ if (!ast_strlen_zero(spec)) { iter = ast_channel_iterator_by_name_new(spec, strlen(spec)); } else if (!ast_strlen_zero(exten)) { iter = ast_channel_iterator_by_exten_new(exten, context); } else { iter = ast_channel_iterator_all_new(); } if (!iter) { return -1; } res = ast_waitfordigit(chan, waitms); if (res < 0) { ast_clear_flag(chan, AST_FLAG_SPYING); break; } if (!ast_strlen_zero(exitcontext)) { char tmp[2]; tmp[0] = res; tmp[1] = '\0'; if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) goto exit; else ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext); } /* reset for the next loop around, unless overridden later */ waitms = 100; num_spyed_upon = 0; for (autochan = next_channel(iter, autochan, chan); autochan; prev = autochan->chan, ast_autochan_destroy(autochan), autochan = next_autochan ? next_autochan : next_channel(iter, autochan, chan), next_autochan = NULL) { int igrp = !mygroup; int ienf = !myenforced; char *s; if (autochan->chan == prev) { ast_autochan_destroy(autochan); break; } if (ast_check_hangup(chan)) { ast_autochan_destroy(autochan); break; } if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(autochan->chan)) { continue; } if (ast_check_hangup(autochan->chan) || ast_test_flag(autochan->chan, AST_FLAG_SPYING)) { continue; } if (mygroup) { int num_groups = 0; int num_mygroups = 0; char dup_group[512]; char dup_mygroup[512]; char *groups[NUM_SPYGROUPS]; char *mygroups[NUM_SPYGROUPS]; const char *group = NULL; int x; int y; ast_copy_string(dup_mygroup, mygroup, sizeof(dup_mygroup)); num_mygroups = ast_app_separate_args(dup_mygroup, ':', mygroups, ARRAY_LEN(mygroups)); /* Before dahdi scan was part of chanspy, it would use the "GROUP" variable * rather than "SPYGROUP", this check is done to preserve expected behavior */ if (ast_test_flag(flags, OPTION_DAHDI_SCAN)) { group = pbx_builtin_getvar_helper(autochan->chan, "GROUP"); } else { group = pbx_builtin_getvar_helper(autochan->chan, "SPYGROUP"); } if (!ast_strlen_zero(group)) { ast_copy_string(dup_group, group, sizeof(dup_group)); num_groups = ast_app_separate_args(dup_group, ':', groups, ARRAY_LEN(groups)); } for (y = 0; y < num_mygroups; y++) { for (x = 0; x < num_groups; x++) { if (!strcmp(mygroups[y], groups[x])) { igrp = 1; break; } } } } if (!igrp) { continue; } if (myenforced) { char ext[AST_CHANNEL_NAME + 3]; char buffer[512]; char *end; snprintf(buffer, sizeof(buffer) - 1, ":%s:", myenforced); ast_copy_string(ext + 1, autochan->chan->name, sizeof(ext) - 1); if ((end = strchr(ext, '-'))) { *end++ = ':'; *end = '\0'; } ext[0] = ':'; if (strcasestr(buffer, ext)) { ienf = 1; } } if (!ienf) { continue; } strcpy(peer_name, "spy-"); strncat(peer_name, autochan->chan->name, AST_NAME_STRLEN - 4 - 1); ptr = strchr(peer_name, '/'); *ptr++ = '\0'; ptr = strsep(&ptr, "-"); for (s = peer_name; s < ptr; s++) *s = tolower(*s); if (!ast_test_flag(flags, OPTION_QUIET)) { if (ast_test_flag(flags, OPTION_NAME)) { const char *local_context = S_OR(name_context, "default"); const char *local_mailbox = S_OR(mailbox, ptr); res = ast_app_sayname(chan, local_mailbox, local_context); } if (!ast_test_flag(flags, OPTION_NAME) || res < 0) { if (!ast_test_flag(flags, OPTION_NOTECH)) { if (ast_fileexists(peer_name, NULL, NULL) > 0) { res = ast_streamfile(chan, peer_name, chan->language); if (!res) { res = ast_waitstream(chan, ""); } if (res) { ast_autochan_destroy(autochan); break; } } else { res = ast_say_character_str(chan, peer_name, "", chan->language); } } if ((num = atoi(ptr))) ast_say_digits(chan, atoi(ptr), "", chan->language); } } res = channel_spy(chan, autochan, &volfactor, fd, user_options, flags, exitcontext); num_spyed_upon++; if (res == -1) { ast_autochan_destroy(autochan); goto exit; } else if (res == -2) { res = 0; ast_autochan_destroy(autochan); goto exit; } else if (res > 1 && spec) { struct ast_channel *next; snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res); if ((next = ast_channel_get_by_name_prefix(nameprefix, strlen(nameprefix)))) { next_autochan = ast_autochan_setup(next); next = ast_channel_unref(next); } else { /* stay on this channel, if it is still valid */ if (!ast_check_hangup(autochan->chan)) { next_autochan = ast_autochan_setup(autochan->chan); } else { /* the channel is gone */ next_autochan = NULL; } } } else if (res == 0 && ast_test_flag(flags, OPTION_EXITONHANGUP)) { goto exit; } } iter = ast_channel_iterator_destroy(iter); if (res == -1 || ast_check_hangup(chan)) break; if (ast_test_flag(flags, OPTION_STOP) && !next_autochan) { break; } } exit: ast_clear_flag(chan, AST_FLAG_SPYING); ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0); return res; }
/*! \brief Start monitoring a channel * \param chan ast_channel struct to record * \param format_spec file format to use for recording * \param fname_base filename base to record to * \param need_lock whether to lock the channel mutex * \param stream_action whether to record the input and/or output streams. X_REC_IN | X_REC_OUT is most often used * Creates the file to record, if no format is specified it assumes WAV * It also sets channel variable __MONITORED=yes * \retval 0 on success * \retval -1 on failure */ int AST_OPTIONAL_API_NAME(ast_monitor_start)(struct ast_channel *chan, const char *format_spec, const char *fname_base, int need_lock, int stream_action) { int res = 0; LOCK_IF_NEEDED(chan, need_lock); if (!(ast_channel_monitor(chan))) { struct ast_channel_monitor *monitor; char *channel_name, *p; /* Create monitoring directory if needed */ ast_mkdir(ast_config_AST_MONITOR_DIR, 0777); if (!(monitor = ast_calloc(1, sizeof(*monitor)))) { UNLOCK_IF_NEEDED(chan, need_lock); return -1; } /* Determine file names */ if (!ast_strlen_zero(fname_base)) { int directory = strchr(fname_base, '/') ? 1 : 0; const char *absolute = *fname_base == '/' ? "" : ast_config_AST_MONITOR_DIR; const char *absolute_suffix = *fname_base == '/' ? "" : "/"; snprintf(monitor->read_filename, FILENAME_MAX, "%s%s%s-in", absolute, absolute_suffix, fname_base); snprintf(monitor->write_filename, FILENAME_MAX, "%s%s%s-out", absolute, absolute_suffix, fname_base); snprintf(monitor->filename_base, FILENAME_MAX, "%s%s%s", absolute, absolute_suffix, fname_base); /* try creating the directory just in case it doesn't exist */ if (directory) { char *name = ast_strdupa(monitor->filename_base); ast_mkdir(dirname(name), 0777); } } else { ast_mutex_lock(&monitorlock); snprintf(monitor->read_filename, FILENAME_MAX, "%s/audio-in-%lu", ast_config_AST_MONITOR_DIR, seq); snprintf(monitor->write_filename, FILENAME_MAX, "%s/audio-out-%lu", ast_config_AST_MONITOR_DIR, seq); seq++; ast_mutex_unlock(&monitorlock); /* Replace all '/' chars from the channel name with '-' chars. */ channel_name = ast_strdupa(ast_channel_name(chan)); for (p = channel_name; (p = strchr(p, '/')); ) { *p = '-'; } snprintf(monitor->filename_base, FILENAME_MAX, "%s/%d-%s", ast_config_AST_MONITOR_DIR, (int)time(NULL), channel_name); monitor->filename_changed = 1; } monitor->stop = ast_monitor_stop; /* Determine file format */ if (!ast_strlen_zero(format_spec)) { monitor->format = ast_strdup(format_spec); } else { monitor->format = ast_strdup("wav"); } /* open files */ if (stream_action & X_REC_IN) { if (ast_fileexists(monitor->read_filename, NULL, NULL) > 0) ast_filedelete(monitor->read_filename, NULL); if (!(monitor->read_stream = ast_writefile(monitor->read_filename, monitor->format, NULL, O_CREAT|O_TRUNC|O_WRONLY, 0, AST_FILE_MODE))) { ast_log(LOG_WARNING, "Could not create file %s\n", monitor->read_filename); ast_free(monitor); UNLOCK_IF_NEEDED(chan, need_lock); return -1; } } else monitor->read_stream = NULL; if (stream_action & X_REC_OUT) { if (ast_fileexists(monitor->write_filename, NULL, NULL) > 0) { ast_filedelete(monitor->write_filename, NULL); } if (!(monitor->write_stream = ast_writefile(monitor->write_filename, monitor->format, NULL, O_CREAT|O_TRUNC|O_WRONLY, 0, AST_FILE_MODE))) { ast_log(LOG_WARNING, "Could not create file %s\n", monitor->write_filename); if (monitor->read_stream) { ast_closestream(monitor->read_stream); } ast_free(monitor); UNLOCK_IF_NEEDED(chan, need_lock); return -1; } } else monitor->write_stream = NULL; ast_channel_monitor_set(chan, monitor); ast_monitor_set_state(chan, AST_MONITOR_RUNNING); /* so we know this call has been monitored in case we need to bill for it or something */ pbx_builtin_setvar_helper(chan, "__MONITORED","true"); ast_manager_event(chan, EVENT_FLAG_CALL, "MonitorStart", "Channel: %s\r\n" "Uniqueid: %s\r\n", ast_channel_name(chan), ast_channel_uniqueid(chan)); } else { ast_debug(1,"Cannot start monitoring %s, already monitored\n", ast_channel_name(chan)); res = -1; } UNLOCK_IF_NEEDED(chan, need_lock); return res; }
/* Stop monitoring a channel */ int ast_monitor_stop(struct ast_channel *chan, int need_lock) { int delfiles = 0; LOCK_IF_NEEDED(chan, need_lock); if (chan->monitor) { char filename[ FILENAME_MAX ]; if (chan->monitor->read_stream) { ast_closestream(chan->monitor->read_stream); } if (chan->monitor->write_stream) { ast_closestream(chan->monitor->write_stream); } if (chan->monitor->filename_changed && !ast_strlen_zero(chan->monitor->filename_base)) { if (ast_fileexists(chan->monitor->read_filename,NULL,NULL) > 0) { snprintf(filename, FILENAME_MAX, "%s-in", chan->monitor->filename_base); if (ast_fileexists(filename, NULL, NULL) > 0) { ast_filedelete(filename, NULL); } ast_filerename(chan->monitor->read_filename, filename, chan->monitor->format); } else { ast_log(LOG_WARNING, "File %s not found\n", chan->monitor->read_filename); } if (ast_fileexists(chan->monitor->write_filename,NULL,NULL) > 0) { snprintf(filename, FILENAME_MAX, "%s-out", chan->monitor->filename_base); if (ast_fileexists(filename, NULL, NULL) > 0) { ast_filedelete(filename, NULL); } ast_filerename(chan->monitor->write_filename, filename, chan->monitor->format); } else { ast_log(LOG_WARNING, "File %s not found\n", chan->monitor->write_filename); } } if (chan->monitor->joinfiles && !ast_strlen_zero(chan->monitor->filename_base)) { char tmp[1024]; char tmp2[1024]; char *format = !strcasecmp(chan->monitor->format,"wav49") ? "WAV" : chan->monitor->format; char *name = chan->monitor->filename_base; int directory = strchr(name, '/') ? 1 : 0; char *dir = directory ? "" : ast_config_AST_MONITOR_DIR; const char *execute, *execute_args; /* Set the execute application */ execute = pbx_builtin_getvar_helper(chan, "MONITOR_EXEC"); if (ast_strlen_zero(execute)) { execute = "nice -n 19 soxmix"; delfiles = 1; } execute_args = pbx_builtin_getvar_helper(chan, "MONITOR_EXEC_ARGS"); if (ast_strlen_zero(execute_args)) { execute_args = ""; } snprintf(tmp, sizeof(tmp), "%s \"%s/%s-in.%s\" \"%s/%s-out.%s\" \"%s/%s.%s\" %s &", execute, dir, name, format, dir, name, format, dir, name, format,execute_args); if (delfiles) { snprintf(tmp2,sizeof(tmp2), "( %s& rm -f \"%s/%s-\"* ) &",tmp, dir ,name); /* remove legs when done mixing */ ast_copy_string(tmp, tmp2, sizeof(tmp)); } ast_log(LOG_DEBUG,"monitor executing %s\n",tmp); if (ast_safe_system(tmp) == -1) ast_log(LOG_WARNING, "Execute of %s failed.\n",tmp); } free(chan->monitor->format); free(chan->monitor); chan->monitor = NULL; } UNLOCK_IF_NEEDED(chan, need_lock); return 0; }
struct stasis_app_recording *stasis_app_control_record( struct stasis_app_control *control, struct stasis_app_recording_options *options) { struct stasis_app_recording *recording; char *last_slash; errno = 0; if (options == NULL || ast_strlen_zero(options->name) || ast_strlen_zero(options->format) || options->max_silence_seconds < 0 || options->max_duration_seconds < 0) { errno = EINVAL; return NULL; } ast_debug(3, "%s: Sending record(%s.%s) command\n", stasis_app_control_get_channel_id(control), options->name, options->format); recording = ao2_alloc(sizeof(*recording), recording_dtor); if (!recording) { errno = ENOMEM; return NULL; } recording->duration.total = -1; recording->duration.energy_only = -1; ast_asprintf(&recording->absolute_name, "%s/%s", ast_config_AST_RECORDING_DIR, options->name); if (recording->absolute_name == NULL) { errno = ENOMEM; ao2_ref(recording, -1); return NULL; } if ((last_slash = strrchr(recording->absolute_name, '/'))) { *last_slash = '\0'; if (ast_safe_mkdir(ast_config_AST_RECORDING_DIR, recording->absolute_name, 0777) != 0) { /* errno set by ast_mkdir */ ao2_ref(recording, -1); return NULL; } *last_slash = '/'; } ao2_ref(options, +1); recording->options = options; recording->control = control; recording->state = STASIS_APP_RECORDING_STATE_QUEUED; if ((recording->options->if_exists == AST_RECORD_IF_EXISTS_FAIL) && (ast_fileexists(recording->absolute_name, NULL, NULL))) { ast_log(LOG_WARNING, "Recording file '%s' already exists and ifExists option is failure.\n", recording->absolute_name); errno = EEXIST; ao2_ref(recording, -1); return NULL; } { RAII_VAR(struct stasis_app_recording *, old_recording, NULL, ao2_cleanup); SCOPED_AO2LOCK(lock, recordings); old_recording = ao2_find(recordings, options->name, OBJ_KEY | OBJ_NOLOCK); if (old_recording) { ast_log(LOG_WARNING, "Recording %s already in progress\n", recording->options->name); errno = EEXIST; ao2_ref(recording, -1); return NULL; } ao2_link(recordings, recording); } stasis_app_control_register_add_rule(control, &rule_recording); stasis_app_send_command_async(control, record_file, ao2_bump(recording), recording_cleanup); return recording; }
static int app_exec(struct ast_channel *chan, void *data) { struct ast_module_user *lu; struct playlist_entry *entry; const char *args = data; int child_stdin[2] = { 0,0 }; int child_stdout[2] = { 0,0 }; int child_stderr[2] = { 0,0 }; int res = -1; int test_available_fd = -1; int gen_active = 0; int pid; char *argv[32]; int argc = 1; char *buf, *command; FILE *child_commands = NULL; FILE *child_errors = NULL; FILE *child_events = NULL; struct ivr_localuser foo = { .playlist = AST_LIST_HEAD_INIT_VALUE, .finishlist = AST_LIST_HEAD_INIT_VALUE, }; struct ivr_localuser *u = &foo; sigset_t fullset, oldset; lu = ast_module_user_add(chan); sigfillset(&fullset); pthread_sigmask(SIG_BLOCK, &fullset, &oldset); u->abort_current_sound = 0; u->chan = chan; if (ast_strlen_zero(args)) { ast_log(LOG_WARNING, "ExternalIVR requires a command to execute\n"); ast_module_user_remove(lu); return -1; } buf = ast_strdupa(data); argc = ast_app_separate_args(buf, '|', argv, sizeof(argv) / sizeof(argv[0])); if (pipe(child_stdin)) { ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child input: %s\n", strerror(errno)); goto exit; } if (pipe(child_stdout)) { ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child output: %s\n", strerror(errno)); goto exit; } if (pipe(child_stderr)) { ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child errors: %s\n", strerror(errno)); goto exit; } if (chan->_state != AST_STATE_UP) { ast_answer(chan); } if (ast_activate_generator(chan, &gen, u) < 0) { ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n"); goto exit; } else gen_active = 1; pid = fork(); if (pid < 0) { ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno)); goto exit; } if (!pid) { /* child process */ int i; signal(SIGPIPE, SIG_DFL); pthread_sigmask(SIG_UNBLOCK, &fullset, NULL); if (ast_opt_high_priority) ast_set_priority(0); dup2(child_stdin[0], STDIN_FILENO); dup2(child_stdout[1], STDOUT_FILENO); dup2(child_stderr[1], STDERR_FILENO); for (i = STDERR_FILENO + 1; i < 1024; i++) close(i); execv(argv[0], argv); fprintf(stderr, "Failed to execute '%s': %s\n", argv[0], strerror(errno)); _exit(1); } else { /* parent process */ int child_events_fd = child_stdin[1]; int child_commands_fd = child_stdout[0]; int child_errors_fd = child_stderr[0]; struct ast_frame *f; int ms; int exception; int ready_fd; int waitfds[2] = { child_errors_fd, child_commands_fd }; struct ast_channel *rchan; pthread_sigmask(SIG_SETMASK, &oldset, NULL); close(child_stdin[0]); child_stdin[0] = 0; close(child_stdout[1]); child_stdout[1] = 0; close(child_stderr[1]); child_stderr[1] = 0; if (!(child_events = fdopen(child_events_fd, "w"))) { ast_chan_log(LOG_WARNING, chan, "Could not open stream for child events\n"); goto exit; } if (!(child_commands = fdopen(child_commands_fd, "r"))) { ast_chan_log(LOG_WARNING, chan, "Could not open stream for child commands\n"); goto exit; } if (!(child_errors = fdopen(child_errors_fd, "r"))) { ast_chan_log(LOG_WARNING, chan, "Could not open stream for child errors\n"); goto exit; } test_available_fd = open("/dev/null", O_RDONLY); setvbuf(child_events, NULL, _IONBF, 0); setvbuf(child_commands, NULL, _IONBF, 0); setvbuf(child_errors, NULL, _IONBF, 0); res = 0; while (1) { if (ast_test_flag(chan, AST_FLAG_ZOMBIE)) { ast_chan_log(LOG_NOTICE, chan, "Is a zombie\n"); res = -1; break; } if (ast_check_hangup(chan)) { ast_chan_log(LOG_NOTICE, chan, "Got check_hangup\n"); send_child_event(child_events, 'H', NULL, chan); res = -1; break; } ready_fd = 0; ms = 100; errno = 0; exception = 0; rchan = ast_waitfor_nandfds(&chan, 1, waitfds, 2, &exception, &ready_fd, &ms); if (!AST_LIST_EMPTY(&u->finishlist)) { AST_LIST_LOCK(&u->finishlist); while ((entry = AST_LIST_REMOVE_HEAD(&u->finishlist, list))) { send_child_event(child_events, 'F', entry->filename, chan); free(entry); } AST_LIST_UNLOCK(&u->finishlist); } if (rchan) { /* the channel has something */ f = ast_read(chan); if (!f) { ast_chan_log(LOG_NOTICE, chan, "Returned no frame\n"); send_child_event(child_events, 'H', NULL, chan); res = -1; break; } if (f->frametype == AST_FRAME_DTMF) { send_child_event(child_events, f->subclass, NULL, chan); if (u->option_autoclear) { if (!u->abort_current_sound && !u->playing_silence) send_child_event(child_events, 'T', NULL, chan); AST_LIST_LOCK(&u->playlist); while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) { send_child_event(child_events, 'D', entry->filename, chan); free(entry); } if (!u->playing_silence) u->abort_current_sound = 1; AST_LIST_UNLOCK(&u->playlist); } } else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) { ast_chan_log(LOG_NOTICE, chan, "Got AST_CONTROL_HANGUP\n"); send_child_event(child_events, 'H', NULL, chan); ast_frfree(f); res = -1; break; } ast_frfree(f); } else if (ready_fd == child_commands_fd) { char input[1024]; if (exception || feof(child_commands)) { ast_chan_log(LOG_WARNING, chan, "Child process went away\n"); res = -1; break; } if (!fgets(input, sizeof(input), child_commands)) continue; command = ast_strip(input); ast_chan_log(LOG_DEBUG, chan, "got command '%s'\n", input); if (strlen(input) < 4) continue; if (input[0] == 'S') { if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) { ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]); send_child_event(child_events, 'Z', NULL, chan); strcpy(&input[2], "exception"); } if (!u->abort_current_sound && !u->playing_silence) send_child_event(child_events, 'T', NULL, chan); AST_LIST_LOCK(&u->playlist); while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) { send_child_event(child_events, 'D', entry->filename, chan); free(entry); } if (!u->playing_silence) u->abort_current_sound = 1; entry = make_entry(&input[2]); if (entry) AST_LIST_INSERT_TAIL(&u->playlist, entry, list); AST_LIST_UNLOCK(&u->playlist); } else if (input[0] == 'A') { if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) { ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]); send_child_event(child_events, 'Z', NULL, chan); strcpy(&input[2], "exception"); } entry = make_entry(&input[2]); if (entry) { AST_LIST_LOCK(&u->playlist); AST_LIST_INSERT_TAIL(&u->playlist, entry, list); AST_LIST_UNLOCK(&u->playlist); } } else if (input[0] == 'H') { ast_chan_log(LOG_NOTICE, chan, "Hanging up: %s\n", &input[2]); send_child_event(child_events, 'H', NULL, chan); break; } else if (input[0] == 'O') { if (!strcasecmp(&input[2], "autoclear")) u->option_autoclear = 1; else if (!strcasecmp(&input[2], "noautoclear")) u->option_autoclear = 0; else ast_chan_log(LOG_WARNING, chan, "Unknown option requested '%s'\n", &input[2]); } } else if (ready_fd == child_errors_fd) { char input[1024]; if (exception || (dup2(child_commands_fd, test_available_fd) == -1) || feof(child_errors)) { ast_chan_log(LOG_WARNING, chan, "Child process went away\n"); res = -1; break; } if (fgets(input, sizeof(input), child_errors)) { command = ast_strip(input); ast_chan_log(LOG_NOTICE, chan, "stderr: %s\n", command); } } else if ((ready_fd < 0) && ms) { if (errno == 0 || errno == EINTR) continue; ast_chan_log(LOG_WARNING, chan, "Wait failed (%s)\n", strerror(errno)); break; } } } exit: if (gen_active) ast_deactivate_generator(chan); if (child_events) fclose(child_events); if (child_commands) fclose(child_commands); if (child_errors) fclose(child_errors); if (test_available_fd > -1) { close(test_available_fd); } if (child_stdin[0]) close(child_stdin[0]); if (child_stdin[1]) close(child_stdin[1]); if (child_stdout[0]) close(child_stdout[0]); if (child_stdout[1]) close(child_stdout[1]); if (child_stderr[0]) close(child_stderr[0]); if (child_stderr[1]) close(child_stderr[1]); while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) free(entry); ast_module_user_remove(lu); return res; }
/* Start monitoring a channel */ int ast_monitor_start( struct ast_channel *chan, const char *format_spec, const char *fname_base, int need_lock) { int res = 0; char tmp[256]; LOCK_IF_NEEDED(chan, need_lock); if (!(chan->monitor)) { struct ast_channel_monitor *monitor; char *channel_name, *p; /* Create monitoring directory if needed */ if (mkdir(ast_config_AST_MONITOR_DIR, 0770) < 0) { if (errno != EEXIST) { ast_log(LOG_WARNING, "Unable to create audio monitor directory: %s\n", strerror(errno)); } } if (!(monitor = ast_calloc(1, sizeof(*monitor)))) { UNLOCK_IF_NEEDED(chan, need_lock); return -1; } /* Determine file names */ if (!ast_strlen_zero(fname_base)) { int directory = strchr(fname_base, '/') ? 1 : 0; /* try creating the directory just in case it doesn't exist */ if (directory) { char *name = strdup(fname_base); snprintf(tmp, sizeof(tmp), "mkdir -p \"%s\"",dirname(name)); free(name); ast_safe_system(tmp); } snprintf(monitor->read_filename, FILENAME_MAX, "%s/%s-in", directory ? "" : ast_config_AST_MONITOR_DIR, fname_base); snprintf(monitor->write_filename, FILENAME_MAX, "%s/%s-out", directory ? "" : ast_config_AST_MONITOR_DIR, fname_base); ast_copy_string(monitor->filename_base, fname_base, sizeof(monitor->filename_base)); } else { ast_mutex_lock(&monitorlock); snprintf(monitor->read_filename, FILENAME_MAX, "%s/audio-in-%ld", ast_config_AST_MONITOR_DIR, seq); snprintf(monitor->write_filename, FILENAME_MAX, "%s/audio-out-%ld", ast_config_AST_MONITOR_DIR, seq); seq++; ast_mutex_unlock(&monitorlock); channel_name = ast_strdupa(chan->name); while ((p = strchr(channel_name, '/'))) { *p = '-'; } snprintf(monitor->filename_base, FILENAME_MAX, "%s/%d-%s", ast_config_AST_MONITOR_DIR, (int)time(NULL), channel_name); monitor->filename_changed = 1; } monitor->stop = ast_monitor_stop; /* Determine file format */ if (!ast_strlen_zero(format_spec)) { monitor->format = strdup(format_spec); } else { monitor->format = strdup("wav"); } /* open files */ if (ast_fileexists(monitor->read_filename, NULL, NULL) > 0) { ast_filedelete(monitor->read_filename, NULL); } if (!(monitor->read_stream = ast_writefile(monitor->read_filename, monitor->format, NULL, O_CREAT|O_TRUNC|O_WRONLY, 0, 0644))) { ast_log(LOG_WARNING, "Could not create file %s\n", monitor->read_filename); free(monitor); ast_channel_unlock(chan); return -1; } if (ast_fileexists(monitor->write_filename, NULL, NULL) > 0) { ast_filedelete(monitor->write_filename, NULL); } if (!(monitor->write_stream = ast_writefile(monitor->write_filename, monitor->format, NULL, O_CREAT|O_TRUNC|O_WRONLY, 0, 0644))) { ast_log(LOG_WARNING, "Could not create file %s\n", monitor->write_filename); ast_closestream(monitor->read_stream); free(monitor); ast_channel_unlock(chan); return -1; } chan->monitor = monitor; ast_monitor_set_state(chan, AST_MONITOR_RUNNING); /* so we know this call has been monitored in case we need to bill for it or something */ pbx_builtin_setvar_helper(chan, "__MONITORED","true"); } else { ast_log(LOG_DEBUG,"Cannot start monitoring %s, already monitored\n", chan->name); res = -1; } UNLOCK_IF_NEEDED(chan, need_lock); return res; }
/* play name of mailbox owner. * returns: -1 for bad or missing extension * '1' for selected entry from directory * '*' for skipped entry from directory */ static int play_mailbox_owner(struct ast_channel *chan, char *context, char *dialcontext, char *ext, char *name, int fromappvm) { int res = 0; int loop = 3; char fn[256]; char fn2[256]; /* Check for the VoiceMail2 greeting first */ snprintf(fn, sizeof(fn), "%s/voicemail/%s/%s/greet", (char *)ast_config_AST_SPOOL_DIR, context, ext); #ifdef USE_ODBC_STORAGE retrieve_file(fn); #endif /* Otherwise, check for an old-style Voicemail greeting */ snprintf(fn2, sizeof(fn2), "%s/vm/%s/greet", (char *)ast_config_AST_SPOOL_DIR, ext); #ifdef USE_ODBC_STORAGE retrieve_file(fn2); #endif if (ast_fileexists(fn, NULL, chan->language) > 0) { res = ast_streamfile(chan, fn, chan->language); if (!res) { res = ast_waitstream(chan, AST_DIGIT_ANY); } ast_stopstream(chan); } else if (ast_fileexists(fn2, NULL, chan->language) > 0) { res = ast_streamfile(chan, fn2, chan->language); if (!res) { res = ast_waitstream(chan, AST_DIGIT_ANY); } ast_stopstream(chan); } else { res = ast_say_character_str(chan, !ast_strlen_zero(name) ? name : ext, AST_DIGIT_ANY, chan->language); } #ifdef USE_ODBC_STORAGE ast_filedelete(fn, NULL); ast_filedelete(fn2, NULL); #endif while (loop) { if (!res) { res = ast_streamfile(chan, "dir-instr", chan->language); } if (!res) { res = ast_waitstream(chan, AST_DIGIT_ANY); } if (!res) { res = ast_waitfordigit(chan, 3000); } ast_stopstream(chan); if (res > -1) { switch (res) { case '1': /* Name selected */ loop = 0; if (fromappvm) { /* We still want to set the exten */ ast_copy_string(chan->exten, ext, sizeof(chan->exten)); } else { if (ast_goto_if_exists(chan, dialcontext, ext, 1)) { ast_log(LOG_WARNING, "Can't find extension '%s' in context '%s'. " "Did you pass the wrong context to Directory?\n", ext, dialcontext); res = -1; } } break; case '*': /* Skip to next match in list */ loop = 0; break; default: /* Not '1', or '*', so decrement number of tries */ res = 0; loop--; break; } /* end switch */ } /* end if */ else { /* User hungup, so jump out now */ loop = 0; } } /* end while */ return(res); }
/* play name of mailbox owner. * returns: -1 for bad or missing extension * '1' for selected entry from directory * '*' for skipped entry from directory */ static int play_mailbox_owner(struct ast_channel *chan, char *context, char *dialcontext, char *ext, char *name, int readext, int fromappvm) { int res = 0; int loop; char fn[256]; /* Check for the VoiceMail2 greeting first */ snprintf(fn, sizeof(fn), "%s/voicemail/%s/%s/greet", ast_config_AST_SPOOL_DIR, context, ext); #ifdef ODBC_STORAGE retrieve_file(fn); #endif if (ast_fileexists(fn, NULL, chan->language) <= 0) { /* no file, check for an old-style Voicemail greeting */ snprintf(fn, sizeof(fn), "%s/vm/%s/greet", ast_config_AST_SPOOL_DIR, ext); } #ifdef ODBC_STORAGE retrieve_file(fn); #endif if (ast_fileexists(fn, NULL, chan->language) > 0) { res = ast_stream_and_wait(chan, fn, chan->language, AST_DIGIT_ANY); ast_stopstream(chan); /* If Option 'e' was specified, also read the extension number with the name */ if (readext) { ast_stream_and_wait(chan, "vm-extension", chan->language, AST_DIGIT_ANY); res = ast_say_character_str(chan, ext, AST_DIGIT_ANY, chan->language); } } else { res = ast_say_character_str(chan, S_OR(name, ext), AST_DIGIT_ANY, chan->language); if (!ast_strlen_zero(name) && readext) { ast_stream_and_wait(chan, "vm-extension", chan->language, AST_DIGIT_ANY); res = ast_say_character_str(chan, ext, AST_DIGIT_ANY, chan->language); } } #ifdef ODBC_STORAGE ast_filedelete(fn, NULL); #endif for (loop = 3 ; loop > 0; loop--) { if (!res) res = ast_stream_and_wait(chan, "dir-instr", chan->language, AST_DIGIT_ANY); if (!res) res = ast_waitfordigit(chan, 3000); ast_stopstream(chan); if (res < 0) /* User hungup, so jump out now */ break; if (res == '1') { /* Name selected */ if (fromappvm) { /* We still want to set the exten though */ ast_copy_string(chan->exten, ext, sizeof(chan->exten)); } else { if (ast_goto_if_exists(chan, dialcontext, ext, 1)) { ast_log(LOG_WARNING, "Can't find extension '%s' in context '%s'. " "Did you pass the wrong context to Directory?\n", ext, dialcontext); res = -1; } } break; } if (res == '*') /* Skip to next match in list */ break; /* Not '1', or '*', so decrement number of tries */ res = 0; } return(res); }
static int common_exec(struct ast_channel *chan, const struct ast_flags *flags, int volfactor, const int fd, const char *mygroup, const char *myenforced, const char *spec, const char *exten, const char *context) { char nameprefix[AST_NAME_STRLEN]; char peer_name[AST_NAME_STRLEN + 5]; char exitcontext[AST_MAX_CONTEXT] = ""; signed char zero_volume = 0; int waitms; int res; char *ptr; int num; int num_spyed_upon = 1; struct chanspy_ds chanspy_ds; if (ast_test_flag(flags, OPTION_EXIT)) { const char *c; if ((c = pbx_builtin_getvar_helper(chan, "SPY_EXIT_CONTEXT"))) ast_copy_string(exitcontext, c, sizeof(exitcontext)); else if (!ast_strlen_zero(chan->macrocontext)) ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext)); else ast_copy_string(exitcontext, chan->context, sizeof(exitcontext)); } ast_mutex_init(&chanspy_ds.lock); snprintf(chanspy_ds.unique_id, sizeof(chanspy_ds.unique_id), "%d", ast_atomic_fetchadd_int(&next_unique_id_to_use, +1)); if (chan->_state != AST_STATE_UP) ast_answer(chan); ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */ waitms = 100; for (;;) { struct chanspy_ds *peer_chanspy_ds = NULL, *next_chanspy_ds = NULL; struct ast_channel *prev = NULL, *peer = NULL; if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) { res = ast_streamfile(chan, "beep", chan->language); if (!res) res = ast_waitstream(chan, ""); else if (res < 0) { ast_clear_flag(chan, AST_FLAG_SPYING); break; } if (!ast_strlen_zero(exitcontext)) { char tmp[2]; tmp[0] = res; tmp[1] = '\0'; if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) goto exit; else ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext); } } res = ast_waitfordigit(chan, waitms); if (res < 0) { ast_clear_flag(chan, AST_FLAG_SPYING); break; } if (!ast_strlen_zero(exitcontext)) { char tmp[2]; tmp[0] = res; tmp[1] = '\0'; if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) goto exit; else ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext); } /* reset for the next loop around, unless overridden later */ waitms = 100; num_spyed_upon = 0; for (peer_chanspy_ds = next_channel(chan, prev, spec, exten, context, &chanspy_ds); peer_chanspy_ds; chanspy_ds_free(peer_chanspy_ds), prev = peer, peer_chanspy_ds = next_chanspy_ds ? next_chanspy_ds : next_channel(chan, prev, spec, exten, context, &chanspy_ds), next_chanspy_ds = NULL) { const char *group; int igrp = !mygroup; char *groups[25]; int num_groups = 0; char dup_group[512]; int x; char *s; char *buffer; char *end; char *ext; char *form_enforced; int ienf = !myenforced; peer = peer_chanspy_ds->chan; ast_mutex_unlock(&peer_chanspy_ds->lock); if (peer == prev) { ast_channel_unlock(peer); chanspy_ds_free(peer_chanspy_ds); break; } if (ast_check_hangup(chan)) { ast_channel_unlock(peer); chanspy_ds_free(peer_chanspy_ds); break; } if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer)) { ast_channel_unlock(peer); continue; } if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING)) { ast_channel_unlock(peer); continue; } if (mygroup) { if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) { ast_copy_string(dup_group, group, sizeof(dup_group)); num_groups = ast_app_separate_args(dup_group, ':', groups, ARRAY_LEN(groups)); } for (x = 0; x < num_groups; x++) { if (!strcmp(mygroup, groups[x])) { igrp = 1; break; } } } if (!igrp) { ast_channel_unlock(peer); continue; } if (myenforced) { /* We don't need to allocate more space than just the length of (peer->name) for ext as we will cut the channel name's ending before copying into ext */ ext = alloca(strlen(peer->name)); form_enforced = alloca(strlen(myenforced) + 3); strcpy(form_enforced, ":"); strcat(form_enforced, myenforced); strcat(form_enforced, ":"); buffer = ast_strdupa(peer->name); if ((end = strchr(buffer, '-'))) { *end++ = ':'; *end = '\0'; } strcpy(ext, ":"); strcat(ext, buffer); if (strcasestr(form_enforced, ext)) ienf = 1; } if (!ienf) continue; strcpy(peer_name, "spy-"); strncat(peer_name, peer->name, AST_NAME_STRLEN - 4 - 1); ptr = strchr(peer_name, '/'); *ptr++ = '\0'; for (s = peer_name; s < ptr; s++) *s = tolower(*s); /* We have to unlock the peer channel here to avoid a deadlock. * So, when we need to dereference it again, we have to lock the * datastore and get the pointer from there to see if the channel * is still valid. */ ast_channel_unlock(peer); if (!ast_test_flag(flags, OPTION_QUIET)) { if (ast_fileexists(peer_name, NULL, NULL) != -1) { res = ast_streamfile(chan, peer_name, chan->language); if (!res) res = ast_waitstream(chan, ""); if (res) { chanspy_ds_free(peer_chanspy_ds); break; } } else res = ast_say_character_str(chan, peer_name, "", chan->language); if ((num = atoi(ptr))) ast_say_digits(chan, atoi(ptr), "", chan->language); } res = channel_spy(chan, peer_chanspy_ds, &volfactor, fd, flags, exitcontext); num_spyed_upon++; if (res == -1) { chanspy_ds_free(peer_chanspy_ds); goto exit; } else if (res == -2) { res = 0; chanspy_ds_free(peer_chanspy_ds); goto exit; } else if (res > 1 && spec) { struct ast_channel *next; snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res); if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) { peer_chanspy_ds = chanspy_ds_free(peer_chanspy_ds); next_chanspy_ds = setup_chanspy_ds(next, &chanspy_ds); } else { /* stay on this channel, if it is still valid */ ast_mutex_lock(&peer_chanspy_ds->lock); if (peer_chanspy_ds->chan) { ast_channel_lock(peer_chanspy_ds->chan); next_chanspy_ds = peer_chanspy_ds; peer_chanspy_ds = NULL; } else { /* the channel is gone */ ast_mutex_unlock(&peer_chanspy_ds->lock); next_chanspy_ds = NULL; } } peer = NULL; } } if (res == -1 || ast_check_hangup(chan)) break; } exit: ast_clear_flag(chan, AST_FLAG_SPYING); ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0); ast_mutex_lock(&chanspy_ds.lock); ast_mutex_unlock(&chanspy_ds.lock); ast_mutex_destroy(&chanspy_ds.lock); return res; }
static int chanspy_exec(struct ast_channel *chan, void *data) { struct localuser *u; struct ast_channel *peer=NULL, *prev=NULL; char name[AST_NAME_STRLEN], peer_name[AST_NAME_STRLEN + 5], *args, *ptr = NULL, *options = NULL, *spec = NULL, *argv[5], *mygroup = NULL, *recbase = NULL; int res = -1, volfactor = 0, silent = 0, argc = 0, bronly = 0, chosen = 0, count=0, waitms = 100, num = 0, oldrf = 0, oldwf = 0, fd = 0; struct ast_flags flags; signed char zero_volume = 0; if (!(args = ast_strdupa((char *)data))) { ast_log(LOG_ERROR, "Out of memory!\n"); return -1; } LOCAL_USER_ADD(u); oldrf = chan->readformat; oldwf = chan->writeformat; if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) { ast_log(LOG_ERROR, "Could Not Set Read Format.\n"); LOCAL_USER_REMOVE(u); return -1; } if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) { ast_log(LOG_ERROR, "Could Not Set Write Format.\n"); LOCAL_USER_REMOVE(u); return -1; } ast_answer(chan); ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */ if ((argc = ast_app_separate_args(args, '|', argv, sizeof(argv) / sizeof(argv[0])))) { spec = argv[0]; if ( argc > 1) { options = argv[1]; } if (ast_strlen_zero(spec) || !strcmp(spec, "all")) { spec = NULL; } } if (options) { char *opts[OPT_ARG_ARRAY_SIZE]; ast_app_parse_options(chanspy_opts, &flags, opts, options); if (ast_test_flag(&flags, OPTION_GROUP)) { mygroup = opts[OPT_ARG_GROUP]; } if (ast_test_flag(&flags, OPTION_RECORD)) { if (!(recbase = opts[OPT_ARG_RECORD])) { recbase = "chanspy"; } } silent = ast_test_flag(&flags, OPTION_QUIET); bronly = ast_test_flag(&flags, OPTION_BRIDGED); if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) { int vol; if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4)) ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n"); else volfactor = vol; } } else ast_clear_flag(&flags, AST_FLAGS_ALL); if (recbase) { char filename[512]; snprintf(filename,sizeof(filename),"%s/%s.%d.raw",ast_config_AST_MONITOR_DIR, recbase, (int)time(NULL)); if ((fd = open(filename, O_CREAT | O_WRONLY, O_TRUNC, 0644)) <= 0) { ast_log(LOG_WARNING, "Cannot open %s for recording\n", filename); fd = 0; } } for(;;) { if (!silent) { res = ast_streamfile(chan, "beep", chan->language); if (!res) res = ast_waitstream(chan, ""); if (res < 0) { ast_clear_flag(chan, AST_FLAG_SPYING); break; } } count = 0; res = ast_waitfordigit(chan, waitms); if (res < 0) { ast_clear_flag(chan, AST_FLAG_SPYING); break; } peer = local_channel_walk(NULL); prev=NULL; while(peer) { if (peer != chan) { char *group = NULL; int igrp = 1; if (peer == prev && !chosen) { break; } chosen = 0; group = pbx_builtin_getvar_helper(peer, "SPYGROUP"); if (mygroup) { if (!group || strcmp(mygroup, group)) { igrp = 0; } } if (igrp && (!spec || ((strlen(spec) <= strlen(peer->name) && !strncasecmp(peer->name, spec, strlen(spec)))))) { if (peer && (!bronly || ast_bridged_channel(peer)) && !ast_check_hangup(peer) && !ast_test_flag(peer, AST_FLAG_SPYING)) { int x = 0; strncpy(peer_name, "spy-", 5); strncpy(peer_name + strlen(peer_name), peer->name, AST_NAME_STRLEN); ptr = strchr(peer_name, '/'); *ptr = '\0'; ptr++; for (x = 0 ; x < strlen(peer_name) ; x++) { if (peer_name[x] == '/') { break; } peer_name[x] = tolower(peer_name[x]); } if (!silent) { if (ast_fileexists(peer_name, NULL, NULL) != -1) { res = ast_streamfile(chan, peer_name, chan->language); if (!res) res = ast_waitstream(chan, ""); if (res) break; } else res = ast_say_character_str(chan, peer_name, "", chan->language); if ((num=atoi(ptr))) ast_say_digits(chan, atoi(ptr), "", chan->language); } count++; prev = peer; res = channel_spy(chan, peer, &volfactor, fd); if (res == -1) { break; } else if (res > 1 && spec) { snprintf(name, AST_NAME_STRLEN, "%s/%d", spec, res); if ((peer = local_get_channel_begin_name(name))) { chosen = 1; } continue; } } } } if ((peer = local_channel_walk(peer)) == NULL) { break; } } waitms = count ? 100 : 5000; } if (fd > 0) { close(fd); } if (oldrf && ast_set_read_format(chan, oldrf) < 0) { ast_log(LOG_ERROR, "Could Not Set Read Format.\n"); } if (oldwf && ast_set_write_format(chan, oldwf) < 0) { ast_log(LOG_ERROR, "Could Not Set Write Format.\n"); } ast_clear_flag(chan, AST_FLAG_SPYING); ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0); ALL_DONE(u, res); }
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) { char nameprefix[AST_NAME_STRLEN]; char peer_name[AST_NAME_STRLEN + 5]; signed char zero_volume = 0; int waitms; int res; char *ptr; int num; int num_spyed_upon = 1; struct chanspy_ds chanspy_ds = { 0, }; ast_mutex_init(&chanspy_ds.lock); snprintf(chanspy_ds.unique_id, sizeof(chanspy_ds.unique_id), "%d", ast_atomic_fetchadd_int(&next_unique_id_to_use, +1)); if (chan->_state != AST_STATE_UP) ast_answer(chan); ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */ waitms = 100; for (;;) { struct chanspy_ds *peer_chanspy_ds = NULL, *next_chanspy_ds = NULL; struct ast_channel *prev = NULL, *peer = NULL; if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) { res = ast_streamfile(chan, "beep", chan->language); if (!res) res = ast_waitstream(chan, ""); else if (res < 0) { ast_clear_flag(chan, AST_FLAG_SPYING); break; } } res = ast_waitfordigit(chan, waitms); if (res < 0) { ast_clear_flag(chan, AST_FLAG_SPYING); break; } /* reset for the next loop around, unless overridden later */ waitms = 100; num_spyed_upon = 0; for (peer_chanspy_ds = next_channel(chan, prev, spec, exten, context, &chanspy_ds); peer_chanspy_ds; chanspy_ds_free(peer_chanspy_ds), prev = peer, peer_chanspy_ds = next_chanspy_ds ? next_chanspy_ds : next_channel(chan, prev, spec, exten, context, &chanspy_ds), next_chanspy_ds = NULL) { const char *group; int igrp = !mygroup; char *groups[25]; int num_groups = 0; char dup_group[512]; int x; char *s; peer = peer_chanspy_ds->chan; ast_mutex_unlock(&peer_chanspy_ds->lock); if (peer == prev) { ast_channel_unlock(peer); chanspy_ds_free(peer_chanspy_ds); break; } if (ast_check_hangup(chan)) { ast_channel_unlock(peer); chanspy_ds_free(peer_chanspy_ds); break; } if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer)) { ast_channel_unlock(peer); continue; } if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING)) { ast_channel_unlock(peer); continue; } if (mygroup) { if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) { ast_copy_string(dup_group, group, sizeof(dup_group)); num_groups = ast_app_separate_args(dup_group, ':', groups, sizeof(groups) / sizeof(groups[0])); } for (x = 0; x < num_groups; x++) { if (!strcmp(mygroup, groups[x])) { igrp = 1; break; } } } if (!igrp) { ast_channel_unlock(peer); continue; } strcpy(peer_name, "spy-"); strncat(peer_name, peer->name, AST_NAME_STRLEN - 4 - 1); ptr = strchr(peer_name, '/'); *ptr++ = '\0'; for (s = peer_name; s < ptr; s++) *s = tolower(*s); /* We have to unlock the peer channel here to avoid a deadlock. * So, when we need to dereference it again, we have to lock the * datastore and get the pointer from there to see if the channel * is still valid. */ ast_channel_unlock(peer); if (!ast_test_flag(flags, OPTION_QUIET)) { if (ast_fileexists(peer_name, NULL, NULL) != -1) { res = ast_streamfile(chan, peer_name, chan->language); if (!res) res = ast_waitstream(chan, ""); if (res) { chanspy_ds_free(peer_chanspy_ds); break; } } else res = ast_say_character_str(chan, peer_name, "", chan->language); if ((num = atoi(ptr))) ast_say_digits(chan, atoi(ptr), "", chan->language); } res = channel_spy(chan, peer_chanspy_ds, &volfactor, fd, flags); num_spyed_upon++; if (res == -1) { chanspy_ds_free(peer_chanspy_ds); break; } else if (res > 1 && spec) { struct ast_channel *next; snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res); if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) { peer_chanspy_ds = chanspy_ds_free(peer_chanspy_ds); next_chanspy_ds = setup_chanspy_ds(next, &chanspy_ds); } else { /* stay on this channel, if it is still valid */ ast_mutex_lock(&peer_chanspy_ds->lock); if (peer_chanspy_ds->chan) { ast_channel_lock(peer_chanspy_ds->chan); next_chanspy_ds = peer_chanspy_ds; peer_chanspy_ds = NULL; } else { /* the channel is gone */ ast_mutex_unlock(&peer_chanspy_ds->lock); next_chanspy_ds = NULL; } } peer = NULL; } } if (res == -1 || ast_check_hangup(chan)) break; } ast_clear_flag(chan, AST_FLAG_SPYING); ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0); ast_mutex_lock(&chanspy_ds.lock); ast_mutex_unlock(&chanspy_ds.lock); ast_mutex_destroy(&chanspy_ds.lock); return res; }
static int record_exec(struct ast_channel *chan, void *data) { int res = 0; int count = 0; int percentflag = 0; char *filename, *ext = NULL, *silstr, *maxstr, *options; char *vdata, *p; int i = 0; char tmp[256]; struct ast_filestream *s = '\0'; struct ast_module_user *u; struct ast_frame *f = NULL; struct ast_dsp *sildet = NULL; /* silence detector dsp */ int totalsilence = 0; int dspsilence = 0; int silence = 0; /* amount of silence to allow */ int gotsilence = 0; /* did we timeout for silence? */ int maxduration = 0; /* max duration of recording in milliseconds */ int gottimeout = 0; /* did we timeout for maxduration exceeded? */ int option_skip = 0; int option_noanswer = 0; int option_append = 0; int terminator = '#'; int option_quiet = 0; int rfmt = 0; int flags; int waitres; struct ast_silence_generator *silgen = NULL; /* The next few lines of code parse out the filename and header from the input string */ if (ast_strlen_zero(data)) { /* no data implies no filename or anything is present */ ast_log(LOG_WARNING, "Record requires an argument (filename)\n"); return -1; } u = ast_module_user_add(chan); /* Yay for strsep being easy */ vdata = ast_strdupa(data); p = vdata; filename = strsep(&p, "|"); silstr = strsep(&p, "|"); maxstr = strsep(&p, "|"); options = strsep(&p, "|"); if (filename) { if (strstr(filename, "%d")) percentflag = 1; ext = strrchr(filename, '.'); /* to support filename with a . in the filename, not format */ if (!ext) ext = strchr(filename, ':'); if (ext) { *ext = '\0'; ext++; } } if (!ext) { ast_log(LOG_WARNING, "No extension specified to filename!\n"); ast_module_user_remove(u); return -1; } if (silstr) { if ((sscanf(silstr, "%d", &i) == 1) && (i > -1)) { silence = i * 1000; } else if (!ast_strlen_zero(silstr)) { ast_log(LOG_WARNING, "'%s' is not a valid silence duration\n", silstr); } } if (maxstr) { if ((sscanf(maxstr, "%d", &i) == 1) && (i > -1)) /* Convert duration to milliseconds */ maxduration = i * 1000; else if (!ast_strlen_zero(maxstr)) ast_log(LOG_WARNING, "'%s' is not a valid maximum duration\n", maxstr); } if (options) { /* Retain backwards compatibility with old style options */ if (!strcasecmp(options, "skip")) option_skip = 1; else if (!strcasecmp(options, "noanswer")) option_noanswer = 1; else { if (strchr(options, 's')) option_skip = 1; if (strchr(options, 'n')) option_noanswer = 1; if (strchr(options, 'a')) option_append = 1; if (strchr(options, 't')) terminator = '*'; if (strchr(options, 'x')) terminator = 0; if (strchr(options, 'q')) option_quiet = 1; } } /* done parsing */ /* these are to allow the use of the %d in the config file for a wild card of sort to create a new file with the inputed name scheme */ if (percentflag) { AST_DECLARE_APP_ARGS(fname, AST_APP_ARG(piece)[100]; ); char *tmp2 = ast_strdupa(filename); char countstring[15]; int i; /* Separate each piece out by the format specifier */ AST_NONSTANDARD_APP_ARGS(fname, tmp2, '%'); do { int tmplen; /* First piece has no leading percent, so it's copied verbatim */ ast_copy_string(tmp, fname.piece[0], sizeof(tmp)); tmplen = strlen(tmp); for (i = 1; i < fname.argc; i++) { if (fname.piece[i][0] == 'd') { /* Substitute the count */ snprintf(countstring, sizeof(countstring), "%d", count); ast_copy_string(tmp + tmplen, countstring, sizeof(tmp) - tmplen); tmplen += strlen(countstring); } else if (tmplen + 2 < sizeof(tmp)) { /* Unknown format specifier - just copy it verbatim */ tmp[tmplen++] = '%'; tmp[tmplen++] = fname.piece[i][0]; } /* Copy the remaining portion of the piece */ ast_copy_string(tmp + tmplen, &(fname.piece[i][1]), sizeof(tmp) - tmplen); } count++; } while (ast_fileexists(tmp, ext, chan->language) > 0); pbx_builtin_setvar_helper(chan, "RECORDED_FILE", tmp); } else
static int record_exec(struct ast_channel *chan, void *data) { int res = 0; int count = 0; int percentflag = 0; char *filename, *ext = NULL, *silstr, *maxstr, *options; char *vdata, *p; int i = 0; char tmp[256]; struct ast_filestream *s = '\0'; struct localuser *u; struct ast_frame *f = NULL; struct ast_dsp *sildet = NULL; /* silence detector dsp */ int totalsilence = 0; int dspsilence = 0; int silence = 0; /* amount of silence to allow */ int gotsilence = 0; /* did we timeout for silence? */ int maxduration = 0; /* max duration of recording in milliseconds */ int gottimeout = 0; /* did we timeout for maxduration exceeded? */ int option_skip = 0; int option_noanswer = 0; int option_append = 0; int terminator = '#'; int option_quiet = 0; int rfmt = 0; int flags; int waitres; struct ast_silence_generator *silgen = NULL; /* The next few lines of code parse out the filename and header from the input string */ if (ast_strlen_zero(data)) { /* no data implies no filename or anything is present */ ast_log(LOG_WARNING, "Record requires an argument (filename)\n"); return -1; } LOCAL_USER_ADD(u); /* Yay for strsep being easy */ vdata = ast_strdupa(data); if (!vdata) { ast_log(LOG_ERROR, "Out of memory\n"); LOCAL_USER_REMOVE(u); return -1; } p = vdata; filename = strsep(&p, "|"); silstr = strsep(&p, "|"); maxstr = strsep(&p, "|"); options = strsep(&p, "|"); if (filename) { if (strstr(filename, "%d")) percentflag = 1; ext = strrchr(filename, '.'); /* to support filename with a . in the filename, not format */ if (!ext) ext = strchr(filename, ':'); if (ext) { *ext = '\0'; ext++; } } if (!ext) { ast_log(LOG_WARNING, "No extension specified to filename!\n"); LOCAL_USER_REMOVE(u); return -1; } if (silstr) { if ((sscanf(silstr, "%d", &i) == 1) && (i > -1)) { silence = i * 1000; } else if (!ast_strlen_zero(silstr)) { ast_log(LOG_WARNING, "'%s' is not a valid silence duration\n", silstr); } } if (maxstr) { if ((sscanf(maxstr, "%d", &i) == 1) && (i > -1)) /* Convert duration to milliseconds */ maxduration = i * 1000; else if (!ast_strlen_zero(maxstr)) ast_log(LOG_WARNING, "'%s' is not a valid maximum duration\n", maxstr); } if (options) { /* Retain backwards compatibility with old style options */ if (!strcasecmp(options, "skip")) option_skip = 1; else if (!strcasecmp(options, "noanswer")) option_noanswer = 1; else { if (strchr(options, 's')) option_skip = 1; if (strchr(options, 'n')) option_noanswer = 1; if (strchr(options, 'a')) option_append = 1; if (strchr(options, 't')) terminator = '*'; if (strchr(options, 'q')) option_quiet = 1; } } /* done parsing */ /* these are to allow the use of the %d in the config file for a wild card of sort to create a new file with the inputed name scheme */ if (percentflag) { do { snprintf(tmp, sizeof(tmp), filename, count); count++; } while ( ast_fileexists(tmp, ext, chan->language) != -1 ); pbx_builtin_setvar_helper(chan, "RECORDED_FILE", tmp); } else strncpy(tmp, filename, sizeof(tmp)-1); /* end of routine mentioned */ if (chan->_state != AST_STATE_UP) { if (option_skip) { /* At the user's option, skip if the line is not up */ LOCAL_USER_REMOVE(u); return 0; } else if (!option_noanswer) { /* Otherwise answer unless we're supposed to record while on-hook */ res = ast_answer(chan); } } if (res) { ast_log(LOG_WARNING, "Could not answer channel '%s'\n", chan->name); goto out; } if (!option_quiet) { /* Some code to play a nice little beep to signify the start of the record operation */ res = ast_streamfile(chan, "beep", chan->language); if (!res) { res = ast_waitstream(chan, ""); } else { ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", chan->name); } ast_stopstream(chan); } /* The end of beep code. Now the recording starts */ if (silence > 0) { rfmt = chan->readformat; res = ast_set_read_format(chan, AST_FORMAT_SLINEAR); if (res < 0) { ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n"); LOCAL_USER_REMOVE(u); return -1; } sildet = ast_dsp_new(); if (!sildet) { ast_log(LOG_WARNING, "Unable to create silence detector :(\n"); LOCAL_USER_REMOVE(u); return -1; } ast_dsp_set_threshold(sildet, 256); } flags = option_append ? O_CREAT|O_APPEND|O_WRONLY : O_CREAT|O_TRUNC|O_WRONLY; s = ast_writefile( tmp, ext, NULL, flags , 0, 0644); if (!s) { ast_log(LOG_WARNING, "Could not create file %s\n", filename); goto out; } if (option_transmit_silence_during_record) silgen = ast_channel_start_silence_generator(chan); /* Request a video update */ ast_indicate(chan, AST_CONTROL_VIDUPDATE); if (maxduration <= 0) maxduration = -1; while ((waitres = ast_waitfor(chan, maxduration)) > -1) { if (maxduration > 0) { if (waitres == 0) { gottimeout = 1; break; } maxduration = waitres; } f = ast_read(chan); if (!f) { res = -1; break; } if (f->frametype == AST_FRAME_VOICE) { res = ast_writestream(s, f); if (res) { ast_log(LOG_WARNING, "Problem writing frame\n"); ast_frfree(f); break; } if (silence > 0) { dspsilence = 0; ast_dsp_silence(sildet, f, &dspsilence); if (dspsilence) { totalsilence = dspsilence; } else { totalsilence = 0; } if (totalsilence > silence) { /* Ended happily with silence */ ast_frfree(f); gotsilence = 1; break; } } } else if (f->frametype == AST_FRAME_VIDEO) { res = ast_writestream(s, f); if (res) { ast_log(LOG_WARNING, "Problem writing frame\n"); ast_frfree(f); break; } } else if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == terminator)) { ast_frfree(f); break; } ast_frfree(f); } if (!f) { ast_log(LOG_DEBUG, "Got hangup\n"); res = -1; } if (gotsilence) { ast_stream_rewind(s, silence-1000); ast_truncstream(s); } else if (!gottimeout) { /* Strip off the last 1/4 second of it */ ast_stream_rewind(s, 250); ast_truncstream(s); } ast_closestream(s); if (silgen) ast_channel_stop_silence_generator(chan, silgen); out: if ((silence > 0) && rfmt) { res = ast_set_read_format(chan, rfmt); if (res) ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name); if (sildet) ast_dsp_free(sildet); } LOCAL_USER_REMOVE(u); return res; }