Wed Aug 18 22:33:58 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"

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

static void __unreg_module ( void   )  [static]

Definition at line 807 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_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.

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

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_debug, ast_strdupa, chan, 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 521 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.

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

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

References app_exec, and ast_register_application.

00803 {
00804    return ast_register_application(app, app_exec, synopsis, descrip);
00805 }

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

Definition at line 302 of file app_externalivr.c.

References ast_calloc.

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

References ast_unregister_application().

00798 {
00799    return ast_unregister_application(app);
00800 }


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 807 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 807 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 Wed Aug 18 22:33:58 2010 for Asterisk - the Open Source PBX by  doxygen 1.4.7