Fri Jun 19 12:09:57 2009

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"

Go to the source code of this file.

Data Structures

struct  gen_state
struct  ivr_localuser
struct  ivr_localuser::finishlist
struct  ivr_localuser::playlist
struct  playlist_entry

Defines

#define ast_chan_log(level, channel, format,)   ast_log(level, "%s: " format, channel->name , ## __VA_ARGS__)

Enumerations

enum  { noanswer = (1 << 0), ignore_hangup = (1 << 1), run_dead = (1 << 2) }

Functions

static void __reg_module (void)
static void __unreg_module (void)
static int app_exec (struct ast_channel *chan, void *data)
static void ast_eivr_getvariable (struct ast_channel *chan, char *data, char *outbuf, int outbuflen)
static void ast_eivr_setvariable (struct ast_channel *chan, char *data)
static int eivr_comm (struct ast_channel *chan, struct ivr_localuser *u, int eivr_events_fd, int eivr_commands_fd, int eivr_errors_fd, const struct ast_str *args, const struct ast_flags flags)
int eivr_connect_socket (struct ast_channel *chan, const char *host, int port)
static void * gen_alloc (struct ast_channel *chan, void *params)
static void gen_closestream (struct gen_state *state)
static int gen_generate (struct ast_channel *chan, void *data, int len, int samples)
static int gen_nextfile (struct gen_state *state)
static struct ast_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 814 of file app_externalivr.c.

static void __unreg_module ( void   )  [static]

Definition at line 814 of file app_externalivr.c.

static int app_exec ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 323 of file app_externalivr.c.

References ast_channel::_state, ivr_localuser::abort_current_sound, ast_activate_generator(), ast_answer(), AST_APP_ARG, ast_app_parse_options(), ast_chan_log, ast_debug, AST_DECLARE_APP_ARGS, AST_LIST_HEAD_INIT_VALUE, ast_log(), AST_STANDARD_APP_ARGS, AST_STATE_UP, ast_str_append(), ast_str_create(), ast_str_reset(), ast_strdupa, ast_strlen_zero(), ast_test_flag, buf, chan, ivr_localuser::chan, ast_flags::flags, gen, ivr_localuser::gen_active, hostname, ignore_hangup, LOG_ERROR, LOG_WARNING, noanswer, option_debug, ivr_localuser::playlist, run_dead, and s.

