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 | finishlist |
| struct | gen_state |
| struct | ivr_localuser |
| struct | 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" |
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 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 805 of file app_externalivr.c.
| static void __unreg_module | ( | void | ) | [static] |
Definition at line 805 of file app_externalivr.c.
| static int app_exec | ( | struct ast_channel * | chan, | |
| void * | data | |||
| ) | [static] |
Definition at line 314 of file app_externalivr.c.
References ast_channel::_state, ivr_localuser::abort_current_sound, ast_tcptls_session_args::accept_fd, ao2_ref, app_opts, 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_STANDARD_APP_ARGS, AST_STATE_UP, ast_str_append(), ast_str_create(), ast_str_reset(), ast_strdupa, ast_strlen_zero(), ast_tcptls_client_start(), ast_test_flag, buf, ivr_localuser::chan, eivr_comm(), errno, ast_tcptls_session_instance::fd, ivr_localuser::gen_active, hostname, ast_hostent::hp, ignore_hangup, ast_tcptls_session_args::local_address, LOG_ERROR, LOG_WARNING, noanswer, option_debug, ivr_localuser::playlist, run_dead, and s.
Referenced by load_module().
00315 { 00316 struct ast_flags flags = { 0, }; 00317 char *opts[0]; 00318 struct playlist_entry *entry; 00319 int child_stdin[2] = { 0, 0 }; 00320 int child_stdout[2] = { 0, 0 }; 00321 int child_stderr[2] = { 0, 0 }; 00322 int res = -1; 00323 int pid; 00324 00325 char hostname[1024]; 00326 char *port_str = NULL; 00327 int port = 0; 00328 struct ast_tcptls_session_instance *ser = NULL; 00329 00330 struct ivr_localuser foo = { 00331 .playlist = AST_LIST_HEAD_INIT_VALUE, 00332 .finishlist = AST_LIST_HEAD_INIT_VALUE, 00333 .gen_active = 0, 00334 }; 00335 struct ivr_localuser *u = &foo; 00336 00337 char *buf; 00338 int j; 00339 char *s, **app_args, *e; 00340 struct ast_str *pipe_delim_args = ast_str_create(100); 00341 00342 AST_DECLARE_APP_ARGS(eivr_args, 00343 AST_APP_ARG(cmd)[32]; 00344 ); 00345 AST_DECLARE_APP_ARGS(application_args, 00346 AST_APP_ARG(cmd)[32]; 00347 ); 00348 00349 u->abort_current_sound = 0; 00350 u->chan = chan; 00351 00352 buf = ast_strdupa(data); 00353 AST_STANDARD_APP_ARGS(eivr_args, buf); 00354 00355 if ((s = strchr(eivr_args.cmd[0], '('))) { 00356 s[0] = ','; 00357 if (( e = strrchr(s, ')')) ) { 00358 *e = '\0'; 00359 } else { 00360 ast_log(LOG_ERROR, "Parse error, no closing paren?\n"); 00361 } 00362 AST_STANDARD_APP_ARGS(application_args, eivr_args.cmd[0]); 00363 app_args = application_args.argv; 00364 00365 /* Put the application + the arguments in a | delimited list */ 00366 ast_str_reset(pipe_delim_args); 00367 for (j = 0; application_args.cmd[j] != NULL; j++) { 00368 ast_str_append(&pipe_delim_args, 0, "%s%s", j == 0 ? "" : ",", application_args.cmd[j]); 00369 } 00370 00371 /* Parse the ExternalIVR() arguments */ 00372 if (option_debug) 00373 ast_debug(1, "Parsing options from: [%s]\n", eivr_args.cmd[1]); 00374 ast_app_parse_options(app_opts, &flags, opts, eivr_args.cmd[1]); 00375 if (option_debug) { 00376 if (ast_test_flag(&flags, noanswer)) 00377 ast_debug(1, "noanswer is set\n"); 00378 if (ast_test_flag(&flags, ignore_hangup)) 00379 ast_debug(1, "ignore_hangup is set\n"); 00380 if (ast_test_flag(&flags, run_dead)) 00381 ast_debug(1, "run_dead is set\n"); 00382 } 00383 00384 } else { 00385 app_args = eivr_args.argv; 00386 for (j = 0; eivr_args.cmd[j] != NULL; j++) { 00387 ast_str_append(&pipe_delim_args, 0, "%s%s", j == 0 ? "" : "|", eivr_args.cmd[j]); 00388 } 00389 } 00390 00391 if (ast_strlen_zero(data)) { 00392 ast_log(LOG_WARNING, "ExternalIVR requires a command to execute\n"); 00393 return -1; 00394 } 00395 00396 if (!(ast_test_flag(&flags, noanswer))) { 00397 ast_chan_log(LOG_WARNING, chan, "Answering channel and starting generator\n"); 00398 if (chan->_state != AST_STATE_UP) { 00399 if (ast_test_flag(&flags, run_dead)) { 00400 ast_chan_log(LOG_WARNING, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n"); 00401 goto exit; 00402 } 00403 ast_answer(chan); 00404 } 00405 if (ast_activate_generator(chan, &gen, u) < 0) { 00406 ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n"); 00407 goto exit; 00408 } else { 00409 u->gen_active = 1; 00410 } 00411 } 00412 00413 if (!strncmp(app_args[0], "ivr://", 6)) { 00414 struct ast_tcptls_session_args ivr_desc = { 00415 .accept_fd = -1, 00416 .name = "IVR", 00417 }; 00418 struct ast_hostent hp; 00419 00420 /*communicate through socket to server*/ 00421 ast_debug(1, "Parsing hostname:port for socket connect from \"%s\"\n", app_args[0]); 00422 ast_copy_string(hostname, app_args[0] + 6, sizeof(hostname)); 00423 if ((port_str = strchr(hostname, ':')) != NULL) { 00424 port_str[0] = 0; 00425 port_str += 1; 00426 port = atoi(port_str); 00427 } 00428 if (!port) { 00429 port = 2949; /* default port, if one is not provided */ 00430 } 00431 00432 ast_gethostbyname(hostname, &hp); 00433 ivr_desc.local_address.sin_family = AF_INET; 00434 ivr_desc.local_address.sin_port = htons(port); 00435 memcpy(&ivr_desc.local_address.sin_addr.s_addr, hp.hp.h_addr, hp.hp.h_length); 00436 ser = ast_tcptls_client_start(&ivr_desc); 00437 00438 if (!ser) { 00439 goto exit; 00440 } 00441 res = eivr_comm(chan, u, ser->fd, ser->fd, -1, pipe_delim_args, flags); 00442 00443 } else { 00444 00445 if (pipe(child_stdin)) { 00446 ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child input: %s\n", strerror(errno)); 00447 goto exit; 00448 } 00449 if (pipe(child_stdout)) { 00450 ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child output: %s\n", strerror(errno)); 00451 goto exit; 00452 } 00453 if (pipe(child_stderr)) { 00454 ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child errors: %s\n", strerror(errno)); 00455 goto exit; 00456 } 00457 00458 pid = ast_safe_fork(0); 00459 if (pid < 0) { 00460 ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno)); 00461 goto exit; 00462 } 00463 00464 if (!pid) { 00465 /* child process */ 00466 if (ast_opt_high_priority) 00467 ast_set_priority(0); 00468 00469 dup2(child_stdin[0], STDIN_FILENO); 00470 dup2(child_stdout[1], STDOUT_FILENO); 00471 dup2(child_stderr[1], STDERR_FILENO); 00472 ast_close_fds_above_n(STDERR_FILENO); 00473 execv(app_args[0], app_args); 00474 fprintf(stderr, "Failed to execute '%s': %s\n", app_args[0], strerror(errno)); 00475 _exit(1); 00476 } else { 00477 /* parent process */ 00478 close(child_stdin[0]); 00479 child_stdin[0] = 0; 00480 close(child_stdout[1]); 00481 child_stdout[1] = 0; 00482 close(child_stderr[1]); 00483 child_stderr[1] = 0; 00484 res = eivr_comm(chan, u, child_stdin[1], child_stdout[0], child_stderr[0], pipe_delim_args, flags); 00485 } 00486 } 00487 00488 exit: 00489 if (u->gen_active) 00490 ast_deactivate_generator(chan); 00491 00492 if (child_stdin[0]) 00493 close(child_stdin[0]); 00494 00495 if (child_stdin[1]) 00496 close(child_stdin[1]); 00497 00498 if (child_stdout[0]) 00499 close(child_stdout[0]); 00500 00501 if (child_stdout[1]) 00502 close(child_stdout[1]); 00503 00504 if (child_stderr[0]) 00505 close(child_stderr[0]); 00506 00507 if (child_stderr[1]) 00508 close(child_stderr[1]); 00509 if (ser) { 00510 ao2_ref(ser, -1); 00511 } 00512 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) 00513 ast_free(entry); 00514 00515 return res; 00516 }
| 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(), 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_debug, ast_strdupa, inbuf(), pbx_builtin_setvar_helper(), and strsep().
Referenced by eivr_comm().
00284 { 00285 char *value; 00286 00287 char *inbuf = ast_strdupa(data), *variable; 00288 00289 for (variable = strsep(&inbuf, ","); variable; variable = strsep(&inbuf, ",")) { 00290 ast_debug(1, "Setting up a variable: %s\n", variable); 00291 /* variable contains "varname=value" */ 00292 value = strchr(variable, '='); 00293 if (!value) { 00294 value = ""; 00295 } else { 00296 *value++ = '\0'; 00297 } 00298 pbx_builtin_setvar_helper(chan, variable, value); 00299 } 00300 }
| 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 518 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, ast_frame::data, errno, f, playlist_entry::filename, ivr_localuser::finishlist, ast_frame::frametype, ivr_localuser::gen_active, ast_channel::hangupcause, ignore_hangup, input(), LOG_NOTICE, LOG_WARNING, make_entry(), ivr_localuser::option_autoclear, option_debug, ivr_localuser::playing_silence, ivr_localuser::playlist, run_dead, send_eivr_event(), ast_str::str, ast_frame::subclass, and ast_frame::uint32.
Referenced by app_exec().
00521 { 00522 struct playlist_entry *entry; 00523 struct ast_frame *f; 00524 int ms; 00525 int exception; 00526 int ready_fd; 00527 int waitfds[2] = { eivr_commands_fd, eivr_errors_fd }; 00528 struct ast_channel *rchan; 00529 char *command; 00530 int res = -1; 00531 int test_available_fd = -1; 00532 int hangup_info_sent = 0; 00533 00534 FILE *eivr_commands = NULL; 00535 FILE *eivr_errors = NULL; 00536 FILE *eivr_events = NULL; 00537 00538 if (!(eivr_events = fdopen(eivr_events_fd, "w"))) { 00539 ast_chan_log(LOG_WARNING, chan, "Could not open stream to send events\n"); 00540 goto exit; 00541 } 00542 if (!(eivr_commands = fdopen(eivr_commands_fd, "r"))) { 00543 ast_chan_log(LOG_WARNING, chan, "Could not open stream to receive commands\n"); 00544 goto exit; 00545 } 00546 if (eivr_errors_fd > -1) { /* if opening a socket connection, error stream will not be used */ 00547 if (!(eivr_errors = fdopen(eivr_errors_fd, "r"))) { 00548 ast_chan_log(LOG_WARNING, chan, "Could not open stream to receive errors\n"); 00549 goto exit; 00550 } 00551 } 00552 00553 test_available_fd = open("/dev/null", O_RDONLY); 00554 00555 setvbuf(eivr_events, NULL, _IONBF, 0); 00556 setvbuf(eivr_commands, NULL, _IONBF, 0); 00557 if (eivr_errors) { 00558 setvbuf(eivr_errors, NULL, _IONBF, 0); 00559 } 00560 00561 res = 0; 00562 00563 while (1) { 00564 if (ast_test_flag(chan, AST_FLAG_ZOMBIE)) { 00565 ast_chan_log(LOG_NOTICE, chan, "Is a zombie\n"); 00566 res = -1; 00567 break; 00568 } 00569 if (!hangup_info_sent && !(ast_test_flag(&flags, run_dead)) && ast_check_hangup(chan)) { 00570 if (ast_test_flag(&flags, ignore_hangup)) { 00571 ast_chan_log(LOG_NOTICE, chan, "Got check_hangup, but ignore_hangup set so sending 'I' command\n"); 00572 send_eivr_event(eivr_events, 'I', "HANGUP", chan); 00573 hangup_info_sent = 1; 00574 } else { 00575 ast_chan_log(LOG_NOTICE, chan, "Got check_hangup\n"); 00576 send_eivr_event(eivr_events, 'H', NULL, chan); 00577 res = -1; 00578 break; 00579 } 00580 } 00581 00582 ready_fd = 0; 00583 ms = 100; 00584 errno = 0; 00585 exception = 0; 00586 00587 rchan = ast_waitfor_nandfds(&chan, 1, waitfds, (eivr_errors_fd < 0) ? 1 : 2, &exception, &ready_fd, &ms); 00588 00589 if (chan->_state == AST_STATE_UP && !AST_LIST_EMPTY(&u->finishlist)) { 00590 AST_LIST_LOCK(&u->finishlist); 00591 while ((entry = AST_LIST_REMOVE_HEAD(&u->finishlist, list))) { 00592 send_eivr_event(eivr_events, 'F', entry->filename, chan); 00593 ast_free(entry); 00594 } 00595 AST_LIST_UNLOCK(&u->finishlist); 00596 } 00597 00598 if (chan->_state == AST_STATE_UP && !(ast_check_hangup(chan)) && rchan) { 00599 /* the channel has something */ 00600 f = ast_read(chan); 00601 if (!f) { 00602 ast_chan_log(LOG_NOTICE, chan, "Returned no frame\n"); 00603 send_eivr_event(eivr_events, 'H', NULL, chan); 00604 res = -1; 00605 break; 00606 } 00607 if (f->frametype == AST_FRAME_DTMF) { 00608 send_eivr_event(eivr_events, f->subclass, NULL, chan); 00609 if (u->option_autoclear) { 00610 if (!u->abort_current_sound && !u->playing_silence) 00611 send_eivr_event(eivr_events, 'T', NULL, chan); 00612 AST_LIST_LOCK(&u->playlist); 00613 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) { 00614 send_eivr_event(eivr_events, 'D', entry->filename, chan); 00615 ast_free(entry); 00616 } 00617 if (!u->playing_silence) 00618 u->abort_current_sound = 1; 00619 AST_LIST_UNLOCK(&u->playlist); 00620 } 00621 } else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) { 00622 ast_chan_log(LOG_NOTICE, chan, "Got AST_CONTROL_HANGUP\n"); 00623 send_eivr_event(eivr_events, 'H', NULL, chan); 00624 if (f->data.uint32) { 00625 chan->hangupcause = f->data.uint32; 00626 } 00627 ast_frfree(f); 00628 res = -1; 00629 break; 00630 } 00631 ast_frfree(f); 00632 } else if (ready_fd == eivr_commands_fd) { 00633 char input[1024]; 00634 00635 if (exception || (dup2(eivr_commands_fd, test_available_fd) == -1) || feof(eivr_commands)) { 00636 ast_chan_log(LOG_WARNING, chan, "Child process went away\n"); 00637 res = -1; 00638 break; 00639 } 00640 00641 if (!fgets(input, sizeof(input), eivr_commands)) 00642 continue; 00643 00644 command = ast_strip(input); 00645 00646 if (option_debug) 00647 ast_debug(1, "got command '%s'\n", input); 00648 00649 if (strlen(input) < 4) 00650 continue; 00651 00652 if (input[0] == 'P') { 00653 send_eivr_event(eivr_events, 'P', args->str, chan); 00654 } else if ( input[0] == 'T' ) { 00655 ast_chan_log(LOG_WARNING, chan, "Answering channel if needed and starting generator\n"); 00656 if (chan->_state != AST_STATE_UP) { 00657 if (ast_test_flag(&flags, run_dead)) { 00658 ast_chan_log(LOG_WARNING, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n"); 00659 send_eivr_event(eivr_events, 'Z', "ANSWER_FAILURE", chan); 00660 continue; 00661 } 00662 ast_answer(chan); 00663 } 00664 if (!(u->gen_active)) { 00665 if (ast_activate_generator(chan, &gen, u) < 0) { 00666 ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n"); 00667 send_eivr_event(eivr_events, 'Z', "GENERATOR_FAILURE", chan); 00668 } else { 00669 u->gen_active = 1; 00670 } 00671 } 00672 } else if (input[0] == 'S') { 00673 if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) { 00674 ast_chan_log(LOG_WARNING, chan, "Queue 'S'et called on unanswered channel\n"); 00675 send_eivr_event(eivr_events, 'Z', NULL, chan); 00676 continue; 00677 } 00678 if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) { 00679 ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]); 00680 send_eivr_event(eivr_events, 'Z', NULL, chan); 00681 strcpy(&input[2], "exception"); 00682 } 00683 if (!u->abort_current_sound && !u->playing_silence) 00684 send_eivr_event(eivr_events, 'T', NULL, chan); 00685 AST_LIST_LOCK(&u->playlist); 00686 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) { 00687 send_eivr_event(eivr_events, 'D', entry->filename, chan); 00688 ast_free(entry); 00689 } 00690 if (!u->playing_silence) 00691 u->abort_current_sound = 1; 00692 entry = make_entry(&input[2]); 00693 if (entry) 00694 AST_LIST_INSERT_TAIL(&u->playlist, entry, list); 00695 AST_LIST_UNLOCK(&u->playlist); 00696 } else if (input[0] == 'A') { 00697 if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) { 00698 ast_chan_log(LOG_WARNING, chan, "Queue 'A'ppend called on unanswered channel\n"); 00699 send_eivr_event(eivr_events, 'Z', NULL, chan); 00700 continue; 00701 } 00702 if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) { 00703 ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]); 00704 send_eivr_event(eivr_events, 'Z', NULL, chan); 00705 strcpy(&input[2], "exception"); 00706 } 00707 entry = make_entry(&input[2]); 00708 if (entry) { 00709 AST_LIST_LOCK(&u->playlist); 00710 AST_LIST_INSERT_TAIL(&u->playlist, entry, list); 00711 AST_LIST_UNLOCK(&u->playlist); 00712 } 00713 } else if (input[0] == 'G') { 00714 /* A get variable message: "G,variable1,variable2,..." */ 00715 char response[2048]; 00716 00717 ast_chan_log(LOG_NOTICE, chan, "Getting a Variable out of the channel: %s\n", &input[2]); 00718 ast_eivr_getvariable(chan, &input[2], response, sizeof(response)); 00719 send_eivr_event(eivr_events, 'G', response, chan); 00720 } else if (input[0] == 'V') { 00721 /* A set variable message: "V,variablename=foo" */ 00722 ast_chan_log(LOG_NOTICE, chan, "Setting a Variable up: %s\n", &input[2]); 00723 ast_eivr_setvariable(chan, &input[2]); 00724 } else if (input[0] == 'L') { 00725 ast_chan_log(LOG_NOTICE, chan, "Log message from EIVR: %s\n", &input[2]); 00726 } else if (input[0] == 'X') { 00727 ast_chan_log(LOG_NOTICE, chan, "Exiting ExternalIVR: %s\n", &input[2]); 00728 /*! \todo add deprecation debug message for X command here */ 00729 res = 0; 00730 break; 00731 } else if (input[0] == 'E') { 00732 ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]); 00733 send_eivr_event(eivr_events, 'E', NULL, chan); 00734 res = 0; 00735 break; 00736 } else if (input[0] == 'H') { 00737 ast_chan_log(LOG_NOTICE, chan, "Hanging up: %s\n", &input[2]); 00738 send_eivr_event(eivr_events, 'H', NULL, chan); 00739 res = -1; 00740 break; 00741 } else if (input[0] == 'O') { 00742 if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) { 00743 ast_chan_log(LOG_WARNING, chan, "Option called on unanswered channel\n"); 00744 send_eivr_event(eivr_events, 'Z', NULL, chan); 00745 continue; 00746 } 00747 if (!strcasecmp(&input[2], "autoclear")) 00748 u->option_autoclear = 1; 00749 else if (!strcasecmp(&input[2], "noautoclear")) 00750 u->option_autoclear = 0; 00751 else 00752 ast_chan_log(LOG_WARNING, chan, "Unknown option requested '%s'\n", &input[2]); 00753 } 00754 } else if (eivr_errors_fd && ready_fd == eivr_errors_fd) { 00755 char input[1024]; 00756 00757 if (exception || feof(eivr_errors)) { 00758 ast_chan_log(LOG_WARNING, chan, "Child process went away\n"); 00759 res = -1; 00760 break; 00761 } 00762 if (fgets(input, sizeof(input), eivr_errors)) { 00763 command = ast_strip(input); 00764 ast_chan_log(LOG_NOTICE, chan, "stderr: %s\n", command); 00765 } 00766 } else if ((ready_fd < 0) && ms) { 00767 if (errno == 0 || errno == EINTR) 00768 continue; 00769 00770 ast_chan_log(LOG_WARNING, chan, "Wait failed (%s)\n", strerror(errno)); 00771 break; 00772 } 00773 } 00774 00775 00776 exit: 00777 00778 if (test_available_fd > -1) { 00779 close(test_available_fd); 00780 } 00781 00782 if (eivr_events) 00783 fclose(eivr_events); 00784 00785 if (eivr_commands) 00786 fclose(eivr_commands); 00787 00788 if (eivr_errors) 00789 fclose(eivr_errors); 00790 00791 return res; 00792 00793 }
| 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().
| 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(), errno, f, gen_readframe(), LOG_WARNING, gen_state::sample_queue, and ast_frame::samples.
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(), 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(), 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 800 of file app_externalivr.c.
References app_exec(), and ast_register_application.
00801 { 00802 return ast_register_application(app, app_exec, synopsis, descrip); 00803 }
| static struct playlist_entry* make_entry | ( | const char * | filename | ) | [static, read] |
Definition at line 302 of file app_externalivr.c.
References ast_calloc, and playlist_entry::filename.
Referenced by eivr_comm().
00303 { 00304 struct playlist_entry *entry; 00305 00306 if (!(entry = ast_calloc(1, sizeof(*entry) + strlen(filename) + 10))) /* XXX why 10 ? */ 00307 return NULL; 00308 00309 strcpy(entry->filename, filename); 00310 00311 return entry; 00312 }
| 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 795 of file app_externalivr.c.
References ast_unregister_application().
00796 { 00797 return ast_unregister_application(app); 00798 }
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 805 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.
Referenced by app_exec().
struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 805 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 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.
1.6.1