External IVR application interface. More...
#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 | playlist_entry |
Defines | |
#define | ast_chan_log(level, channel, format,...) ast_log(level, "%s: " format, channel->name , ## __VA_ARGS__) |
#define | EIVR_CMD_ANS 'T' |
#define | EIVR_CMD_APND 'A' |
#define | EIVR_CMD_DTMF 'D' |
#define | EIVR_CMD_EXIT 'E' |
#define | EIVR_CMD_GET 'G' |
#define | EIVR_CMD_HGUP 'H' |
#define | EIVR_CMD_LOG 'L' |
#define | EIVR_CMD_OPT 'O' |
#define | EIVR_CMD_PARM 'P' |
#define | EIVR_CMD_SQUE 'S' |
#define | EIVR_CMD_SVAR 'V' |
#define | EIVR_CMD_XIT 'X' |
Enumerations | |
enum | options_flags { noanswer = (1 << 0), ignore_hangup = (1 << 1), run_dead = (1 << 2) } |
Functions | |
static int | app_exec (struct ast_channel *chan, const char *data) |
AST_APP_OPTIONS (app_opts,{AST_APP_OPTION('n', noanswer), AST_APP_OPTION('i', ignore_hangup), AST_APP_OPTION('d', run_dead),}) | |
static void | ast_eivr_getvariable (struct ast_channel *chan, char *data, char *outbuf, int outbuflen) |
static void | ast_eivr_senddtmf (struct ast_channel *chan, char *vdata) |
static void | ast_eivr_setvariable (struct ast_channel *chan, char *data) |
AST_MODULE_INFO_STANDARD (ASTERISK_GPL_KEY,"External IVR Interface Application") | |
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 const char | app [] = "ExternalIVR" |
static struct ast_generator | gen |
External IVR application interface.
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 100 of file app_externalivr.c.
Referenced by app_exec(), eivr_comm(), gen_generate(), and gen_nextfile().
#define EIVR_CMD_ANS 'T' |
Definition at line 112 of file app_externalivr.c.
Referenced by eivr_comm().
#define EIVR_CMD_APND 'A' |
Definition at line 103 of file app_externalivr.c.
Referenced by eivr_comm().
#define EIVR_CMD_DTMF 'D' |
Definition at line 104 of file app_externalivr.c.
Referenced by eivr_comm().
#define EIVR_CMD_EXIT 'E' |
Definition at line 105 of file app_externalivr.c.
Referenced by eivr_comm().
#define EIVR_CMD_GET 'G' |
Definition at line 106 of file app_externalivr.c.
Referenced by eivr_comm().
#define EIVR_CMD_HGUP 'H' |
Definition at line 107 of file app_externalivr.c.
Referenced by eivr_comm().
#define EIVR_CMD_LOG 'L' |
Definition at line 108 of file app_externalivr.c.
Referenced by eivr_comm().
#define EIVR_CMD_OPT 'O' |
Definition at line 109 of file app_externalivr.c.
Referenced by eivr_comm().
#define EIVR_CMD_PARM 'P' |
Definition at line 110 of file app_externalivr.c.
Referenced by eivr_comm().
#define EIVR_CMD_SQUE 'S' |
Definition at line 111 of file app_externalivr.c.
Referenced by eivr_comm().
#define EIVR_CMD_SVAR 'V' |
Definition at line 113 of file app_externalivr.c.
Referenced by eivr_comm().
#define EIVR_CMD_XIT 'X' |
Definition at line 114 of file app_externalivr.c.
Referenced by eivr_comm().
enum options_flags |
Definition at line 116 of file app_externalivr.c.
00116 { 00117 noanswer = (1 << 0), 00118 ignore_hangup = (1 << 1), 00119 run_dead = (1 << 2), 00120 };
static int app_exec | ( | struct ast_channel * | chan, | |
const char * | data | |||
) | [static] |
Definition at line 389 of file app_externalivr.c.
References ast_channel::_state, ast_tcptls_session_args::accept_fd, ao2_ref, ast_activate_generator(), ast_answer(), AST_APP_ARG, ast_app_parse_options(), ast_chan_log, ast_close_fds_above_n(), ast_copy_string(), ast_deactivate_generator(), ast_debug, AST_DECLARE_APP_ARGS, ast_free, ast_gethostbyname(), AST_LIST_HEAD_INIT_VALUE, AST_LIST_REMOVE_HEAD, ast_log(), ast_opt_high_priority, ast_safe_fork(), ast_set_priority(), ast_sockaddr_from_sin, AST_STANDARD_APP_ARGS, AST_STATE_UP, ast_str_alloca, ast_str_append(), ast_str_reset(), ast_strdupa, ast_strlen_zero(), ast_tcptls_client_create(), ast_tcptls_client_start(), ast_test_flag, ast_verb, ivr_localuser::chan, eivr_comm(), errno, ast_tcptls_session_instance::fd, gen, hostname, ast_hostent::hp, ignore_hangup, LOG_ERROR, noanswer, ast_tcptls_session_args::remote_address, and run_dead.
Referenced by load_module().
00390 { 00391 struct ast_flags flags = { 0, }; 00392 char *opts[0]; 00393 struct playlist_entry *entry; 00394 int child_stdin[2] = { -1, -1 }; 00395 int child_stdout[2] = { -1, -1 }; 00396 int child_stderr[2] = { -1, -1 }; 00397 int res = -1; 00398 int pid; 00399 00400 char hostname[1024]; 00401 char *port_str = NULL; 00402 int port = 0; 00403 struct ast_tcptls_session_instance *ser = NULL; 00404 00405 struct ivr_localuser foo = { 00406 .playlist = AST_LIST_HEAD_INIT_VALUE, 00407 .finishlist = AST_LIST_HEAD_INIT_VALUE, 00408 .gen_active = 0, 00409 }; 00410 struct ivr_localuser *u = &foo; 00411 00412 char *buf; 00413 int j; 00414 char *s, **app_args, *e; 00415 struct ast_str *comma_delim_args = ast_str_alloca(100); 00416 00417 AST_DECLARE_APP_ARGS(eivr_args, 00418 AST_APP_ARG(application); 00419 AST_APP_ARG(options); 00420 ); 00421 AST_DECLARE_APP_ARGS(application_args, 00422 AST_APP_ARG(cmd)[32]; 00423 ); 00424 00425 u->abort_current_sound = 0; 00426 u->chan = chan; 00427 00428 if (ast_strlen_zero(data)) { 00429 ast_log(LOG_ERROR, "ExternalIVR requires a command to execute\n"); 00430 goto exit; 00431 } 00432 00433 buf = ast_strdupa(data); 00434 AST_STANDARD_APP_ARGS(eivr_args, buf); 00435 00436 ast_verb(4, "ExternalIVR received application and arguments: %s\n", eivr_args.application); 00437 ast_verb(4, "ExternalIVR received options: %s\n", eivr_args.options); 00438 00439 /* Parse out any application arguments */ 00440 if ((s = strchr(eivr_args.application, '('))) { 00441 s[0] = ','; 00442 if ((e = strrchr(s, ')'))) { 00443 *e = '\0'; 00444 } else { 00445 ast_log(LOG_ERROR, "Parse error, missing closing parenthesis\n"); 00446 goto exit; 00447 } 00448 } 00449 00450 AST_STANDARD_APP_ARGS(application_args, eivr_args.application); 00451 app_args = application_args.argv; 00452 00453 /* Put the application + the arguments in a , delimited list */ 00454 ast_str_reset(comma_delim_args); 00455 for (j = 0; application_args.cmd[j] != NULL; j++) { 00456 ast_str_append(&comma_delim_args, 0, "%s%s", j == 0 ? "" : ",", application_args.cmd[j]); 00457 } 00458 00459 /* Get rid of any extraneous arguments */ 00460 if (eivr_args.options && (s = strchr(eivr_args.options, ','))) { 00461 *s = '\0'; 00462 } 00463 00464 /* Parse the ExternalIVR() arguments */ 00465 ast_verb(4, "Parsing options from: [%s]\n", eivr_args.options); 00466 ast_app_parse_options(app_opts, &flags, opts, eivr_args.options); 00467 if (ast_test_flag(&flags, noanswer)) { 00468 ast_verb(4, "noanswer is set\n"); 00469 } 00470 if (ast_test_flag(&flags, ignore_hangup)) { 00471 ast_verb(4, "ignore_hangup is set\n"); 00472 } 00473 if (ast_test_flag(&flags, run_dead)) { 00474 ast_verb(4, "run_dead is set\n"); 00475 } 00476 00477 if (!(ast_test_flag(&flags, noanswer))) { 00478 ast_verb(3, "Answering channel and starting generator\n"); 00479 if (chan->_state != AST_STATE_UP) { 00480 if (ast_test_flag(&flags, run_dead)) { 00481 ast_chan_log(LOG_ERROR, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n"); 00482 goto exit; 00483 } 00484 ast_answer(chan); 00485 } 00486 if (ast_activate_generator(chan, &gen, u) < 0) { 00487 ast_chan_log(LOG_ERROR, chan, "Failed to activate generator\n"); 00488 goto exit; 00489 } else { 00490 u->gen_active = 1; 00491 } 00492 } 00493 00494 if (!strncmp(app_args[0], "ivr://", 6)) { 00495 struct ast_tcptls_session_args ivr_desc = { 00496 .accept_fd = -1, 00497 .name = "IVR", 00498 }; 00499 struct ast_hostent hp; 00500 struct sockaddr_in remote_address_tmp; 00501 00502 /*communicate through socket to server*/ 00503 ast_debug(1, "Parsing hostname:port for socket connect from \"%s\"\n", app_args[0]); 00504 ast_copy_string(hostname, app_args[0] + 6, sizeof(hostname)); 00505 if ((port_str = strchr(hostname, ':')) != NULL) { 00506 port_str[0] = 0; 00507 port_str += 1; 00508 port = atoi(port_str); 00509 } 00510 if (!port) { 00511 port = 2949; /* default port, if one is not provided */ 00512 } 00513 00514 ast_gethostbyname(hostname, &hp); 00515 remote_address_tmp.sin_family = AF_INET; 00516 remote_address_tmp.sin_port = htons(port); 00517 memcpy(&remote_address_tmp.sin_addr.s_addr, hp.hp.h_addr, hp.hp.h_length); 00518 ast_sockaddr_from_sin(&ivr_desc.remote_address, &remote_address_tmp); 00519 if (!(ser = ast_tcptls_client_create(&ivr_desc)) || !(ser = ast_tcptls_client_start(ser))) { 00520 goto exit; 00521 } 00522 res = eivr_comm(chan, u, &ser->fd, &ser->fd, NULL, comma_delim_args, flags); 00523 00524 } else { 00525 if (pipe(child_stdin)) { 00526 ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child input: %s\n", strerror(errno)); 00527 goto exit; 00528 } 00529 if (pipe(child_stdout)) { 00530 ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child output: %s\n", strerror(errno)); 00531 goto exit; 00532 } 00533 if (pipe(child_stderr)) { 00534 ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child errors: %s\n", strerror(errno)); 00535 goto exit; 00536 } 00537 00538 pid = ast_safe_fork(0); 00539 if (pid < 0) { 00540 ast_log(LOG_ERROR, "Failed to fork(): %s\n", strerror(errno)); 00541 goto exit; 00542 } 00543 00544 if (!pid) { 00545 /* child process */ 00546 if (ast_opt_high_priority) 00547 ast_set_priority(0); 00548 00549 dup2(child_stdin[0], STDIN_FILENO); 00550 dup2(child_stdout[1], STDOUT_FILENO); 00551 dup2(child_stderr[1], STDERR_FILENO); 00552 ast_close_fds_above_n(STDERR_FILENO); 00553 execv(app_args[0], app_args); 00554 fprintf(stderr, "Failed to execute '%s': %s\n", app_args[0], strerror(errno)); 00555 _exit(1); 00556 } else { 00557 /* parent process */ 00558 close(child_stdin[0]); 00559 child_stdin[0] = -1; 00560 close(child_stdout[1]); 00561 child_stdout[1] = -1; 00562 close(child_stderr[1]); 00563 child_stderr[1] = -1; 00564 res = eivr_comm(chan, u, &child_stdin[1], &child_stdout[0], &child_stderr[0], comma_delim_args, flags); 00565 } 00566 } 00567 00568 exit: 00569 if (u->gen_active) { 00570 ast_deactivate_generator(chan); 00571 } 00572 if (child_stdin[0] > -1) { 00573 close(child_stdin[0]); 00574 } 00575 if (child_stdin[1] > -1) { 00576 close(child_stdin[1]); 00577 } 00578 if (child_stdout[0] > -1) { 00579 close(child_stdout[0]); 00580 } 00581 if (child_stdout[1] > -1) { 00582 close(child_stdout[1]); 00583 } 00584 if (child_stderr[0] > -1) { 00585 close(child_stderr[0]); 00586 } 00587 if (child_stderr[1] > -1) { 00588 close(child_stderr[1]); 00589 } 00590 if (ser) { 00591 ao2_ref(ser, -1); 00592 } 00593 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) { 00594 ast_free(entry); 00595 } 00596 return res; 00597 }
AST_APP_OPTIONS | ( | app_opts | ) |
static void ast_eivr_getvariable | ( | struct ast_channel * | chan, | |
char * | data, | |||
char * | outbuf, | |||
int | outbuflen | |||
) | [static] |
Definition at line 301 of file app_externalivr.c.
References ast_channel_lock, ast_channel_unlock, ast_copy_string(), ast_str_alloca, ast_str_append(), ast_str_buffer(), inbuf(), pbx_builtin_getvar_helper(), and value.
Referenced by eivr_comm().
00302 { 00303 /* original input data: "G,var1,var2," */ 00304 /* data passed as "data": "var1,var2" */ 00305 00306 char *inbuf, *variable; 00307 const char *value; 00308 int j; 00309 struct ast_str *newstring = ast_str_alloca(outbuflen); 00310 00311 outbuf[0] = '\0'; 00312 00313 for (j = 1, inbuf = data; ; j++) { 00314 variable = strsep(&inbuf, ","); 00315 if (variable == NULL) { 00316 int outstrlen = strlen(outbuf); 00317 if (outstrlen && outbuf[outstrlen - 1] == ',') { 00318 outbuf[outstrlen - 1] = 0; 00319 } 00320 break; 00321 } 00322 00323 ast_channel_lock(chan); 00324 if (!(value = pbx_builtin_getvar_helper(chan, variable))) { 00325 value = ""; 00326 } 00327 00328 ast_str_append(&newstring, 0, "%s=%s,", variable, value); 00329 ast_channel_unlock(chan); 00330 ast_copy_string(outbuf, ast_str_buffer(newstring), outbuflen); 00331 } 00332 }
static void ast_eivr_senddtmf | ( | struct ast_channel * | chan, | |
char * | vdata | |||
) | [static] |
Definition at line 353 of file app_externalivr.c.
References args, AST_APP_ARG, ast_app_parse_timelen(), AST_DECLARE_APP_ARGS, ast_dtmf_stream(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_verb, and TIMELEN_MILLISECONDS.
Referenced by eivr_comm().
00354 { 00355 00356 char *data; 00357 int dinterval = 0, duration = 0; 00358 AST_DECLARE_APP_ARGS(args, 00359 AST_APP_ARG(digits); 00360 AST_APP_ARG(dinterval); 00361 AST_APP_ARG(duration); 00362 ); 00363 00364 data = ast_strdupa(vdata); 00365 AST_STANDARD_APP_ARGS(args, data); 00366 00367 if (!ast_strlen_zero(args.dinterval)) { 00368 ast_app_parse_timelen(args.dinterval, &dinterval, TIMELEN_MILLISECONDS); 00369 } 00370 if (!ast_strlen_zero(args.duration)) { 00371 ast_app_parse_timelen(args.duration, &duration, TIMELEN_MILLISECONDS); 00372 } 00373 ast_verb(4, "Sending DTMF: %s %d %d\n", args.digits, dinterval <= 0 ? 250 : dinterval, duration); 00374 ast_dtmf_stream(chan, NULL, args.digits, dinterval <= 0 ? 250 : dinterval, duration); 00375 }
static void ast_eivr_setvariable | ( | struct ast_channel * | chan, | |
char * | data | |||
) | [static] |
Definition at line 334 of file app_externalivr.c.
References ast_debug, ast_strdupa, inbuf(), pbx_builtin_setvar_helper(), and value.
Referenced by eivr_comm().
00335 { 00336 char *value; 00337 00338 char *inbuf = ast_strdupa(data), *variable; 00339 00340 for (variable = strsep(&inbuf, ","); variable; variable = strsep(&inbuf, ",")) { 00341 ast_debug(1, "Setting up a variable: %s\n", variable); 00342 /* variable contains "varname=value" */ 00343 value = strchr(variable, '='); 00344 if (!value) { 00345 value = ""; 00346 } else { 00347 *value++ = '\0'; 00348 } 00349 pbx_builtin_setvar_helper(chan, variable, value); 00350 } 00351 }
AST_MODULE_INFO_STANDARD | ( | ASTERISK_GPL_KEY | , | |
"External IVR Interface Application" | ||||
) |
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 599 of file app_externalivr.c.
References ast_channel::_state, ast_activate_generator(), ast_answer(), ast_chan_log, ast_check_hangup(), AST_CONTROL_HANGUP, ast_eivr_getvariable(), ast_eivr_senddtmf(), 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_str_buffer(), ast_strip(), ast_test_flag, ast_verb, ast_waitfor_nandfds(), ivr_localuser::chan, ast_frame::data, EIVR_CMD_ANS, EIVR_CMD_APND, EIVR_CMD_DTMF, EIVR_CMD_EXIT, EIVR_CMD_GET, EIVR_CMD_HGUP, EIVR_CMD_LOG, EIVR_CMD_OPT, EIVR_CMD_PARM, EIVR_CMD_SQUE, EIVR_CMD_SVAR, EIVR_CMD_XIT, errno, f, ast_frame::frametype, gen, ast_channel::hangupcause, ignore_hangup, input(), ast_frame_subclass::integer, LOG_ERROR, LOG_NOTICE, LOG_WARNING, make_entry(), run_dead, send_eivr_event(), ast_frame::subclass, and ast_frame::uint32.
Referenced by app_exec().
00602 { 00603 struct playlist_entry *entry; 00604 struct ast_frame *f; 00605 int ms; 00606 int exception; 00607 int ready_fd; 00608 int waitfds[2] = { *eivr_commands_fd, (eivr_errors_fd) ? *eivr_errors_fd : -1 }; 00609 struct ast_channel *rchan; 00610 int res = -1; 00611 int test_available_fd = -1; 00612 int hangup_info_sent = 0; 00613 00614 FILE *eivr_commands = NULL; 00615 FILE *eivr_errors = NULL; 00616 FILE *eivr_events = NULL; 00617 00618 if (!(eivr_events = fdopen(*eivr_events_fd, "w"))) { 00619 ast_chan_log(LOG_ERROR, chan, "Could not open stream to send events\n"); 00620 goto exit; 00621 } 00622 if (!(eivr_commands = fdopen(*eivr_commands_fd, "r"))) { 00623 ast_chan_log(LOG_ERROR, chan, "Could not open stream to receive commands\n"); 00624 goto exit; 00625 } 00626 if (eivr_errors_fd) { /* if opening a socket connection, error stream will not be used */ 00627 if (!(eivr_errors = fdopen(*eivr_errors_fd, "r"))) { 00628 ast_chan_log(LOG_ERROR, chan, "Could not open stream to receive errors\n"); 00629 goto exit; 00630 } 00631 } 00632 00633 test_available_fd = open("/dev/null", O_RDONLY); 00634 00635 setvbuf(eivr_events, NULL, _IONBF, 0); 00636 setvbuf(eivr_commands, NULL, _IONBF, 0); 00637 if (eivr_errors) { 00638 setvbuf(eivr_errors, NULL, _IONBF, 0); 00639 } 00640 00641 while (1) { 00642 if (ast_test_flag(chan, AST_FLAG_ZOMBIE)) { 00643 ast_chan_log(LOG_ERROR, chan, "Is a zombie\n"); 00644 break; 00645 } 00646 if (!hangup_info_sent && !(ast_test_flag(&flags, run_dead)) && ast_check_hangup(chan)) { 00647 if (ast_test_flag(&flags, ignore_hangup)) { 00648 ast_verb(3, "Got check_hangup, but ignore_hangup set so sending 'I' command\n"); 00649 send_eivr_event(eivr_events, 'I', "HANGUP", chan); 00650 hangup_info_sent = 1; 00651 } else { 00652 ast_verb(3, "Got check_hangup\n"); 00653 send_eivr_event(eivr_events, 'H', NULL, chan); 00654 break; 00655 } 00656 } 00657 00658 ready_fd = 0; 00659 ms = 100; 00660 errno = 0; 00661 exception = 0; 00662 00663 rchan = ast_waitfor_nandfds(&chan, 1, waitfds, (eivr_errors_fd) ? 2 : 1, &exception, &ready_fd, &ms); 00664 00665 if (chan->_state == AST_STATE_UP && !AST_LIST_EMPTY(&u->finishlist)) { 00666 AST_LIST_LOCK(&u->finishlist); 00667 while ((entry = AST_LIST_REMOVE_HEAD(&u->finishlist, list))) { 00668 send_eivr_event(eivr_events, 'F', entry->filename, chan); 00669 ast_free(entry); 00670 } 00671 AST_LIST_UNLOCK(&u->finishlist); 00672 } 00673 00674 if (chan->_state == AST_STATE_UP && !(ast_check_hangup(chan)) && rchan) { 00675 /* the channel has something */ 00676 f = ast_read(chan); 00677 if (!f) { 00678 ast_verb(3, "Returned no frame\n"); 00679 send_eivr_event(eivr_events, 'H', NULL, chan); 00680 break; 00681 } 00682 if (f->frametype == AST_FRAME_DTMF) { 00683 send_eivr_event(eivr_events, f->subclass.integer, NULL, chan); 00684 if (u->option_autoclear) { 00685 AST_LIST_LOCK(&u->playlist); 00686 if (!u->abort_current_sound && !u->playing_silence) { 00687 /* send interrupted file as T data */ 00688 if ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) { 00689 send_eivr_event(eivr_events, 'T', entry->filename, chan); 00690 ast_free(entry); 00691 } 00692 } 00693 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) { 00694 send_eivr_event(eivr_events, 'D', entry->filename, chan); 00695 ast_free(entry); 00696 } 00697 if (!u->playing_silence) 00698 u->abort_current_sound = 1; 00699 AST_LIST_UNLOCK(&u->playlist); 00700 } 00701 } else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP)) { 00702 ast_verb(3, "Got AST_CONTROL_HANGUP\n"); 00703 send_eivr_event(eivr_events, 'H', NULL, chan); 00704 if (f->data.uint32) { 00705 chan->hangupcause = f->data.uint32; 00706 } 00707 ast_frfree(f); 00708 break; 00709 } 00710 ast_frfree(f); 00711 } else if (ready_fd == *eivr_commands_fd) { 00712 char input[1024]; 00713 00714 if (exception || (dup2(*eivr_commands_fd, test_available_fd) == -1) || feof(eivr_commands)) { 00715 ast_chan_log(LOG_ERROR, chan, "Child process went away\n"); 00716 break; 00717 } 00718 00719 if (!fgets(input, sizeof(input), eivr_commands)) { 00720 continue; 00721 } 00722 00723 ast_strip(input); 00724 ast_verb(4, "got command '%s'\n", input); 00725 00726 if (strlen(input) < 3) { 00727 continue; 00728 } 00729 00730 if (input[0] == EIVR_CMD_PARM) { 00731 struct ast_str *tmp = (struct ast_str *) args; 00732 send_eivr_event(eivr_events, 'P', ast_str_buffer(tmp), chan); 00733 } else if (input[0] == EIVR_CMD_DTMF) { 00734 ast_verb(4, "Sending DTMF: %s\n", &input[2]); 00735 ast_eivr_senddtmf(chan, &input[2]); 00736 } else if (input[0] == EIVR_CMD_ANS) { 00737 ast_verb(3, "Answering channel if needed and starting generator\n"); 00738 if (chan->_state != AST_STATE_UP) { 00739 if (ast_test_flag(&flags, run_dead)) { 00740 ast_chan_log(LOG_WARNING, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n"); 00741 send_eivr_event(eivr_events, 'Z', "ANSWER_FAILURE", chan); 00742 continue; 00743 } 00744 if (ast_answer(chan)) { 00745 ast_chan_log(LOG_WARNING, chan, "Failed to answer channel\n"); 00746 send_eivr_event(eivr_events, 'Z', "ANSWER_FAILURE", chan); 00747 continue; 00748 } 00749 } 00750 if (!(u->gen_active)) { 00751 if (ast_activate_generator(chan, &gen, u) < 0) { 00752 ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n"); 00753 send_eivr_event(eivr_events, 'Z', "GENERATOR_FAILURE", chan); 00754 } else { 00755 u->gen_active = 1; 00756 } 00757 } 00758 } else if (input[0] == EIVR_CMD_SQUE) { 00759 if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) { 00760 ast_chan_log(LOG_WARNING, chan, "Queue re'S'et called on unanswered channel\n"); 00761 send_eivr_event(eivr_events, 'Z', NULL, chan); 00762 continue; 00763 } 00764 if (!ast_fileexists(&input[2], NULL, u->chan->language)) { 00765 ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]); 00766 send_eivr_event(eivr_events, 'Z', &input[2], chan); 00767 } else { 00768 AST_LIST_LOCK(&u->playlist); 00769 if (!u->abort_current_sound && !u->playing_silence) { 00770 /* send interrupted file as T data */ 00771 if ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) { 00772 send_eivr_event(eivr_events, 'T', entry->filename, chan); 00773 ast_free(entry); 00774 } 00775 } 00776 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) { 00777 send_eivr_event(eivr_events, 'D', entry->filename, chan); 00778 ast_free(entry); 00779 } 00780 if (!u->playing_silence) { 00781 u->abort_current_sound = 1; 00782 } 00783 entry = make_entry(&input[2]); 00784 if (entry) { 00785 AST_LIST_INSERT_TAIL(&u->playlist, entry, list); 00786 } 00787 AST_LIST_UNLOCK(&u->playlist); 00788 } 00789 } else if (input[0] == EIVR_CMD_APND) { 00790 if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) { 00791 ast_chan_log(LOG_WARNING, chan, "Queue 'A'ppend called on unanswered channel\n"); 00792 send_eivr_event(eivr_events, 'Z', NULL, chan); 00793 continue; 00794 } 00795 if (!ast_fileexists(&input[2], NULL, u->chan->language)) { 00796 ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]); 00797 send_eivr_event(eivr_events, 'Z', &input[2], chan); 00798 } else { 00799 entry = make_entry(&input[2]); 00800 if (entry) { 00801 AST_LIST_LOCK(&u->playlist); 00802 AST_LIST_INSERT_TAIL(&u->playlist, entry, list); 00803 AST_LIST_UNLOCK(&u->playlist); 00804 } 00805 } 00806 } else if (input[0] == EIVR_CMD_GET) { 00807 char response[2048]; 00808 ast_verb(4, "Retriving Variables from channel: %s\n", &input[2]); 00809 ast_eivr_getvariable(chan, &input[2], response, sizeof(response)); 00810 send_eivr_event(eivr_events, 'G', response, chan); 00811 } else if (input[0] == EIVR_CMD_SVAR) { 00812 ast_verb(4, "Setting Variables in channel: %s\n", &input[2]); 00813 ast_eivr_setvariable(chan, &input[2]); 00814 } else if (input[0] == EIVR_CMD_LOG) { 00815 ast_chan_log(LOG_NOTICE, chan, "Log message from EIVR: %s\n", &input[2]); 00816 } else if (input[0] == EIVR_CMD_XIT) { 00817 ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]); 00818 ast_chan_log(LOG_WARNING, chan, "e'X'it command is depricated, use 'E'xit instead\n"); 00819 res = 0; 00820 break; 00821 } else if (input[0] == EIVR_CMD_EXIT) { 00822 ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]); 00823 send_eivr_event(eivr_events, 'E', NULL, chan); 00824 res = 0; 00825 break; 00826 } else if (input[0] == EIVR_CMD_HGUP) { 00827 ast_chan_log(LOG_NOTICE, chan, "Hanging up: %s\n", &input[2]); 00828 send_eivr_event(eivr_events, 'H', NULL, chan); 00829 break; 00830 } else if (input[0] == EIVR_CMD_OPT) { 00831 if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) { 00832 ast_chan_log(LOG_WARNING, chan, "Option called on unanswered channel\n"); 00833 send_eivr_event(eivr_events, 'Z', NULL, chan); 00834 continue; 00835 } 00836 if (!strcasecmp(&input[2], "autoclear")) 00837 u->option_autoclear = 1; 00838 else if (!strcasecmp(&input[2], "noautoclear")) 00839 u->option_autoclear = 0; 00840 else 00841 ast_chan_log(LOG_WARNING, chan, "Unknown option requested: %s\n", &input[2]); 00842 } 00843 } else if (eivr_errors_fd && (ready_fd == *eivr_errors_fd)) { 00844 char input[1024]; 00845 00846 if (exception || feof(eivr_errors)) { 00847 ast_chan_log(LOG_ERROR, chan, "Child process went away\n"); 00848 break; 00849 } 00850 if (fgets(input, sizeof(input), eivr_errors)) { 00851 ast_chan_log(LOG_NOTICE, chan, "stderr: %s\n", ast_strip(input)); 00852 } 00853 } else if ((ready_fd < 0) && ms) { 00854 if (errno == 0 || errno == EINTR) 00855 continue; 00856 00857 ast_chan_log(LOG_ERROR, chan, "Wait failed (%s)\n", strerror(errno)); 00858 break; 00859 } 00860 } 00861 00862 exit: 00863 if (test_available_fd > -1) { 00864 close(test_available_fd); 00865 } 00866 if (eivr_events) { 00867 fclose(eivr_events); 00868 *eivr_events_fd = -1; 00869 } 00870 if (eivr_commands) { 00871 fclose(eivr_commands); 00872 *eivr_commands_fd = -1; 00873 } 00874 if (eivr_errors) { 00875 fclose(eivr_errors); 00876 *eivr_errors_fd = -1; 00877 } 00878 return res; 00879 }
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 172 of file app_externalivr.c.
References ast_calloc, and gen_state::u.
00173 { 00174 struct ivr_localuser *u = params; 00175 struct gen_state *state; 00176 00177 if (!(state = ast_calloc(1, sizeof(*state)))) 00178 return NULL; 00179 00180 state->u = u; 00181 00182 return state; 00183 }
static void gen_closestream | ( | struct gen_state * | state | ) | [static] |
Definition at line 185 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().
static int gen_generate | ( | struct ast_channel * | chan, | |
void * | data, | |||
int | len, | |||
int | samples | |||
) | [static] |
Definition at line 270 of file app_externalivr.c.
References ast_chan_log, ast_frfree, ast_write(), errno, f, gen_readframe(), LOG_WARNING, gen_state::sample_queue, and ast_frame::samples.
00271 { 00272 struct gen_state *state = data; 00273 struct ast_frame *f = NULL; 00274 int res = 0; 00275 00276 state->sample_queue += samples; 00277 00278 while (state->sample_queue > 0) { 00279 if (!(f = gen_readframe(state))) 00280 return -1; 00281 00282 res = ast_write(chan, f); 00283 ast_frfree(f); 00284 if (res < 0) { 00285 ast_chan_log(LOG_WARNING, chan, "Failed to write frame: %s\n", strerror(errno)); 00286 return -1; 00287 } 00288 state->sample_queue -= f->samples; 00289 } 00290 00291 return res; 00292 }
static int gen_nextfile | ( | struct gen_state * | state | ) | [static] |
Definition at line 204 of file app_externalivr.c.
References ast_chan_log, AST_LIST_FIRST, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, ast_openstream_full(), ivr_localuser::chan, gen_state::current, errno, gen_closestream(), LOG_WARNING, gen_state::stream, and gen_state::u.
Referenced by gen_readframe().
00205 { 00206 struct ivr_localuser *u = state->u; 00207 char *file_to_stream; 00208 00209 u->abort_current_sound = 0; 00210 u->playing_silence = 0; 00211 gen_closestream(state); 00212 00213 while (!state->stream) { 00214 state->current = AST_LIST_FIRST(&u->playlist); 00215 if (state->current) { 00216 file_to_stream = state->current->filename; 00217 } else { 00218 file_to_stream = "silence/10"; 00219 u->playing_silence = 1; 00220 } 00221 00222 if (!(state->stream = ast_openstream_full(u->chan, file_to_stream, u->chan->language, 1))) { 00223 ast_chan_log(LOG_WARNING, u->chan, "File '%s' could not be opened: %s\n", file_to_stream, strerror(errno)); 00224 AST_LIST_LOCK(&u->playlist); 00225 AST_LIST_REMOVE_HEAD(&u->playlist, list); 00226 AST_LIST_UNLOCK(&u->playlist); 00227 if (!u->playing_silence) { 00228 continue; 00229 } else { 00230 break; 00231 } 00232 } 00233 } 00234 00235 return (!state->stream); 00236 }
Definition at line 238 of file app_externalivr.c.
References AST_LIST_FIRST, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, ast_readframe(), gen_state::current, f, gen_closestream(), gen_nextfile(), gen_state::stream, and gen_state::u.
Referenced by gen_generate().
00239 { 00240 struct ast_frame *f = NULL; 00241 struct ivr_localuser *u = state->u; 00242 00243 if (u->abort_current_sound || 00244 (u->playing_silence && AST_LIST_FIRST(&u->playlist))) { 00245 gen_closestream(state); 00246 AST_LIST_LOCK(&u->playlist); 00247 gen_nextfile(state); 00248 AST_LIST_UNLOCK(&u->playlist); 00249 } 00250 00251 if (!(state->stream && (f = ast_readframe(state->stream)))) { 00252 if (state->current) { 00253 /* remove finished file from playlist */ 00254 AST_LIST_LOCK(&u->playlist); 00255 AST_LIST_REMOVE_HEAD(&u->playlist, list); 00256 AST_LIST_UNLOCK(&u->playlist); 00257 /* add finished file to finishlist */ 00258 AST_LIST_LOCK(&u->finishlist); 00259 AST_LIST_INSERT_TAIL(&u->finishlist, state->current, list); 00260 AST_LIST_UNLOCK(&u->finishlist); 00261 state->current = NULL; 00262 } 00263 if (!gen_nextfile(state)) 00264 f = ast_readframe(state->stream); 00265 } 00266 00267 return f; 00268 }
static void gen_release | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 195 of file app_externalivr.c.
References ast_free, and gen_closestream().
00196 { 00197 struct gen_state *state = data; 00198 00199 gen_closestream(state); 00200 ast_free(data); 00201 }
static int load_module | ( | void | ) | [static] |
Definition at line 886 of file app_externalivr.c.
References app_exec(), and ast_register_application_xml.
00887 { 00888 return ast_register_application_xml(app, app_exec); 00889 }
static struct playlist_entry* make_entry | ( | const char * | filename | ) | [static, read] |
Definition at line 377 of file app_externalivr.c.
References ast_calloc.
Referenced by eivr_comm().
00378 { 00379 struct playlist_entry *entry; 00380 00381 if (!(entry = ast_calloc(1, sizeof(*entry) + strlen(filename) + 10))) /* XXX why 10 ? */ 00382 return NULL; 00383 00384 strcpy(entry->filename, filename); 00385 00386 return entry; 00387 }
static void send_eivr_event | ( | FILE * | handle, | |
const char | event, | |||
const char * | data, | |||
const struct ast_channel * | chan | |||
) | [static] |
Definition at line 157 of file app_externalivr.c.
References ast_debug, ast_free, ast_str_append(), ast_str_buffer(), and ast_str_create().
Referenced by eivr_comm().
00159 { 00160 struct ast_str *tmp = ast_str_create(12); 00161 00162 ast_str_append(&tmp, 0, "%c,%10d", event, (int)time(NULL)); 00163 if (data) { 00164 ast_str_append(&tmp, 0, ",%s", data); 00165 } 00166 00167 fprintf(handle, "%s\n", ast_str_buffer(tmp)); 00168 ast_debug(1, "sent '%s'\n", ast_str_buffer(tmp)); 00169 ast_free(tmp); 00170 }
static int unload_module | ( | void | ) | [static] |
Definition at line 881 of file app_externalivr.c.
References ast_unregister_application().
00882 { 00883 return ast_unregister_application(app); 00884 }
const char app[] = "ExternalIVR" [static] |
Definition at line 97 of file app_externalivr.c.
struct ast_generator gen [static] |
Definition at line 294 of file app_externalivr.c.
Referenced by app_exec(), eivr_comm(), reload_config(), and set_config().