00324 {
00325    struct ast_flags flags = { 0, };
00326    char *opts[0];
00327    struct playlist_entry *entry;
00328    int child_stdin[2] = { 0, 0 };
00329    int child_stdout[2] = { 0, 0 };
00330    int child_stderr[2] = { 0, 0 };
00331    int res = -1;
00332    int pid;
00333 
00334    char hostname[1024];
00335    char *port_str = NULL;
00336    int port = 0;
00337    struct ast_tcptls_session_instance *ser = NULL;
00338 
00339    struct ivr_localuser foo = {
00340       .playlist = AST_LIST_HEAD_INIT_VALUE,
00341       .finishlist = AST_LIST_HEAD_INIT_VALUE,
00342       .gen_active = 0,
00343    };
00344    struct ivr_localuser *u = &foo;
00345 
00346    char *buf;
00347    int j;
00348    char *s, **app_args, *e; 
00349    struct ast_str *pipe_delim_args = ast_str_create(100);
00350 
00351    AST_DECLARE_APP_ARGS(eivr_args,
00352       AST_APP_ARG(cmd)[32];
00353    );
00354    AST_DECLARE_APP_ARGS(application_args,
00355       AST_APP_ARG(cmd)[32];
00356    );
00357 
00358    u->abort_current_sound = 0;
00359    u->chan = chan;
00360 
00361    buf = ast_strdupa(data);
00362    AST_STANDARD_APP_ARGS(eivr_args, buf);
00363 
00364    if ((s = strchr(eivr_args.cmd[0], '('))) {
00365       s[0] = ',';
00366       if (( e = strrchr(s, ')')) ) {
00367          *e = '\0';
00368       } else {
00369          ast_log(LOG_ERROR, "Parse error, no closing paren?\n");
00370       }
00371       AST_STANDARD_APP_ARGS(application_args, eivr_args.cmd[0]);
00372       app_args = application_args.argv;
00373 
00374       /* Put the application + the arguments in a | delimited list */
00375       ast_str_reset(pipe_delim_args);
00376       for (j = 0; application_args.cmd[j] != NULL; j++) {
00377          ast_str_append(&pipe_delim_args, 0, "%s%s", j == 0 ? "" : ",", application_args.cmd[j]);
00378       }
00379 
00380       /* Parse the ExternalIVR() arguments */
00381       if (option_debug)
00382          ast_debug(1, "Parsing options from: [%s]\n", eivr_args.cmd[1]);
00383       ast_app_parse_options(app_opts, &flags, opts, eivr_args.cmd[1]);
00384       if (option_debug) {
00385          if (ast_test_flag(&flags, noanswer))
00386             ast_debug(1, "noanswer is set\n");
00387          if (ast_test_flag(&flags, ignore_hangup))
00388             ast_debug(1, "ignore_hangup is set\n");
00389          if (ast_test_flag(&flags, run_dead))
00390             ast_debug(1, "run_dead is set\n");
00391       }
00392 
00393    } else {
00394       app_args = eivr_args.argv;
00395       for (j = 0; eivr_args.cmd[j] != NULL; j++) {
00396          ast_str_append(&pipe_delim_args, 0, "%s%s", j == 0 ? "" : "|", eivr_args.cmd[j]);
00397       }
00398    }
00399    
00400    if (ast_strlen_zero(data)) {
00401       ast_log(LOG_WARNING, "ExternalIVR requires a command to execute\n");
00402       return -1;
00403    }
00404 
00405    if (!(ast_test_flag(&flags, noanswer))) {
00406       ast_chan_log(LOG_WARNING, chan, "Answering channel and starting generator\n");
00407       if (chan->_state != AST_STATE_UP) {
00408          if (ast_test_flag(&flags, run_dead)) {
00409             ast_chan_log(LOG_WARNING, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n");
00410             goto exit;
00411          }
00412          ast_answer(chan);
00413       }
00414       if (ast_activate_generator(chan, &gen, u) < 0) {
00415          ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n");
00416          goto exit;
00417       } else {
00418          u->gen_active = 1;
00419       }
00420    }
00421 
00422    if (!strncmp(app_args[0], "ivr://", 6)) {
00423       struct ast_tcptls_session_args ivr_desc = {
00424          .accept_fd = -1,
00425          .name = "IVR",
00426       };
00427       struct ast_hostent hp;
00428 
00429       /*communicate through socket to server*/
00430       ast_debug(1, "Parsing hostname:port for socket connect from \"%s\"\n", app_args[0]);
00431       ast_copy_string(hostname, app_args[0] + 6, sizeof(hostname));
00432       if ((port_str = strchr(hostname, ':')) != NULL) {
00433          port_str[0] = 0;
00434          port_str += 1;
00435          port = atoi(port_str);
00436       }
00437       if (!port) {
00438          port = 2949;  /* default port, if one is not provided */
00439       }
00440 
00441       ast_gethostbyname(hostname, &hp);
00442       ivr_desc.local_address.sin_family = AF_INET;
00443       ivr_desc.local_address.sin_port = htons(port);
00444       memcpy(&ivr_desc.local_address.sin_addr.s_addr, hp.hp.h_addr, hp.hp.h_length);
00445       ser = ast_tcptls_client_start(&ivr_desc);
00446 
00447       if (!ser) {
00448          goto exit;
00449       }
00450       res = eivr_comm(chan, u, ser->fd, ser->fd, -1, pipe_delim_args, flags);
00451 
00452    } else {
00453    
00454       if (pipe(child_stdin)) {
00455          ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child input: %s\n", strerror(errno));
00456          goto exit;
00457       }
00458       if (pipe(child_stdout)) {
00459          ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child output: %s\n", strerror(errno));
00460          goto exit;
00461       }
00462       if (pipe(child_stderr)) {
00463          ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child errors: %s\n", strerror(errno));
00464          goto exit;
00465       }
00466    
00467       pid = ast_safe_fork(0);
00468       if (pid < 0) {
00469          ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno));
00470          goto exit;
00471       }
00472    
00473       if (!pid) {
00474          /* child process */
00475          if (ast_opt_high_priority)
00476             ast_set_priority(0);
00477    
00478          dup2(child_stdin[0], STDIN_FILENO);
00479          dup2(child_stdout[1], STDOUT_FILENO);
00480          dup2(child_stderr[1], STDERR_FILENO);
00481          ast_close_fds_above_n(STDERR_FILENO);
00482          execv(app_args[0], app_args);
00483          fprintf(stderr, "Failed to execute '%s': %s\n", app_args[0], strerror(errno));
00484          _exit(1);
00485       } else {
00486          /* parent process */
00487          close(child_stdin[0]);
00488          child_stdin[0] = 0;
00489          close(child_stdout[1]);
00490          child_stdout[1] = 0;
00491          close(child_stderr[1]);
00492          child_stderr[1] = 0;
00493          res = eivr_comm(chan, u, child_stdin[1], child_stdout[0], child_stderr[0], pipe_delim_args, flags);
00494       }
00495    }
00496 
00497    exit:
00498    if (u->gen_active)
00499       ast_deactivate_generator(chan);
00500 
00501    if (child_stdin[0])
00502       close(child_stdin[0]);
00503 
00504    if (child_stdin[1])
00505       close(child_stdin[1]);
00506 
00507    if (child_stdout[0])
00508       close(child_stdout[0]);
00509 
00510    if (child_stdout[1])
00511       close(child_stdout[1]);
00512 
00513    if (child_stderr[0])
00514       close(child_stderr[0]);
00515 
00516    if (child_stderr[1])
00517       close(child_stderr[1]);
00518    if (ser) {
00519       ao2_ref(ser, -1);
00520    }
00521    while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list)))
00522       ast_free(entry);
00523 
00524    return res;
00525 }

