Wed Mar 3 22:39:45 2010

Asterisk developer's documentation


app_externalivr.c File Reference

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"
Include dependency graph for app_externalivr.c:

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_framegen_readframe (struct gen_state *state)
static void gen_release (struct ast_channel *chan, void *data)
static int load_module (void)
static struct playlist_entrymake_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_infoast_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"

Detailed Description

External IVR application interface.

Author:
Kevin P. Fleming <kpfleming@digium.com>
Note:
Portions taken from the file-based music-on-hold work created by Anthony Minessale II in res_musiconhold.c

Definition in file app_externalivr.c.


Define Documentation

#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().


Enumeration Type Documentation

anonymous enum
Enumerator:
noanswer 
ignore_hangup 
run_dead 

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;


Function Documentation

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]

Todo:
add deprecation debug message for X command here

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().

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(), 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 }

static struct ast_frame* gen_readframe ( struct gen_state state  )  [static, read]

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 }


Variable Documentation

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().

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.


Generated on 3 Mar 2010 for Asterisk - the Open Source PBX by  doxygen 1.6.1