#include "asterisk.h"
#include <signal.h>
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/linkedlists.h"
#include "asterisk/app.h"
#include "asterisk/utils.h"
#include "asterisk/tcptls.h"
#include "asterisk/astobj2.h"
Go to the source code of this file.
Data Structures | |
struct | gen_state |
struct | ivr_localuser |
struct | ivr_localuser::finishlist |
struct | ivr_localuser::playlist |
struct | playlist_entry |
Defines | |
#define | ast_chan_log(level, channel, format,) ast_log(level, "%s: " format, channel->name , ## __VA_ARGS__) |
Enumerations | |
enum | { noanswer = (1 << 0), ignore_hangup = (1 << 1), run_dead = (1 << 2) } |
Functions | |
static void | __reg_module (void) |
static void | __unreg_module (void) |
static int | app_exec (struct ast_channel *chan, void *data) |
static void | ast_eivr_getvariable (struct ast_channel *chan, char *data, char *outbuf, int outbuflen) |
static void | ast_eivr_setvariable (struct ast_channel *chan, char *data) |
static int | eivr_comm (struct ast_channel *chan, struct ivr_localuser *u, int eivr_events_fd, int eivr_commands_fd, int eivr_errors_fd, const struct ast_str *args, const struct ast_flags flags) |
int | eivr_connect_socket (struct ast_channel *chan, const char *host, int port) |
static void * | gen_alloc (struct ast_channel *chan, void *params) |
static void | gen_closestream (struct gen_state *state) |
static int | gen_generate (struct ast_channel *chan, void *data, int len, int samples) |
static int | gen_nextfile (struct gen_state *state) |
static struct ast_frame * | gen_readframe (struct gen_state *state) |
static void | gen_release (struct ast_channel *chan, void *data) |
static int | load_module (void) |
static struct playlist_entry * | make_entry (const char *filename) |
static void | send_eivr_event (FILE *handle, const char event, const char *data, const struct ast_channel *chan) |
static int | unload_module (void) |
Variables | |
static struct ast_module_info | __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "External IVR Interface Application" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "a9c98e5d177805051735cb5b0b16b0a0" , .load = load_module, .unload = unload_module, } |
static const char * | app = "ExternalIVR" |
static struct ast_app_option | app_opts [128] = { [ 'n' ] = { .flag = noanswer }, [ 'i' ] = { .flag = ignore_hangup }, [ 'd' ] = { .flag = run_dead },} |
static struct ast_module_info * | ast_module_info = &__mod_info |
static const char * | descrip |
static struct ast_generator | gen |
enum { ... } | options_flags |
static const char * | synopsis = "Interfaces with an external IVR application" |
Definition in file app_externalivr.c.
#define ast_chan_log | ( | level, | |||
channel, | |||||
format | ) | ast_log(level, "%s: " format, channel->name , ## __VA_ARGS__) |
Definition at line 72 of file app_externalivr.c.
Referenced by app_exec(), eivr_comm(), gen_generate(), and gen_nextfile().
anonymous enum |
Definition at line 74 of file app_externalivr.c.
00074 { 00075 noanswer = (1 << 0), 00076 ignore_hangup = (1 << 1), 00077 run_dead = (1 << 2), 00078 } options_flags;
static void __reg_module | ( | void | ) | [static] |
Definition at line 814 of file app_externalivr.c.
static void __unreg_module | ( | void | ) | [static] |
Definition at line 814 of file app_externalivr.c.
static int app_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 323 of file app_externalivr.c.
References ast_channel::_state, ivr_localuser::abort_current_sound, ast_activate_generator(), ast_answer(), AST_APP_ARG, ast_app_parse_options(), ast_chan_log, ast_debug, AST_DECLARE_APP_ARGS, AST_LIST_HEAD_INIT_VALUE, ast_log(), AST_STANDARD_APP_ARGS, AST_STATE_UP, ast_str_append(), ast_str_create(), ast_str_reset(), ast_strdupa, ast_strlen_zero(), ast_test_flag, buf, chan, ivr_localuser::chan, ast_flags::flags, gen, ivr_localuser::gen_active, hostname, ignore_hangup, LOG_ERROR, LOG_WARNING, noanswer, option_debug, ivr_localuser::playlist, run_dead, and s.
00324 { 00325 struct ast_flags flags = { 0, }; 00326 char *opts[0]; 00327 struct playlist_entry *entry; 00328 int child_stdin[2] = { 0, 0 }; 00329 int child_stdout[2] = { 0, 0 }; 00330 int child_stderr[2] = { 0, 0 }; 00331 int res = -1; 00332 int pid; 00333 00334 char hostname[1024]; 00335 char *port_str = NULL; 00336 int port = 0; 00337 struct ast_tcptls_session_instance *ser = NULL; 00338 00339 struct ivr_localuser foo = { 00340 .playlist = AST_LIST_HEAD_INIT_VALUE, 00341 .finishlist = AST_LIST_HEAD_INIT_VALUE, 00342 .gen_active = 0, 00343 }; 00344 struct ivr_localuser *u = &foo; 00345 00346 char *buf; 00347 int j; 00348 char *s, **app_args, *e; 00349 struct ast_str *pipe_delim_args = ast_str_create(100); 00350 00351 AST_DECLARE_APP_ARGS(eivr_args, 00352 AST_APP_ARG(cmd)[32]; 00353 ); 00354 AST_DECLARE_APP_ARGS(application_args, 00355 AST_APP_ARG(cmd)[32]; 00356 ); 00357 00358 u->abort_current_sound = 0; 00359 u->chan = chan; 00360 00361 buf = ast_strdupa(data); 00362 AST_STANDARD_APP_ARGS(eivr_args, buf); 00363 00364 if ((s = strchr(eivr_args.cmd[0], '('))) { 00365 s[0] = ','; 00366 if (( e = strrchr(s, ')')) ) { 00367 *e = '\0'; 00368 } else { 00369 ast_log(LOG_ERROR, "Parse error, no closing paren?\n"); 00370 } 00371 AST_STANDARD_APP_ARGS(application_args, eivr_args.cmd[0]); 00372 app_args = application_args.argv; 00373 00374 /* Put the application + the arguments in a | delimited list */ 00375 ast_str_reset(pipe_delim_args); 00376 for (j = 0; application_args.cmd[j] != NULL; j++) { 00377 ast_str_append(&pipe_delim_args, 0, "%s%s", j == 0 ? "" : ",", application_args.cmd[j]); 00378 } 00379 00380 /* Parse the ExternalIVR() arguments */ 00381 if (option_debug) 00382 ast_debug(1, "Parsing options from: [%s]\n", eivr_args.cmd[1]); 00383 ast_app_parse_options(app_opts, &flags, opts, eivr_args.cmd[1]); 00384 if (option_debug) { 00385 if (ast_test_flag(&flags, noanswer)) 00386 ast_debug(1, "noanswer is set\n"); 00387 if (ast_test_flag(&flags, ignore_hangup)) 00388 ast_debug(1, "ignore_hangup is set\n"); 00389 if (ast_test_flag(&flags, run_dead)) 00390 ast_debug(1, "run_dead is set\n"); 00391 } 00392 00393 } else { 00394 app_args = eivr_args.argv; 00395 for (j = 0; eivr_args.cmd[j] != NULL; j++) { 00396 ast_str_append(&pipe_delim_args, 0, "%s%s", j == 0 ? "" : "|", eivr_args.cmd[j]); 00397 } 00398 } 00399 00400 if (ast_strlen_zero(data)) { 00401 ast_log(LOG_WARNING, "ExternalIVR requires a command to execute\n"); 00402 return -1; 00403 } 00404 00405 if (!(ast_test_flag(&flags, noanswer))) { 00406 ast_chan_log(LOG_WARNING, chan, "Answering channel and starting generator\n"); 00407 if (chan->_state != AST_STATE_UP) { 00408 if (ast_test_flag(&flags, run_dead)) { 00409 ast_chan_log(LOG_WARNING, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n"); 00410 goto exit; 00411 } 00412 ast_answer(chan); 00413 } 00414 if (ast_activate_generator(chan, &gen, u) < 0) { 00415 ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n"); 00416 goto exit; 00417 } else { 00418 u->gen_active = 1; 00419 } 00420 } 00421 00422 if (!strncmp(app_args[0], "ivr://", 6)) { 00423 struct ast_tcptls_session_args ivr_desc = { 00424 .accept_fd = -1, 00425 .name = "IVR", 00426 }; 00427 struct ast_hostent hp; 00428 00429 /*communicate through socket to server*/ 00430 ast_debug(1, "Parsing hostname:port for socket connect from \"%s\"\n", app_args[0]); 00431 ast_copy_string(hostname, app_args[0] + 6, sizeof(hostname)); 00432 if ((port_str = strchr(hostname, ':')) != NULL) { 00433 port_str[0] = 0; 00434 port_str += 1; 00435 port = atoi(port_str); 00436 } 00437 if (!port) { 00438 port = 2949; /* default port, if one is not provided */ 00439 } 00440 00441 ast_gethostbyname(hostname, &hp); 00442 ivr_desc.local_address.sin_family = AF_INET; 00443 ivr_desc.local_address.sin_port = htons(port); 00444 memcpy(&ivr_desc.local_address.sin_addr.s_addr, hp.hp.h_addr, hp.hp.h_length); 00445 ser = ast_tcptls_client_start(&ivr_desc); 00446 00447 if (!ser) { 00448 goto exit; 00449 } 00450 res = eivr_comm(chan, u, ser->fd, ser->fd, -1, pipe_delim_args, flags); 00451 00452 } else { 00453 00454 if (pipe(child_stdin)) { 00455 ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child input: %s\n", strerror(errno)); 00456 goto exit; 00457 } 00458 if (pipe(child_stdout)) { 00459 ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child output: %s\n", strerror(errno)); 00460 goto exit; 00461 } 00462 if (pipe(child_stderr)) { 00463 ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child errors: %s\n", strerror(errno)); 00464 goto exit; 00465 } 00466 00467 pid = ast_safe_fork(0); 00468 if (pid < 0) { 00469 ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno)); 00470 goto exit; 00471 } 00472 00473 if (!pid) { 00474 /* child process */ 00475 if (ast_opt_high_priority) 00476 ast_set_priority(0); 00477 00478 dup2(child_stdin[0], STDIN_FILENO); 00479 dup2(child_stdout[1], STDOUT_FILENO); 00480 dup2(child_stderr[1], STDERR_FILENO); 00481 ast_close_fds_above_n(STDERR_FILENO); 00482 execv(app_args[0], app_args); 00483 fprintf(stderr, "Failed to execute '%s': %s\n", app_args[0], strerror(errno)); 00484 _exit(1); 00485 } else { 00486 /* parent process */ 00487 close(child_stdin[0]); 00488 child_stdin[0] = 0; 00489 close(child_stdout[1]); 00490 child_stdout[1] = 0; 00491 close(child_stderr[1]); 00492 child_stderr[1] = 0; 00493 res = eivr_comm(chan, u, child_stdin[1], child_stdout[0], child_stderr[0], pipe_delim_args, flags); 00494 } 00495 } 00496 00497 exit: 00498 if (u->gen_active) 00499 ast_deactivate_generator(chan); 00500 00501 if (child_stdin[0]) 00502 close(child_stdin[0]); 00503 00504 if (child_stdin[1]) 00505 close(child_stdin[1]); 00506 00507 if (child_stdout[0]) 00508 close(child_stdout[0]); 00509 00510 if (child_stdout[1]) 00511 close(child_stdout[1]); 00512 00513 if (child_stderr[0]) 00514 close(child_stderr[0]); 00515 00516 if (child_stderr[1]) 00517 close(child_stderr[1]); 00518 if (ser) { 00519 ao2_ref(ser, -1); 00520 } 00521 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) 00522 ast_free(entry); 00523 00524 return res; 00525 }
static void ast_eivr_getvariable | ( | struct ast_channel * | chan, | |
char * | data, | |||
char * | outbuf, | |||
int | outbuflen | |||
) | [static] |
Definition at line 250 of file app_externalivr.c.
References ast_channel_lock, ast_channel_unlock, ast_copy_string(), ast_str_alloca, ast_str_append(), chan, inbuf(), pbx_builtin_getvar_helper(), ast_str::str, and strsep().
Referenced by eivr_comm().
00251 { 00252 /* original input data: "G,var1,var2," */ 00253 /* data passed as "data": "var1,var2" */ 00254 00255 char *inbuf, *variable; 00256 const char *value; 00257 int j; 00258 struct ast_str *newstring = ast_str_alloca(outbuflen); 00259 00260 outbuf[0] = '\0'; 00261 00262 for (j = 1, inbuf = data; ; j++) { 00263 variable = strsep(&inbuf, ","); 00264 if (variable == NULL) { 00265 int outstrlen = strlen(outbuf); 00266 if (outstrlen && outbuf[outstrlen - 1] == ',') { 00267 outbuf[outstrlen - 1] = 0; 00268 } 00269 break; 00270 } 00271 00272 ast_channel_lock(chan); 00273 if (!(value = pbx_builtin_getvar_helper(chan, variable))) { 00274 value = ""; 00275 } 00276 00277 ast_str_append(&newstring, 0, "%s=%s,", variable, value); 00278 ast_channel_unlock(chan); 00279 ast_copy_string(outbuf, newstring->str, outbuflen); 00280 } 00281 }
static void ast_eivr_setvariable | ( | struct ast_channel * | chan, | |
char * | data | |||
) | [static] |
Definition at line 283 of file app_externalivr.c.
References ast_copy_string(), ast_debug, buf, chan, inbuf(), pbx_builtin_setvar_helper(), and strsep().
Referenced by eivr_comm().
00284 { 00285 char buf[1024]; 00286 char *value; 00287 00288 char *inbuf, *variable; 00289 00290 int j; 00291 00292 for (j = 1, inbuf = data; ; j++, inbuf = NULL) { 00293 variable = strsep(&inbuf, ","); 00294 ast_debug(1, "Setting up a variable: %s\n", variable); 00295 if (variable) { 00296 /* variable contains "varname=value" */ 00297 ast_copy_string(buf, variable, sizeof(buf)); 00298 value = strchr(buf, '='); 00299 if (!value) { 00300 value = ""; 00301 } else { 00302 *value++ = '\0'; 00303 } 00304 pbx_builtin_setvar_helper(chan, buf, value); 00305 } else { 00306 break; 00307 } 00308 } 00309 }
static int eivr_comm | ( | struct ast_channel * | chan, | |
struct ivr_localuser * | u, | |||
int | eivr_events_fd, | |||
int | eivr_commands_fd, | |||
int | eivr_errors_fd, | |||
const struct ast_str * | args, | |||
const struct ast_flags | flags | |||
) | [static] |
Definition at line 527 of file app_externalivr.c.
References ast_channel::_state, ivr_localuser::abort_current_sound, ast_activate_generator(), ast_answer(), ast_chan_log, ast_check_hangup(), AST_CONTROL_HANGUP, ast_debug, ast_eivr_getvariable(), ast_eivr_setvariable(), ast_fileexists(), AST_FLAG_ZOMBIE, AST_FRAME_CONTROL, AST_FRAME_DTMF, ast_free, ast_frfree, AST_LIST_EMPTY, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, ast_read(), AST_STATE_UP, ast_strip(), ast_test_flag, ast_waitfor_nandfds(), ivr_localuser::chan, chan, errno, f, playlist_entry::filename, ivr_localuser::finishlist, ast_channel::flags, gen, ivr_localuser::gen_active, ast_channel::hangupcause, ignore_hangup, input(), ast_channel::language, playlist_entry::list, LOG_NOTICE, LOG_WARNING, make_entry(), ivr_localuser::option_autoclear, option_debug, ivr_localuser::playing_silence, ivr_localuser::playlist, run_dead, send_eivr_event(), and ast_str::str.
00530 { 00531 struct playlist_entry *entry; 00532 struct ast_frame *f; 00533 int ms; 00534 int exception; 00535 int ready_fd; 00536 int waitfds[2] = { eivr_commands_fd, eivr_errors_fd }; 00537 struct ast_channel *rchan; 00538 char *command; 00539 int res = -1; 00540 int test_available_fd = -1; 00541 int hangup_info_sent = 0; 00542 00543 FILE *eivr_commands = NULL; 00544 FILE *eivr_errors = NULL; 00545 FILE *eivr_events = NULL; 00546 00547 if (!(eivr_events = fdopen(eivr_events_fd, "w"))) { 00548 ast_chan_log(LOG_WARNING, chan, "Could not open stream to send events\n"); 00549 goto exit; 00550 } 00551 if (!(eivr_commands = fdopen(eivr_commands_fd, "r"))) { 00552 ast_chan_log(LOG_WARNING, chan, "Could not open stream to receive commands\n"); 00553 goto exit; 00554 } 00555 if (eivr_errors_fd > -1) { /* if opening a socket connection, error stream will not be used */ 00556 if (!(eivr_errors = fdopen(eivr_errors_fd, "r"))) { 00557 ast_chan_log(LOG_WARNING, chan, "Could not open stream to receive errors\n"); 00558 goto exit; 00559 } 00560 } 00561 00562 test_available_fd = open("/dev/null", O_RDONLY); 00563 00564 setvbuf(eivr_events, NULL, _IONBF, 0); 00565 setvbuf(eivr_commands, NULL, _IONBF, 0); 00566 if (eivr_errors) { 00567 setvbuf(eivr_errors, NULL, _IONBF, 0); 00568 } 00569 00570 res = 0; 00571 00572 while (1) { 00573 if (ast_test_flag(chan, AST_FLAG_ZOMBIE)) { 00574 ast_chan_log(LOG_NOTICE, chan, "Is a zombie\n"); 00575 res = -1; 00576 break; 00577 } 00578 if (!hangup_info_sent && !(ast_test_flag(&flags, run_dead)) && ast_check_hangup(chan)) { 00579 if (ast_test_flag(&flags, ignore_hangup)) { 00580 ast_chan_log(LOG_NOTICE, chan, "Got check_hangup, but ignore_hangup set so sending 'I' command\n"); 00581 send_eivr_event(eivr_events, 'I', "HANGUP", chan); 00582 hangup_info_sent = 1; 00583 } else { 00584 ast_chan_log(LOG_NOTICE, chan, "Got check_hangup\n"); 00585 send_eivr_event(eivr_events, 'H', NULL, chan); 00586 res = -1; 00587 break; 00588 } 00589 } 00590 00591 ready_fd = 0; 00592 ms = 100; 00593 errno = 0; 00594 exception = 0; 00595 00596 rchan = ast_waitfor_nandfds(&chan, 1, waitfds, (eivr_errors_fd < 0) ? 1 : 2, &exception, &ready_fd, &ms); 00597 00598 if (chan->_state == AST_STATE_UP && !AST_LIST_EMPTY(&u->finishlist)) { 00599 AST_LIST_LOCK(&u->finishlist); 00600 while ((entry = AST_LIST_REMOVE_HEAD(&u->finishlist, list))) { 00601 send_eivr_event(eivr_events, 'F', entry->filename, chan); 00602 ast_free(entry); 00603 } 00604 AST_LIST_UNLOCK(&u->finishlist); 00605 } 00606 00607 if (chan->_state == AST_STATE_UP && !(ast_check_hangup(chan)) && rchan) { 00608 /* the channel has something */ 00609 f = ast_read(chan); 00610 if (!f) { 00611 ast_chan_log(LOG_NOTICE, chan, "Returned no frame\n"); 00612 send_eivr_event(eivr_events, 'H', NULL, chan); 00613 res = -1; 00614 break; 00615 } 00616 if (f->frametype == AST_FRAME_DTMF) { 00617 send_eivr_event(eivr_events, f->subclass, NULL, chan); 00618 if (u->option_autoclear) { 00619 if (!u->abort_current_sound && !u->playing_silence) 00620 send_eivr_event(eivr_events, 'T', NULL, chan); 00621 AST_LIST_LOCK(&u->playlist); 00622 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) { 00623 send_eivr_event(eivr_events, 'D', entry->filename, chan); 00624 ast_free(entry); 00625 } 00626 if (!u->playing_silence) 00627 u->abort_current_sound = 1; 00628 AST_LIST_UNLOCK(&u->playlist); 00629 } 00630 } else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) { 00631 ast_chan_log(LOG_NOTICE, chan, "Got AST_CONTROL_HANGUP\n"); 00632 send_eivr_event(eivr_events, 'H', NULL, chan); 00633 if (f->data.uint32) { 00634 chan->hangupcause = f->data.uint32; 00635 } 00636 ast_frfree(f); 00637 res = -1; 00638 break; 00639 } 00640 ast_frfree(f); 00641 } else if (ready_fd == eivr_commands_fd) { 00642 char input[1024]; 00643 00644 if (exception || (dup2(eivr_commands_fd, test_available_fd) == -1) || feof(eivr_commands)) { 00645 ast_chan_log(LOG_WARNING, chan, "Child process went away\n"); 00646 res = -1; 00647 break; 00648 } 00649 00650 if (!fgets(input, sizeof(input), eivr_commands)) 00651 continue; 00652 00653 command = ast_strip(input); 00654 00655 if (option_debug) 00656 ast_debug(1, "got command '%s'\n", input); 00657 00658 if (strlen(input) < 4) 00659 continue; 00660 00661 if (input[0] == 'P') { 00662 send_eivr_event(eivr_events, 'P', args->str, chan); 00663 } else if ( input[0] == 'T' ) { 00664 ast_chan_log(LOG_WARNING, chan, "Answering channel if needed and starting generator\n"); 00665 if (chan->_state != AST_STATE_UP) { 00666 if (ast_test_flag(&flags, run_dead)) { 00667 ast_chan_log(LOG_WARNING, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n"); 00668 send_eivr_event(eivr_events, 'Z', "ANSWER_FAILURE", chan); 00669 continue; 00670 } 00671 ast_answer(chan); 00672 } 00673 if (!(u->gen_active)) { 00674 if (ast_activate_generator(chan, &gen, u) < 0) { 00675 ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n"); 00676 send_eivr_event(eivr_events, 'Z', "GENERATOR_FAILURE", chan); 00677 } else { 00678 u->gen_active = 1; 00679 } 00680 } 00681 } else if (input[0] == 'S') { 00682 if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) { 00683 ast_chan_log(LOG_WARNING, chan, "Queue 'S'et called on unanswered channel\n"); 00684 send_eivr_event(eivr_events, 'Z', NULL, chan); 00685 continue; 00686 } 00687 if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) { 00688 ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]); 00689 send_eivr_event(eivr_events, 'Z', NULL, chan); 00690 strcpy(&input[2], "exception"); 00691 } 00692 if (!u->abort_current_sound && !u->playing_silence) 00693 send_eivr_event(eivr_events, 'T', NULL, chan); 00694 AST_LIST_LOCK(&u->playlist); 00695 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) { 00696 send_eivr_event(eivr_events, 'D', entry->filename, chan); 00697 ast_free(entry); 00698 } 00699 if (!u->playing_silence) 00700 u->abort_current_sound = 1; 00701 entry = make_entry(&input[2]); 00702 if (entry) 00703 AST_LIST_INSERT_TAIL(&u->playlist, entry, list); 00704 AST_LIST_UNLOCK(&u->playlist); 00705 } else if (input[0] == 'A') { 00706 if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) { 00707 ast_chan_log(LOG_WARNING, chan, "Queue 'A'ppend called on unanswered channel\n"); 00708 send_eivr_event(eivr_events, 'Z', NULL, chan); 00709 continue; 00710 } 00711 if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) { 00712 ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]); 00713 send_eivr_event(eivr_events, 'Z', NULL, chan); 00714 strcpy(&input[2], "exception"); 00715 } 00716 entry = make_entry(&input[2]); 00717 if (entry) { 00718 AST_LIST_LOCK(&u->playlist); 00719 AST_LIST_INSERT_TAIL(&u->playlist, entry, list); 00720 AST_LIST_UNLOCK(&u->playlist); 00721 } 00722 } else if (input[0] == 'G') { 00723 /* A get variable message: "G,variable1,variable2,..." */ 00724 char response[2048]; 00725 00726 ast_chan_log(LOG_NOTICE, chan, "Getting a Variable out of the channel: %s\n", &input[2]); 00727 ast_eivr_getvariable(chan, &input[2], response, sizeof(response)); 00728 send_eivr_event(eivr_events, 'G', response, chan); 00729 } else if (input[0] == 'V') { 00730 /* A set variable message: "V,variablename=foo" */ 00731 ast_chan_log(LOG_NOTICE, chan, "Setting a Variable up: %s\n", &input[2]); 00732 ast_eivr_setvariable(chan, &input[2]); 00733 } else if (input[0] == 'L') { 00734 ast_chan_log(LOG_NOTICE, chan, "Log message from EIVR: %s\n", &input[2]); 00735 } else if (input[0] == 'X') { 00736 ast_chan_log(LOG_NOTICE, chan, "Exiting ExternalIVR: %s\n", &input[2]); 00737 /*! \todo add deprecation debug message for X command here */ 00738 res = 0; 00739 break; 00740 } else if (input[0] == 'E') { 00741 ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]); 00742 send_eivr_event(eivr_events, 'E', NULL, chan); 00743 res = 0; 00744 break; 00745 } else if (input[0] == 'H') { 00746 ast_chan_log(LOG_NOTICE, chan, "Hanging up: %s\n", &input[2]); 00747 send_eivr_event(eivr_events, 'H', NULL, chan); 00748 res = -1; 00749 break; 00750 } else if (input[0] == 'O') { 00751 if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) { 00752 ast_chan_log(LOG_WARNING, chan, "Option called on unanswered channel\n"); 00753 send_eivr_event(eivr_events, 'Z', NULL, chan); 00754 continue; 00755 } 00756 if (!strcasecmp(&input[2], "autoclear")) 00757 u->option_autoclear = 1; 00758 else if (!strcasecmp(&input[2], "noautoclear")) 00759 u->option_autoclear = 0; 00760 else 00761 ast_chan_log(LOG_WARNING, chan, "Unknown option requested '%s'\n", &input[2]); 00762 } 00763 } else if (eivr_errors_fd && ready_fd == eivr_errors_fd) { 00764 char input[1024]; 00765 00766 if (exception || feof(eivr_errors)) { 00767 ast_chan_log(LOG_WARNING, chan, "Child process went away\n"); 00768 res = -1; 00769 break; 00770 } 00771 if (fgets(input, sizeof(input), eivr_errors)) { 00772 command = ast_strip(input); 00773 ast_chan_log(LOG_NOTICE, chan, "stderr: %s\n", command); 00774 } 00775 } else if ((ready_fd < 0) && ms) { 00776 if (errno == 0 || errno == EINTR) 00777 continue; 00778 00779 ast_chan_log(LOG_WARNING, chan, "Wait failed (%s)\n", strerror(errno)); 00780 break; 00781 } 00782 } 00783 00784 00785 exit: 00786 00787 if (test_available_fd > -1) { 00788 close(test_available_fd); 00789 } 00790 00791 if (eivr_events) 00792 fclose(eivr_events); 00793 00794 if (eivr_commands) 00795 fclose(eivr_commands); 00796 00797 if (eivr_errors) 00798 fclose(eivr_errors); 00799 00800 return res; 00801 00802 }
int eivr_connect_socket | ( | struct ast_channel * | chan, | |
const char * | host, | |||
int | port | |||
) |
static void* gen_alloc | ( | struct ast_channel * | chan, | |
void * | params | |||
) | [static] |
Definition at line 129 of file app_externalivr.c.
References ast_calloc, and gen_state::u.
00130 { 00131 struct ivr_localuser *u = params; 00132 struct gen_state *state; 00133 00134 if (!(state = ast_calloc(1, sizeof(*state)))) 00135 return NULL; 00136 00137 state->u = u; 00138 00139 return state; 00140 }
static void gen_closestream | ( | struct gen_state * | state | ) | [static] |
Definition at line 142 of file app_externalivr.c.
References ast_closestream(), ivr_localuser::chan, ast_channel::stream, gen_state::stream, and gen_state::u.
Referenced by gen_nextfile(), gen_readframe(), and gen_release().
00143 { 00144 if (!state->stream) 00145 return; 00146 00147 ast_closestream(state->stream); 00148 state->u->chan->stream = NULL; 00149 state->stream = NULL; 00150 }
static int gen_generate | ( | struct ast_channel * | chan, | |
void * | data, | |||
int | len, | |||
int | samples | |||
) | [static] |
Definition at line 219 of file app_externalivr.c.
References ast_chan_log, ast_frfree, ast_write(), chan, errno, f, gen_readframe(), LOG_WARNING, and gen_state::sample_queue.
00220 { 00221 struct gen_state *state = data; 00222 struct ast_frame *f = NULL; 00223 int res = 0; 00224 00225 state->sample_queue += samples; 00226 00227 while (state->sample_queue > 0) { 00228 if (!(f = gen_readframe(state))) 00229 return -1; 00230 00231 res = ast_write(chan, f); 00232 ast_frfree(f); 00233 if (res < 0) { 00234 ast_chan_log(LOG_WARNING, chan, "Failed to write frame: %s\n", strerror(errno)); 00235 return -1; 00236 } 00237 state->sample_queue -= f->samples; 00238 } 00239 00240 return res; 00241 }
static int gen_nextfile | ( | struct gen_state * | state | ) | [static] |
Definition at line 161 of file app_externalivr.c.
References ivr_localuser::abort_current_sound, ast_chan_log, AST_LIST_REMOVE_HEAD, ast_openstream_full(), ivr_localuser::chan, gen_state::current, errno, playlist_entry::filename, gen_closestream(), ast_channel::language, playlist_entry::list, LOG_WARNING, ivr_localuser::playing_silence, ivr_localuser::playlist, gen_state::stream, and gen_state::u.
Referenced by gen_readframe().
00162 { 00163 struct ivr_localuser *u = state->u; 00164 char *file_to_stream; 00165 00166 u->abort_current_sound = 0; 00167 u->playing_silence = 0; 00168 gen_closestream(state); 00169 00170 while (!state->stream) { 00171 state->current = AST_LIST_REMOVE_HEAD(&u->playlist, list); 00172 if (state->current) { 00173 file_to_stream = state->current->filename; 00174 } else { 00175 file_to_stream = "silence/10"; 00176 u->playing_silence = 1; 00177 } 00178 00179 if (!(state->stream = ast_openstream_full(u->chan, file_to_stream, u->chan->language, 1))) { 00180 ast_chan_log(LOG_WARNING, u->chan, "File '%s' could not be opened: %s\n", file_to_stream, strerror(errno)); 00181 if (!u->playing_silence) { 00182 continue; 00183 } else { 00184 break; 00185 } 00186 } 00187 } 00188 00189 return (!state->stream); 00190 }
Definition at line 192 of file app_externalivr.c.
References ivr_localuser::abort_current_sound, AST_LIST_FIRST, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_UNLOCK, ast_readframe(), gen_state::current, f, ivr_localuser::finishlist, gen_closestream(), gen_nextfile(), playlist_entry::list, ivr_localuser::playing_silence, ivr_localuser::playlist, gen_state::stream, and gen_state::u.
Referenced by gen_generate().
00193 { 00194 struct ast_frame *f = NULL; 00195 struct ivr_localuser *u = state->u; 00196 00197 if (u->abort_current_sound || 00198 (u->playing_silence && AST_LIST_FIRST(&u->playlist))) { 00199 gen_closestream(state); 00200 AST_LIST_LOCK(&u->playlist); 00201 gen_nextfile(state); 00202 AST_LIST_UNLOCK(&u->playlist); 00203 } 00204 00205 if (!(state->stream && (f = ast_readframe(state->stream)))) { 00206 if (state->current) { 00207 AST_LIST_LOCK(&u->finishlist); 00208 AST_LIST_INSERT_TAIL(&u->finishlist, state->current, list); 00209 AST_LIST_UNLOCK(&u->finishlist); 00210 state->current = NULL; 00211 } 00212 if (!gen_nextfile(state)) 00213 f = ast_readframe(state->stream); 00214 } 00215 00216 return f; 00217 }
static void gen_release | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 152 of file app_externalivr.c.
References ast_free, and gen_closestream().
00153 { 00154 struct gen_state *state = data; 00155 00156 gen_closestream(state); 00157 ast_free(data); 00158 }
static int load_module | ( | void | ) | [static] |
Definition at line 809 of file app_externalivr.c.
References app_exec, and ast_register_application.
00810 { 00811 return ast_register_application(app, app_exec, synopsis, descrip); 00812 }
static struct playlist_entry* make_entry | ( | const char * | filename | ) | [static] |
Definition at line 311 of file app_externalivr.c.
References ast_calloc.
Referenced by eivr_comm().
00312 { 00313 struct playlist_entry *entry; 00314 00315 if (!(entry = ast_calloc(1, sizeof(*entry) + strlen(filename) + 10))) /* XXX why 10 ? */ 00316 return NULL; 00317 00318 strcpy(entry->filename, filename); 00319 00320 return entry; 00321 }
static void send_eivr_event | ( | FILE * | handle, | |
const char | event, | |||
const char * | data, | |||
const struct ast_channel * | chan | |||
) | [static] |
Definition at line 115 of file app_externalivr.c.
References ast_debug, ast_str_append(), ast_str_create(), and ast_str::str.
Referenced by eivr_comm().
00117 { 00118 struct ast_str *tmp = ast_str_create(12); 00119 00120 ast_str_append(&tmp, 0, "%c,%10d", event, (int)time(NULL)); 00121 if (data) { 00122 ast_str_append(&tmp, 0, ",%s", data); 00123 } 00124 00125 fprintf(handle, "%s\n", tmp->str); 00126 ast_debug(1, "sent '%s'\n", tmp->str); 00127 }
static int unload_module | ( | void | ) | [static] |
Definition at line 804 of file app_externalivr.c.
References ast_unregister_application().
00805 { 00806 return ast_unregister_application(app); 00807 }
struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "External IVR Interface Application" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "a9c98e5d177805051735cb5b0b16b0a0" , .load = load_module, .unload = unload_module, } [static] |
Definition at line 814 of file app_externalivr.c.
const char* app = "ExternalIVR" [static] |
Definition at line 51 of file app_externalivr.c.
struct ast_app_option app_opts[128] = { [ 'n' ] = { .flag = noanswer }, [ 'i' ] = { .flag = ignore_hangup }, [ 'd' ] = { .flag = run_dead },} [static] |
Definition at line 84 of file app_externalivr.c.
struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 814 of file app_externalivr.c.
const char* descrip [static] |
Definition at line 54 of file app_externalivr.c.
struct ast_generator gen [static] |
Definition at line 243 of file app_externalivr.c.
Referenced by app_exec(), ast_activate_generator(), eivr_comm(), reload_config(), and set_config().
enum { ... } options_flags |
const char* synopsis = "Interfaces with an external IVR application" [static] |
Definition at line 53 of file app_externalivr.c.