static void ast_eivr_getvariable ( struct ast_channel chan,
char *  data,
char *  outbuf,
int  outbuflen 
) [static]

Definition at line 250 of file app_externalivr.c.

References ast_channel_lock, ast_channel_unlock, ast_copy_string(), ast_str_alloca, ast_str_append(), chan, inbuf(), pbx_builtin_getvar_helper(), ast_str::str, and strsep().

Referenced by eivr_comm().

00251 {
00252    /* original input data: "G,var1,var2," */
00253    /* data passed as "data":  "var1,var2" */
00254 
00255    char *inbuf, *variable;
00256    const char *value;
00257    int j;
00258    struct ast_str *newstring = ast_str_alloca(outbuflen); 
00259 
00260    outbuf[0] = '\0';
00261 
00262    for (j = 1, inbuf = data; ; j++) {
00263       variable = strsep(&inbuf, ",");
00264       if (variable == NULL) {
00265          int outstrlen = strlen(outbuf);
00266          if (outstrlen && outbuf[outstrlen - 1] == ',') {
00267             outbuf[outstrlen - 1] = 0;
00268          }
00269          break;
00270       }
00271       
00272       ast_channel_lock(chan);
00273       if (!(value = pbx_builtin_getvar_helper(chan, variable))) {
00274          value = "";
00275       }
00276 
00277       ast_str_append(&newstring, 0, "%s=%s,", variable, value);
00278       ast_channel_unlock(chan);
00279       ast_copy_string(outbuf, newstring->str, outbuflen);
00280    }
00281 }

static void ast_eivr_setvariable ( struct ast_channel chan,
char *  data 
) [static]

Definition at line 283 of file app_externalivr.c.

References ast_copy_string(), ast_debug, buf, chan, inbuf(), pbx_builtin_setvar_helper(), and strsep().

Referenced by eivr_comm().

00284 {
00285    char buf[1024];
00286    char *value;
00287 
00288    char *inbuf, *variable;
00289 
00290    int j;
00291 
00292    for (j = 1, inbuf = data; ; j++, inbuf = NULL) {
00293       variable = strsep(&inbuf, ",");
00294       ast_debug(1, "Setting up a variable: %s\n", variable);
00295       if (variable) {
00296          /* variable contains "varname=value" */
00297          ast_copy_string(buf, variable, sizeof(buf));
00298          value = strchr(buf, '=');
00299          if (!value) {
00300             value = "";
00301          } else {
00302             *value++ = '\0';
00303          }
00304          pbx_builtin_setvar_helper(chan, buf, value);
00305       } else {
00306          break;
00307       }
00308    }
00309 }

static int eivr_comm ( struct ast_channel chan,
struct ivr_localuser u,
int  eivr_events_fd,
int  eivr_commands_fd,
int  eivr_errors_fd,
const struct ast_str args,
const struct ast_flags  flags 
) [static]

Definition at line 527 of file app_externalivr.c.

References ast_channel::_state, ivr_localuser::abort_current_sound, ast_activate_generator(), ast_answer(), ast_chan_log, ast_check_hangup(), AST_CONTROL_HANGUP, ast_debug, ast_eivr_getvariable(), ast_eivr_setvariable(), ast_fileexists(), AST_FLAG_ZOMBIE, AST_FRAME_CONTROL, AST_FRAME_DTMF, ast_free, ast_frfree, AST_LIST_EMPTY, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, ast_read(), AST_STATE_UP, ast_strip(), ast_test_flag, ast_waitfor_nandfds(), ivr_localuser::chan, chan, errno, f, playlist_entry::filename, ivr_localuser::finishlist, ast_channel::flags, gen, ivr_localuser::gen_active, ast_channel::hangupcause, ignore_hangup, input(), ast_channel::language, playlist_entry::list, LOG_NOTICE, LOG_WARNING, make_entry(), ivr_localuser::option_autoclear, option_debug, ivr_localuser::playing_silence, ivr_localuser::playlist, run_dead, send_eivr_event(), and ast_str::str.

00530 {
00531    struct playlist_entry *entry;
00532    struct ast_frame *f;
00533    int ms;
00534    int exception;
00535    int ready_fd;
00536    int waitfds[2] = { eivr_commands_fd, eivr_errors_fd };
00537    struct ast_channel *rchan;
00538    char *command;
00539    int res = -1;
00540    int test_available_fd = -1;
00541    int hangup_info_sent = 0;
00542   
00543    FILE *eivr_commands = NULL;
00544    FILE *eivr_errors = NULL;
00545    FILE *eivr_events = NULL;
00546 
00547    if (!(eivr_events = fdopen(eivr_events_fd, "w"))) {
00548       ast_chan_log(LOG_WARNING, chan, "Could not open stream to send events\n");
00549       goto exit;
00550    }
00551    if (!(eivr_commands = fdopen(eivr_commands_fd, "r"))) {
00552       ast_chan_log(LOG_WARNING, chan, "Could not open stream to receive commands\n");
00553       goto exit;
00554    }
00555    if (eivr_errors_fd > -1) {  /* if opening a socket connection, error stream will not be used */
00556       if (!(eivr_errors = fdopen(eivr_errors_fd, "r"))) {
00557          ast_chan_log(LOG_WARNING, chan, "Could not open stream to receive errors\n");
00558          goto exit;
00559       }
00560    }
00561 
00562    test_available_fd = open("/dev/null", O_RDONLY);
00563  
00564    setvbuf(eivr_events, NULL, _IONBF, 0);
00565    setvbuf(eivr_commands, NULL, _IONBF, 0);
00566    if (eivr_errors) {
00567       setvbuf(eivr_errors, NULL, _IONBF, 0);
00568    }
00569 
00570    res = 0;
00571  
00572    while (1) {
00573       if (ast_test_flag(chan, AST_FLAG_ZOMBIE)) {
00574          ast_chan_log(LOG_NOTICE, chan, "Is a zombie\n");
00575          res = -1;
00576          break;
00577       }
00578       if (!hangup_info_sent && !(ast_test_flag(&flags, run_dead)) && ast_check_hangup(chan)) {
00579          if (ast_test_flag(&flags, ignore_hangup)) {
00580             ast_chan_log(LOG_NOTICE, chan, "Got check_hangup, but ignore_hangup set so sending 'I' command\n");
00581             send_eivr_event(eivr_events, 'I', "HANGUP", chan);
00582             hangup_info_sent = 1;
00583          } else {
00584             ast_chan_log(LOG_NOTICE, chan, "Got check_hangup\n");
00585             send_eivr_event(eivr_events, 'H', NULL, chan);
00586             res = -1;
00587             break;
00588          }
00589       }
00590  
00591       ready_fd = 0;
00592       ms = 100;
00593       errno = 0;
00594       exception = 0;
00595  
00596       rchan = ast_waitfor_nandfds(&chan, 1, waitfds, (eivr_errors_fd < 0) ? 1 : 2, &exception, &ready_fd, &ms);
00597  
00598       if (chan->_state == AST_STATE_UP && !AST_LIST_EMPTY(&u->finishlist)) {
00599          AST_LIST_LOCK(&u->finishlist);
00600          while ((entry = AST_LIST_REMOVE_HEAD(&u->finishlist, list))) {
00601             send_eivr_event(eivr_events, 'F', entry->filename, chan);
00602             ast_free(entry);
00603          }
00604          AST_LIST_UNLOCK(&u->finishlist);
00605       }
00606  
00607       if (chan->_state == AST_STATE_UP && !(ast_check_hangup(chan)) && rchan) {
00608          /* the channel has something */
00609          f = ast_read(chan);
00610          if (!f) {
00611             ast_chan_log(LOG_NOTICE, chan, "Returned no frame\n");
00612             send_eivr_event(eivr_events, 'H', NULL, chan);
00613             res = -1;
00614             break;
00615          }
00616          if (f->frametype == AST_FRAME_DTMF) {
00617             send_eivr_event(eivr_events, f->subclass, NULL, chan);
00618             if (u->option_autoclear) {
00619                if (!u->abort_current_sound && !u->playing_silence)
00620                   send_eivr_event(eivr_events, 'T', NULL, chan);
00621                AST_LIST_LOCK(&u->playlist);
00622                while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
00623                   send_eivr_event(eivr_events, 'D', entry->filename, chan);
00624                   ast_free(entry);
00625                }
00626                if (!u->playing_silence)
00627                   u->abort_current_sound = 1;
00628                AST_LIST_UNLOCK(&u->playlist);
00629             }
00630          } else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) {
00631             ast_chan_log(LOG_NOTICE, chan, "Got AST_CONTROL_HANGUP\n");
00632             send_eivr_event(eivr_events, 'H', NULL, chan);
00633             if (f->data.uint32) {
00634                chan->hangupcause = f->data.uint32;
00635             }
00636             ast_frfree(f);
00637             res = -1;
00638             break;
00639          }
00640          ast_frfree(f);
00641       } else if (ready_fd == eivr_commands_fd) {
00642          char input[1024];
00643  
00644          if (exception || (dup2(eivr_commands_fd, test_available_fd) == -1) || feof(eivr_commands)) {
00645             ast_chan_log(LOG_WARNING, chan, "Child process went away\n");
00646             res = -1;
00647             break;
00648          }
00649   
00650          if (!fgets(input, sizeof(input), eivr_commands))
00651             continue;
00652  
00653          command = ast_strip(input);
00654   
00655          if (option_debug)
00656             ast_debug(1, "got command '%s'\n", input);
00657   
00658          if (strlen(input) < 4)
00659             continue;
00660   
00661          if (input[0] == 'P') {
00662             send_eivr_event(eivr_events, 'P', args->str, chan);
00663          } else if ( input[0] == 'T' ) {
00664             ast_chan_log(LOG_WARNING, chan, "Answering channel if needed and starting generator\n");
00665             if (chan->_state != AST_STATE_UP) {
00666                if (ast_test_flag(&flags, run_dead)) {
00667                   ast_chan_log(LOG_WARNING, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n");
00668                   send_eivr_event(eivr_events, 'Z', "ANSWER_FAILURE", chan);
00669                   continue;
00670                }
00671                ast_answer(chan);
00672             }
00673             if (!(u->gen_active)) {
00674                if (ast_activate_generator(chan, &gen, u) < 0) {
00675                   ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n");
00676                   send_eivr_event(eivr_events, 'Z', "GENERATOR_FAILURE", chan);
00677                } else {
00678                   u->gen_active = 1;
00679                }
00680             }
00681          } else if (input[0] == 'S') {
00682             if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) {
00683                ast_chan_log(LOG_WARNING, chan, "Queue 'S'et called on unanswered channel\n");
00684                send_eivr_event(eivr_events, 'Z', NULL, chan);
00685                continue;
00686             }
00687             if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) {
00688                ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
00689                send_eivr_event(eivr_events, 'Z', NULL, chan);
00690                strcpy(&input[2], "exception");
00691             }
00692             if (!u->abort_current_sound && !u->playing_silence)
00693                send_eivr_event(eivr_events, 'T', NULL, chan);
00694             AST_LIST_LOCK(&u->playlist);
00695             while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
00696                send_eivr_event(eivr_events, 'D', entry->filename, chan);
00697                ast_free(entry);
00698             }
00699             if (!u->playing_silence)
00700                u->abort_current_sound = 1;
00701             entry = make_entry(&input[2]);
00702             if (entry)
00703                AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
00704             AST_LIST_UNLOCK(&u->playlist);
00705          } else if (input[0] == 'A') {
00706             if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) {
00707                ast_chan_log(LOG_WARNING, chan, "Queue 'A'ppend called on unanswered channel\n");
00708                send_eivr_event(eivr_events, 'Z', NULL, chan);
00709                continue;
00710             }
00711             if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) {
00712                ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
00713                send_eivr_event(eivr_events, 'Z', NULL, chan);
00714                strcpy(&input[2], "exception");
00715             }
00716             entry = make_entry(&input[2]);
00717             if (entry) {
00718                AST_LIST_LOCK(&u->playlist);
00719                AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
00720                AST_LIST_UNLOCK(&u->playlist);
00721             }
00722          } else if (input[0] == 'G') {
00723             /* A get variable message:  "G,variable1,variable2,..." */
00724             char response[2048];
00725 
00726             ast_chan_log(LOG_NOTICE, chan, "Getting a Variable out of the channel: %s\n", &input[2]);
00727             ast_eivr_getvariable(chan, &input[2], response, sizeof(response));
00728             send_eivr_event(eivr_events, 'G', response, chan);
00729          } else if (input[0] == 'V') {
00730             /* A set variable message:  "V,variablename=foo" */
00731             ast_chan_log(LOG_NOTICE, chan, "Setting a Variable up: %s\n", &input[2]);
00732             ast_eivr_setvariable(chan, &input[2]);
00733          } else if (input[0] == 'L') {
00734             ast_chan_log(LOG_NOTICE, chan, "Log message from EIVR: %s\n", &input[2]);
00735          } else if (input[0] == 'X') {
00736             ast_chan_log(LOG_NOTICE, chan, "Exiting ExternalIVR: %s\n", &input[2]);
00737             /*! \todo add deprecation debug message for X command here */
00738             res = 0;
00739             break;
00740          } else if (input[0] == 'E') {
00741             ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]);
00742             send_eivr_event(eivr_events, 'E', NULL, chan);
00743             res = 0;
00744             break;
00745          } else if (input[0] == 'H') {
00746             ast_chan_log(LOG_NOTICE, chan, "Hanging up: %s\n", &input[2]);
00747             send_eivr_event(eivr_events, 'H', NULL, chan);
00748             res = -1;
00749             break;
00750          } else if (input[0] == 'O') {
00751             if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) {
00752                ast_chan_log(LOG_WARNING, chan, "Option called on unanswered channel\n");
00753                send_eivr_event(eivr_events, 'Z', NULL, chan);
00754                continue;
00755             }
00756             if (!strcasecmp(&input[2], "autoclear"))
00757                u->option_autoclear = 1;
00758             else if (!strcasecmp(&input[2], "noautoclear"))
00759                u->option_autoclear = 0;
00760             else
00761                ast_chan_log(LOG_WARNING, chan, "Unknown option requested '%s'\n", &input[2]);
00762          }
00763       } else if (eivr_errors_fd && ready_fd == eivr_errors_fd) {
00764          char input[1024];
00765   
00766          if (exception || feof(eivr_errors)) {
00767             ast_chan_log(LOG_WARNING, chan, "Child process went away\n");
00768             res = -1;
00769             break;
00770          }
00771          if (fgets(input, sizeof(input), eivr_errors)) {
00772             command = ast_strip(input);
00773             ast_chan_log(LOG_NOTICE, chan, "stderr: %s\n", command);
00774          }
00775       } else if ((ready_fd < 0) && ms) { 
00776          if (errno == 0 || errno == EINTR)
00777             continue;
00778  
00779          ast_chan_log(LOG_WARNING, chan, "Wait failed (%s)\n", strerror(errno));
00780          break;
00781       }
00782    }
00783   
00784  
00785 exit:
00786  
00787    if (test_available_fd > -1) {
00788       close(test_available_fd);
00789    }
00790 
00791    if (eivr_events)
00792       fclose(eivr_events);
00793  
00794    if (eivr_commands)
00795       fclose(eivr_commands);
00796 
00797    if (eivr_errors)
00798       fclose(eivr_errors);
00799   
00800    return res;
00801  
00802 }

int eivr_connect_socket ( struct ast_channel chan,
const char *  host,
int  port 
)

static void* gen_alloc ( struct ast_channel chan,
void *  params 
) [static]

Definition at line 129 of file app_externalivr.c.

References ast_calloc, and gen_state::u.

00130 {
00131    struct ivr_localuser *u = params;
00132    struct gen_state *state;
00133 
00134    if (!(state = ast_calloc(1, sizeof(*state))))
00135       return NULL;
00136 
00137    state->u = u;
00138 
00139    return state;
00140 }

static void gen_closestream ( struct gen_state state  )  [static]

Definition at line 142 of file app_externalivr.c.

References ast_closestream(), ivr_localuser::chan, ast_channel::stream, gen_state::stream, and gen_state::u.

Referenced by gen_nextfile(), gen_readframe(), and gen_release().

00143 {
00144    if (!state->stream)
00145       return;
00146 
00147    ast_closestream(state->stream);
00148    state->u->chan->stream = NULL;
00149    state->stream = NULL;
00150 }

static int gen_generate ( struct ast_channel chan,
void *  data,
int  len,
int  samples 
) [static]

Definition at line 219 of file app_externalivr.c.

References ast_chan_log, ast_frfree, ast_write(), chan, errno, f, gen_readframe(), LOG_WARNING, and gen_state::sample_queue.

00220 {
00221    struct gen_state *state = data;
00222    struct ast_frame *f = NULL;
00223    int res = 0;
00224 
00225    state->sample_queue += samples;
00226 
00227    while (state->sample_queue > 0) {
00228       if (!(f = gen_readframe(state)))
00229          return -1;
00230 
00231       res = ast_write(chan, f);
00232       ast_frfree(f);
00233       if (res < 0) {
00234          ast_chan_log(LOG_WARNING, chan, "Failed to write frame: %s\n", strerror(errno));
00235          return -1;
00236       }
00237       state->sample_queue -= f->samples;
00238    }
00239 
00240    return res;
00241 }

static int gen_nextfile ( struct gen_state state  )  [static]

Definition at line 161 of file app_externalivr.c.

References ivr_localuser::abort_current_sound, ast_chan_log, AST_LIST_REMOVE_HEAD, ast_openstream_full(), ivr_localuser::chan, gen_state::current, errno, playlist_entry::filename, gen_closestream(), ast_channel::language, playlist_entry::list, LOG_WARNING, ivr_localuser::playing_silence, ivr_localuser::playlist, gen_state::stream, and gen_state::u.

Referenced by gen_readframe().

00162 {
00163    struct ivr_localuser *u = state->u;
00164    char *file_to_stream;
00165 
00166    u->abort_current_sound = 0;
00167    u->playing_silence = 0;
00168    gen_closestream(state);
00169 
00170    while (!state->stream) {
00171       state->current = AST_LIST_REMOVE_HEAD(&u->playlist, list);
00172       if (state->current) {
00173          file_to_stream = state->current->filename;
00174       } else {
00175          file_to_stream = "silence/10";
00176          u->playing_silence = 1;
00177       }
00178 
00179       if (!(state->stream = ast_openstream_full(u->chan, file_to_stream, u->chan->language, 1))) {
00180          ast_chan_log(LOG_WARNING, u->chan, "File '%s' could not be opened: %s\n", file_to_stream, strerror(errno));
00181          if (!u->playing_silence) {
00182             continue;
00183          } else {
00184             break;
00185          }
00186       }
00187    }
00188 
00189    return (!state->stream);
00190 }

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

Definition at line 192 of file app_externalivr.c.

References ivr_localuser::abort_current_sound, AST_LIST_FIRST, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_UNLOCK, ast_readframe(), gen_state::current, f, ivr_localuser::finishlist, gen_closestream(), gen_nextfile(), playlist_entry::list, ivr_localuser::playing_silence, ivr_localuser::playlist, gen_state::stream, and gen_state::u.

Referenced by gen_generate().

00193 {
00194    struct ast_frame *f = NULL;
00195    struct ivr_localuser *u = state->u;
00196 
00197    if (u->abort_current_sound ||
00198       (u->playing_silence && AST_LIST_FIRST(&u->playlist))) {
00199       gen_closestream(state);
00200       AST_LIST_LOCK(&u->playlist);
00201       gen_nextfile(state);
00202       AST_LIST_UNLOCK(&u->playlist);
00203    }
00204 
00205    if (!(state->stream && (f = ast_readframe(state->stream)))) {
00206       if (state->current) {
00207          AST_LIST_LOCK(&u->finishlist);
00208          AST_LIST_INSERT_TAIL(&u->finishlist, state->current, list);
00209          AST_LIST_UNLOCK(&u->finishlist);
00210          state->current = NULL;
00211       }
00212       if (!gen_nextfile(state))
00213          f = ast_readframe(state->stream);
00214    }
00215 
00216    return f;
00217 }

static void gen_release ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 152 of file app_externalivr.c.

References ast_free, and gen_closestream().

00153 {
00154    struct gen_state *state = data;
00155 
00156    gen_closestream(state);
00157    ast_free(data);
00158 }

static int load_module ( void   )  [static]

Definition at line 809 of file app_externalivr.c.

References app_exec, and ast_register_application.

00810 {
00811    return ast_register_application(app, app_exec, synopsis, descrip);
00812 }

static struct playlist_entry* make_entry ( const char *  filename  )  [static]

Definition at line 311 of file app_externalivr.c.

References ast_calloc.

Referenced by eivr_comm().

00312 {
00313    struct playlist_entry *entry;
00314 
00315    if (!(entry = ast_calloc(1, sizeof(*entry) + strlen(filename) + 10))) /* XXX why 10 ? */
00316       return NULL;
00317 
00318    strcpy(entry->filename, filename);
00319 
00320    return entry;
00321 }

static void send_eivr_event ( FILE *  handle,
const char  event,
const char *  data,
const struct ast_channel chan 
) [static]

Definition at line 115 of file app_externalivr.c.

References ast_debug, ast_str_append(), ast_str_create(), and ast_str::str.

Referenced by eivr_comm().

00117 {
00118    struct ast_str *tmp = ast_str_create(12);
00119 
00120    ast_str_append(&tmp, 0, "%c,%10d", event, (int)time(NULL));
00121    if (data) {
00122       ast_str_append(&tmp, 0, ",%s", data);
00123    }
00124 
00125    fprintf(handle, "%s\n", tmp->str);
00126    ast_debug(1, "sent '%s'\n", tmp->str);
00127 }

static int unload_module ( void   )  [static]

Definition at line 804 of file app_externalivr.c.

References ast_unregister_application().

00805 {
00806    return ast_unregister_application(app);
00807 }


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 814 of file app_externalivr.c.

const char* app = "ExternalIVR" [static]

Definition at line 51 of file app_externalivr.c.

struct ast_app_option app_opts[128] = { [ 'n' ] = { .flag = noanswer }, [ 'i' ] = { .flag = ignore_hangup }, [ 'd' ] = { .flag = run_dead },} [static]

Definition at line 84 of file app_externalivr.c.

struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 814 of file app_externalivr.c.

const char* descrip [static]

Definition at line 54 of file app_externalivr.c.

struct ast_generator gen [static]

Definition at line 243 of file app_externalivr.c.

Referenced by app_exec(), ast_activate_generator(), eivr_comm(), reload_config(), and set_config().

enum { ... } options_flags

const char* synopsis = "Interfaces with an external IVR application" [static]

Definition at line 53 of file app_externalivr.c.


Generated on Fri Jun 19 12:09:57 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7