Mon Aug 31 12:30:15 2015

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  playlist_entry

Defines

#define ast_chan_log(level, channel, format,...)   ast_log(level, "%s: " format, channel->name , ## __VA_ARGS__)
#define EIVR_CMD_ANS   'T'
#define EIVR_CMD_APND   'A'
#define EIVR_CMD_DTMF   'D'
#define EIVR_CMD_EXIT   'E'
#define EIVR_CMD_GET   'G'
#define EIVR_CMD_HGUP   'H'
#define EIVR_CMD_LOG   'L'
#define EIVR_CMD_OPT   'O'
#define EIVR_CMD_PARM   'P'
#define EIVR_CMD_SQUE   'S'
#define EIVR_CMD_SVAR   'V'
#define EIVR_CMD_XIT   'X'

Enumerations

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

Functions

static int app_exec (struct ast_channel *chan, const char *data)
 AST_APP_OPTIONS (app_opts,{AST_APP_OPTION('n', noanswer), AST_APP_OPTION('i', ignore_hangup), AST_APP_OPTION('d', run_dead),})
static void ast_eivr_getvariable (struct ast_channel *chan, char *data, char *outbuf, int outbuflen)
static void ast_eivr_senddtmf (struct ast_channel *chan, char *vdata)
static void ast_eivr_setvariable (struct ast_channel *chan, char *data)
 AST_MODULE_INFO_STANDARD (ASTERISK_GPL_KEY,"External IVR Interface Application")
static int eivr_comm (struct ast_channel *chan, struct ivr_localuser *u, int *eivr_events_fd, int *eivr_commands_fd, int *eivr_errors_fd, const struct ast_str *args, const struct ast_flags flags)
int eivr_connect_socket (struct ast_channel *chan, const char *host, int port)
static void * gen_alloc (struct ast_channel *chan, void *params)
static void gen_closestream (struct gen_state *state)
static int gen_generate (struct ast_channel *chan, void *data, int len, int samples)
static int gen_nextfile (struct gen_state *state)
static struct ast_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 const char app [] = "ExternalIVR"
static struct ast_generator gen

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

Referenced by app_exec(), eivr_comm(), gen_generate(), and gen_nextfile().

#define EIVR_CMD_ANS   'T'

Definition at line 112 of file app_externalivr.c.

Referenced by eivr_comm().

#define EIVR_CMD_APND   'A'

Definition at line 103 of file app_externalivr.c.

Referenced by eivr_comm().

#define EIVR_CMD_DTMF   'D'

Definition at line 104 of file app_externalivr.c.

Referenced by eivr_comm().

#define EIVR_CMD_EXIT   'E'

Definition at line 105 of file app_externalivr.c.

Referenced by eivr_comm().

#define EIVR_CMD_GET   'G'

Definition at line 106 of file app_externalivr.c.

Referenced by eivr_comm().

#define EIVR_CMD_HGUP   'H'

Definition at line 107 of file app_externalivr.c.

Referenced by eivr_comm().

#define EIVR_CMD_LOG   'L'

Definition at line 108 of file app_externalivr.c.

Referenced by eivr_comm().

#define EIVR_CMD_OPT   'O'

Definition at line 109 of file app_externalivr.c.

Referenced by eivr_comm().

#define EIVR_CMD_PARM   'P'

Definition at line 110 of file app_externalivr.c.

Referenced by eivr_comm().

#define EIVR_CMD_SQUE   'S'

Definition at line 111 of file app_externalivr.c.

Referenced by eivr_comm().

#define EIVR_CMD_SVAR   'V'

Definition at line 113 of file app_externalivr.c.

Referenced by eivr_comm().

#define EIVR_CMD_XIT   'X'

Definition at line 114 of file app_externalivr.c.

Referenced by eivr_comm().


Enumeration Type Documentation

Enumerator:
noanswer 
ignore_hangup 
run_dead 

Definition at line 116 of file app_externalivr.c.

00116                    {
00117    noanswer = (1 << 0),
00118    ignore_hangup = (1 << 1),
00119    run_dead = (1 << 2),
00120 };


Function Documentation

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

Definition at line 389 of file app_externalivr.c.

References ast_channel::_state, ast_tcptls_session_args::accept_fd, ao2_ref, ast_activate_generator(), ast_answer(), AST_APP_ARG, ast_app_parse_options(), ast_chan_log, ast_close_fds_above_n(), ast_copy_string(), ast_deactivate_generator(), ast_debug, AST_DECLARE_APP_ARGS, ast_free, ast_gethostbyname(), AST_LIST_HEAD_INIT_VALUE, AST_LIST_REMOVE_HEAD, ast_log(), ast_opt_high_priority, ast_safe_fork(), ast_set_priority(), ast_sockaddr_from_sin, AST_STANDARD_APP_ARGS, AST_STATE_UP, ast_str_alloca, ast_str_append(), ast_str_reset(), ast_strdupa, ast_strlen_zero(), ast_tcptls_client_create(), ast_tcptls_client_start(), ast_test_flag, ast_verb, ivr_localuser::chan, eivr_comm(), errno, ast_tcptls_session_instance::fd, gen, hostname, ast_hostent::hp, ignore_hangup, LOG_ERROR, noanswer, ast_tcptls_session_args::remote_address, and run_dead.

Referenced by load_module().

00390 {
00391    struct ast_flags flags = { 0, };
00392    char *opts[0];
00393    struct playlist_entry *entry;
00394    int child_stdin[2] = { -1, -1 };
00395    int child_stdout[2] = { -1, -1 };
00396    int child_stderr[2] = { -1, -1 };
00397    int res = -1;
00398    int pid;
00399 
00400    char hostname[1024];
00401    char *port_str = NULL;
00402    int port = 0;
00403    struct ast_tcptls_session_instance *ser = NULL;
00404 
00405    struct ivr_localuser foo = {
00406       .playlist = AST_LIST_HEAD_INIT_VALUE,
00407       .finishlist = AST_LIST_HEAD_INIT_VALUE,
00408       .gen_active = 0,
00409    };
00410    struct ivr_localuser *u = &foo;
00411 
00412    char *buf;
00413    int j;
00414    char *s, **app_args, *e; 
00415    struct ast_str *comma_delim_args = ast_str_alloca(100);
00416 
00417    AST_DECLARE_APP_ARGS(eivr_args,
00418       AST_APP_ARG(application);
00419       AST_APP_ARG(options);
00420    );
00421    AST_DECLARE_APP_ARGS(application_args,
00422       AST_APP_ARG(cmd)[32];
00423    );
00424 
00425    u->abort_current_sound = 0;
00426    u->chan = chan;
00427 
00428    if (ast_strlen_zero(data)) {
00429       ast_log(LOG_ERROR, "ExternalIVR requires a command to execute\n");
00430       goto exit;
00431    }
00432 
00433    buf = ast_strdupa(data);
00434    AST_STANDARD_APP_ARGS(eivr_args, buf);
00435 
00436    ast_verb(4, "ExternalIVR received application and arguments: %s\n", eivr_args.application);
00437    ast_verb(4, "ExternalIVR received options: %s\n", eivr_args.options);
00438 
00439    /* Parse out any application arguments */
00440    if ((s = strchr(eivr_args.application, '('))) {
00441       s[0] = ',';
00442       if ((e = strrchr(s, ')'))) {
00443          *e = '\0';
00444       } else {
00445          ast_log(LOG_ERROR, "Parse error, missing closing parenthesis\n");
00446          goto exit;
00447       }
00448    }
00449 
00450    AST_STANDARD_APP_ARGS(application_args, eivr_args.application);
00451    app_args = application_args.argv;
00452 
00453    /* Put the application + the arguments in a , delimited list */
00454    ast_str_reset(comma_delim_args);
00455    for (j = 0; application_args.cmd[j] != NULL; j++) {
00456       ast_str_append(&comma_delim_args, 0, "%s%s", j == 0 ? "" : ",", application_args.cmd[j]);
00457    }
00458 
00459    /* Get rid of any extraneous arguments */
00460    if (eivr_args.options && (s = strchr(eivr_args.options, ','))) {
00461       *s = '\0';
00462    }
00463 
00464    /* Parse the ExternalIVR() arguments */
00465    ast_verb(4, "Parsing options from: [%s]\n", eivr_args.options);
00466    ast_app_parse_options(app_opts, &flags, opts, eivr_args.options);
00467    if (ast_test_flag(&flags, noanswer)) {
00468       ast_verb(4, "noanswer is set\n");
00469    }
00470    if (ast_test_flag(&flags, ignore_hangup)) {
00471       ast_verb(4, "ignore_hangup is set\n");
00472    }
00473    if (ast_test_flag(&flags, run_dead)) {
00474       ast_verb(4, "run_dead is set\n");
00475    }
00476    
00477    if (!(ast_test_flag(&flags, noanswer))) {
00478       ast_verb(3, "Answering channel and starting generator\n");
00479       if (chan->_state != AST_STATE_UP) {
00480          if (ast_test_flag(&flags, run_dead)) {
00481             ast_chan_log(LOG_ERROR, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n");
00482             goto exit;
00483          }
00484          ast_answer(chan);
00485       }
00486       if (ast_activate_generator(chan, &gen, u) < 0) {
00487          ast_chan_log(LOG_ERROR, chan, "Failed to activate generator\n");
00488          goto exit;
00489       } else {
00490          u->gen_active = 1;
00491       }
00492    }
00493 
00494    if (!strncmp(app_args[0], "ivr://", 6)) {
00495       struct ast_tcptls_session_args ivr_desc = {
00496          .accept_fd = -1,
00497          .name = "IVR",
00498       };
00499       struct ast_hostent hp;
00500       struct sockaddr_in remote_address_tmp;
00501 
00502       /*communicate through socket to server*/
00503       ast_debug(1, "Parsing hostname:port for socket connect from \"%s\"\n", app_args[0]);
00504       ast_copy_string(hostname, app_args[0] + 6, sizeof(hostname));
00505       if ((port_str = strchr(hostname, ':')) != NULL) {
00506          port_str[0] = 0;
00507          port_str += 1;
00508          port = atoi(port_str);
00509       }
00510       if (!port) {
00511          port = 2949;  /* default port, if one is not provided */
00512       }
00513 
00514       ast_gethostbyname(hostname, &hp);
00515       remote_address_tmp.sin_family = AF_INET;
00516       remote_address_tmp.sin_port = htons(port);
00517       memcpy(&remote_address_tmp.sin_addr.s_addr, hp.hp.h_addr, hp.hp.h_length);
00518       ast_sockaddr_from_sin(&ivr_desc.remote_address, &remote_address_tmp);
00519       if (!(ser = ast_tcptls_client_create(&ivr_desc)) || !(ser = ast_tcptls_client_start(ser))) {
00520          goto exit;
00521       }
00522       res = eivr_comm(chan, u, &ser->fd, &ser->fd, NULL, comma_delim_args, flags);
00523 
00524    } else {
00525       if (pipe(child_stdin)) {
00526          ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child input: %s\n", strerror(errno));
00527          goto exit;
00528       }
00529       if (pipe(child_stdout)) {
00530          ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child output: %s\n", strerror(errno));
00531          goto exit;
00532       }
00533       if (pipe(child_stderr)) {
00534          ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child errors: %s\n", strerror(errno));
00535          goto exit;
00536       }
00537    
00538       pid = ast_safe_fork(0);
00539       if (pid < 0) {
00540          ast_log(LOG_ERROR, "Failed to fork(): %s\n", strerror(errno));
00541          goto exit;
00542       }
00543    
00544       if (!pid) {
00545          /* child process */
00546          if (ast_opt_high_priority)
00547             ast_set_priority(0);
00548    
00549          dup2(child_stdin[0], STDIN_FILENO);
00550          dup2(child_stdout[1], STDOUT_FILENO);
00551          dup2(child_stderr[1], STDERR_FILENO);
00552          ast_close_fds_above_n(STDERR_FILENO);
00553          execv(app_args[0], app_args);
00554          fprintf(stderr, "Failed to execute '%s': %s\n", app_args[0], strerror(errno));
00555          _exit(1);
00556       } else {
00557          /* parent process */
00558          close(child_stdin[0]);
00559          child_stdin[0] = -1;
00560          close(child_stdout[1]);
00561          child_stdout[1] = -1;
00562          close(child_stderr[1]);
00563          child_stderr[1] = -1;
00564          res = eivr_comm(chan, u, &child_stdin[1], &child_stdout[0], &child_stderr[0], comma_delim_args, flags);
00565       }
00566    }
00567 
00568    exit:
00569    if (u->gen_active) {
00570       ast_deactivate_generator(chan);
00571    }
00572    if (child_stdin[0] > -1) {
00573       close(child_stdin[0]);
00574    }
00575    if (child_stdin[1] > -1) {
00576       close(child_stdin[1]);
00577    }
00578    if (child_stdout[0] > -1) {
00579       close(child_stdout[0]);
00580    }
00581    if (child_stdout[1] > -1) {
00582       close(child_stdout[1]);
00583    }
00584    if (child_stderr[0] > -1) {
00585       close(child_stderr[0]);
00586    }
00587    if (child_stderr[1] > -1) {
00588       close(child_stderr[1]);
00589    }
00590    if (ser) {
00591       ao2_ref(ser, -1);
00592    }
00593    while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
00594       ast_free(entry);
00595    }
00596    return res;
00597 }

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

Definition at line 301 of file app_externalivr.c.

References ast_channel_lock, ast_channel_unlock, ast_copy_string(), ast_str_alloca, ast_str_append(), ast_str_buffer(), inbuf(), pbx_builtin_getvar_helper(), and value.

Referenced by eivr_comm().

00302 {
00303    /* original input data: "G,var1,var2," */
00304    /* data passed as "data":  "var1,var2" */
00305 
00306    char *inbuf, *variable;
00307    const char *value;
00308    int j;
00309    struct ast_str *newstring = ast_str_alloca(outbuflen); 
00310 
00311    outbuf[0] = '\0';
00312 
00313    for (j = 1, inbuf = data; ; j++) {
00314       variable = strsep(&inbuf, ",");
00315       if (variable == NULL) {
00316          int outstrlen = strlen(outbuf);
00317          if (outstrlen && outbuf[outstrlen - 1] == ',') {
00318             outbuf[outstrlen - 1] = 0;
00319          }
00320          break;
00321       }
00322       
00323       ast_channel_lock(chan);
00324       if (!(value = pbx_builtin_getvar_helper(chan, variable))) {
00325          value = "";
00326       }
00327 
00328       ast_str_append(&newstring, 0, "%s=%s,", variable, value);
00329       ast_channel_unlock(chan);
00330       ast_copy_string(outbuf, ast_str_buffer(newstring), outbuflen);
00331    }
00332 }

static void ast_eivr_senddtmf ( struct ast_channel chan,
char *  vdata 
) [static]

Definition at line 353 of file app_externalivr.c.

References args, AST_APP_ARG, ast_app_parse_timelen(), AST_DECLARE_APP_ARGS, ast_dtmf_stream(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_verb, and TIMELEN_MILLISECONDS.

Referenced by eivr_comm().

00354 {
00355 
00356    char *data;
00357    int dinterval = 0, duration = 0;
00358    AST_DECLARE_APP_ARGS(args,
00359       AST_APP_ARG(digits);
00360       AST_APP_ARG(dinterval);
00361       AST_APP_ARG(duration);
00362    );
00363 
00364    data = ast_strdupa(vdata);
00365    AST_STANDARD_APP_ARGS(args, data);
00366 
00367    if (!ast_strlen_zero(args.dinterval)) {
00368       ast_app_parse_timelen(args.dinterval, &dinterval, TIMELEN_MILLISECONDS);
00369    }
00370    if (!ast_strlen_zero(args.duration)) {
00371       ast_app_parse_timelen(args.duration, &duration, TIMELEN_MILLISECONDS);
00372    }
00373    ast_verb(4, "Sending DTMF: %s %d %d\n", args.digits, dinterval <= 0 ? 250 : dinterval, duration);
00374    ast_dtmf_stream(chan, NULL, args.digits, dinterval <= 0 ? 250 : dinterval, duration);
00375 }

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

Definition at line 334 of file app_externalivr.c.

References ast_debug, ast_strdupa, inbuf(), pbx_builtin_setvar_helper(), and value.

Referenced by eivr_comm().

00335 {
00336    char *value;
00337 
00338    char *inbuf = ast_strdupa(data), *variable;
00339 
00340    for (variable = strsep(&inbuf, ","); variable; variable = strsep(&inbuf, ",")) {
00341       ast_debug(1, "Setting up a variable: %s\n", variable);
00342       /* variable contains "varname=value" */
00343       value = strchr(variable, '=');
00344       if (!value) {
00345          value = "";
00346       } else {
00347          *value++ = '\0';
00348       }
00349       pbx_builtin_setvar_helper(chan, variable, value);
00350    }
00351 }

AST_MODULE_INFO_STANDARD ( ASTERISK_GPL_KEY  ,
"External IVR Interface Application"   
)
static int eivr_comm ( struct ast_channel chan,
struct ivr_localuser u,
int *  eivr_events_fd,
int *  eivr_commands_fd,
int *  eivr_errors_fd,
const struct ast_str args,
const struct ast_flags  flags 
) [static]

Definition at line 599 of file app_externalivr.c.

References ast_channel::_state, ast_activate_generator(), ast_answer(), ast_chan_log, ast_check_hangup(), AST_CONTROL_HANGUP, ast_eivr_getvariable(), ast_eivr_senddtmf(), ast_eivr_setvariable(), ast_fileexists(), AST_FLAG_ZOMBIE, AST_FRAME_CONTROL, AST_FRAME_DTMF, ast_free, ast_frfree, AST_LIST_EMPTY, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, ast_read(), AST_STATE_UP, ast_str_buffer(), ast_strip(), ast_test_flag, ast_verb, ast_waitfor_nandfds(), ivr_localuser::chan, ast_frame::data, EIVR_CMD_ANS, EIVR_CMD_APND, EIVR_CMD_DTMF, EIVR_CMD_EXIT, EIVR_CMD_GET, EIVR_CMD_HGUP, EIVR_CMD_LOG, EIVR_CMD_OPT, EIVR_CMD_PARM, EIVR_CMD_SQUE, EIVR_CMD_SVAR, EIVR_CMD_XIT, errno, f, ast_frame::frametype, gen, ast_channel::hangupcause, ignore_hangup, input(), ast_frame_subclass::integer, LOG_ERROR, LOG_NOTICE, LOG_WARNING, make_entry(), run_dead, send_eivr_event(), ast_frame::subclass, and ast_frame::uint32.

Referenced by app_exec().

00602 {
00603    struct playlist_entry *entry;
00604    struct ast_frame *f;
00605    int ms;
00606    int exception;
00607    int ready_fd;
00608    int waitfds[2] = { *eivr_commands_fd, (eivr_errors_fd) ? *eivr_errors_fd : -1 };
00609    struct ast_channel *rchan;
00610    int res = -1;
00611    int test_available_fd = -1;
00612    int hangup_info_sent = 0;
00613   
00614    FILE *eivr_commands = NULL;
00615    FILE *eivr_errors = NULL;
00616    FILE *eivr_events = NULL;
00617 
00618    if (!(eivr_events = fdopen(*eivr_events_fd, "w"))) {
00619       ast_chan_log(LOG_ERROR, chan, "Could not open stream to send events\n");
00620       goto exit;
00621    }
00622    if (!(eivr_commands = fdopen(*eivr_commands_fd, "r"))) {
00623       ast_chan_log(LOG_ERROR, chan, "Could not open stream to receive commands\n");
00624       goto exit;
00625    }
00626    if (eivr_errors_fd) {  /* if opening a socket connection, error stream will not be used */
00627       if (!(eivr_errors = fdopen(*eivr_errors_fd, "r"))) {
00628          ast_chan_log(LOG_ERROR, chan, "Could not open stream to receive errors\n");
00629          goto exit;
00630       }
00631    }
00632 
00633    test_available_fd = open("/dev/null", O_RDONLY);
00634  
00635    setvbuf(eivr_events, NULL, _IONBF, 0);
00636    setvbuf(eivr_commands, NULL, _IONBF, 0);
00637    if (eivr_errors) {
00638       setvbuf(eivr_errors, NULL, _IONBF, 0);
00639    }
00640 
00641    while (1) {
00642       if (ast_test_flag(chan, AST_FLAG_ZOMBIE)) {
00643          ast_chan_log(LOG_ERROR, chan, "Is a zombie\n");
00644          break;
00645       }
00646       if (!hangup_info_sent && !(ast_test_flag(&flags, run_dead)) && ast_check_hangup(chan)) {
00647          if (ast_test_flag(&flags, ignore_hangup)) {
00648             ast_verb(3, "Got check_hangup, but ignore_hangup set so sending 'I' command\n");
00649             send_eivr_event(eivr_events, 'I', "HANGUP", chan);
00650             hangup_info_sent = 1;
00651          } else {
00652             ast_verb(3, "Got check_hangup\n");
00653             send_eivr_event(eivr_events, 'H', NULL, chan);
00654             break;
00655          }
00656       }
00657  
00658       ready_fd = 0;
00659       ms = 100;
00660       errno = 0;
00661       exception = 0;
00662  
00663       rchan = ast_waitfor_nandfds(&chan, 1, waitfds, (eivr_errors_fd) ? 2 : 1, &exception, &ready_fd, &ms);
00664  
00665       if (chan->_state == AST_STATE_UP && !AST_LIST_EMPTY(&u->finishlist)) {
00666          AST_LIST_LOCK(&u->finishlist);
00667          while ((entry = AST_LIST_REMOVE_HEAD(&u->finishlist, list))) {
00668             send_eivr_event(eivr_events, 'F', entry->filename, chan);
00669             ast_free(entry);
00670          }
00671          AST_LIST_UNLOCK(&u->finishlist);
00672       }
00673  
00674       if (chan->_state == AST_STATE_UP && !(ast_check_hangup(chan)) && rchan) {
00675          /* the channel has something */
00676          f = ast_read(chan);
00677          if (!f) {
00678             ast_verb(3, "Returned no frame\n");
00679             send_eivr_event(eivr_events, 'H', NULL, chan);
00680             break;
00681          }
00682          if (f->frametype == AST_FRAME_DTMF) {
00683             send_eivr_event(eivr_events, f->subclass.integer, NULL, chan);
00684             if (u->option_autoclear) {
00685                AST_LIST_LOCK(&u->playlist);
00686                if (!u->abort_current_sound && !u->playing_silence) {
00687                   /* send interrupted file as T data */
00688                   if ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
00689                      send_eivr_event(eivr_events, 'T', entry->filename, chan);
00690                      ast_free(entry);
00691                   }
00692                }
00693                while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
00694                   send_eivr_event(eivr_events, 'D', entry->filename, chan);
00695                   ast_free(entry);
00696                }
00697                if (!u->playing_silence)
00698                   u->abort_current_sound = 1;
00699                AST_LIST_UNLOCK(&u->playlist);
00700             }
00701          } else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP)) {
00702             ast_verb(3, "Got AST_CONTROL_HANGUP\n");
00703             send_eivr_event(eivr_events, 'H', NULL, chan);
00704             if (f->data.uint32) {
00705                chan->hangupcause = f->data.uint32;
00706             }
00707             ast_frfree(f);
00708             break;
00709          }
00710          ast_frfree(f);
00711       } else if (ready_fd == *eivr_commands_fd) {
00712          char input[1024];
00713  
00714          if (exception || (dup2(*eivr_commands_fd, test_available_fd) == -1) || feof(eivr_commands)) {
00715             ast_chan_log(LOG_ERROR, chan, "Child process went away\n");
00716             break;
00717          }
00718   
00719          if (!fgets(input, sizeof(input), eivr_commands)) {
00720             continue;
00721          }
00722 
00723          ast_strip(input);
00724          ast_verb(4, "got command '%s'\n", input);
00725 
00726          if (strlen(input) < 3) {
00727             continue;
00728          }
00729 
00730          if (input[0] == EIVR_CMD_PARM) {
00731             struct ast_str *tmp = (struct ast_str *) args;
00732             send_eivr_event(eivr_events, 'P', ast_str_buffer(tmp), chan);
00733          } else if (input[0] == EIVR_CMD_DTMF) {
00734             ast_verb(4, "Sending DTMF: %s\n", &input[2]);
00735             ast_eivr_senddtmf(chan, &input[2]);
00736          } else if (input[0] == EIVR_CMD_ANS) {
00737             ast_verb(3, "Answering channel if needed and starting generator\n");
00738             if (chan->_state != AST_STATE_UP) {
00739                if (ast_test_flag(&flags, run_dead)) {
00740                   ast_chan_log(LOG_WARNING, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n");
00741                   send_eivr_event(eivr_events, 'Z', "ANSWER_FAILURE", chan);
00742                   continue;
00743                }
00744                if (ast_answer(chan)) {
00745                   ast_chan_log(LOG_WARNING, chan, "Failed to answer channel\n");
00746                   send_eivr_event(eivr_events, 'Z', "ANSWER_FAILURE", chan);
00747                   continue;
00748                }
00749             }
00750             if (!(u->gen_active)) {
00751                if (ast_activate_generator(chan, &gen, u) < 0) {
00752                   ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n");
00753                   send_eivr_event(eivr_events, 'Z', "GENERATOR_FAILURE", chan);
00754                } else {
00755                   u->gen_active = 1;
00756                }
00757             }
00758          } else if (input[0] == EIVR_CMD_SQUE) {
00759             if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) {
00760                ast_chan_log(LOG_WARNING, chan, "Queue re'S'et called on unanswered channel\n");
00761                send_eivr_event(eivr_events, 'Z', NULL, chan);
00762                continue;
00763             }
00764             if (!ast_fileexists(&input[2], NULL, u->chan->language)) {
00765                ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
00766                send_eivr_event(eivr_events, 'Z', &input[2], chan);
00767             } else {
00768                AST_LIST_LOCK(&u->playlist);
00769                if (!u->abort_current_sound && !u->playing_silence) {
00770                   /* send interrupted file as T data */
00771                   if ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
00772                      send_eivr_event(eivr_events, 'T', entry->filename, chan);
00773                      ast_free(entry);
00774                   }
00775                }
00776                while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
00777                   send_eivr_event(eivr_events, 'D', entry->filename, chan);
00778                   ast_free(entry);
00779                }
00780                if (!u->playing_silence) {
00781                   u->abort_current_sound = 1;
00782                }
00783                entry = make_entry(&input[2]);
00784                if (entry) {
00785                   AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
00786                }
00787                AST_LIST_UNLOCK(&u->playlist);
00788             }
00789          } else if (input[0] == EIVR_CMD_APND) {
00790             if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) {
00791                ast_chan_log(LOG_WARNING, chan, "Queue 'A'ppend called on unanswered channel\n");
00792                send_eivr_event(eivr_events, 'Z', NULL, chan);
00793                continue;
00794             }
00795             if (!ast_fileexists(&input[2], NULL, u->chan->language)) {
00796                ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
00797                send_eivr_event(eivr_events, 'Z', &input[2], chan);
00798             } else {
00799                entry = make_entry(&input[2]);
00800                if (entry) {
00801                   AST_LIST_LOCK(&u->playlist);
00802                   AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
00803                   AST_LIST_UNLOCK(&u->playlist);
00804                }
00805             }
00806          } else if (input[0] == EIVR_CMD_GET) {
00807             char response[2048];
00808             ast_verb(4, "Retriving Variables from channel: %s\n", &input[2]);
00809             ast_eivr_getvariable(chan, &input[2], response, sizeof(response));
00810             send_eivr_event(eivr_events, 'G', response, chan);
00811          } else if (input[0] == EIVR_CMD_SVAR) {
00812             ast_verb(4, "Setting Variables in channel: %s\n", &input[2]);
00813             ast_eivr_setvariable(chan, &input[2]);
00814          } else if (input[0] == EIVR_CMD_LOG) {
00815             ast_chan_log(LOG_NOTICE, chan, "Log message from EIVR: %s\n", &input[2]);
00816          } else if (input[0] == EIVR_CMD_XIT) {
00817             ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]);
00818             ast_chan_log(LOG_WARNING, chan, "e'X'it command is depricated, use 'E'xit instead\n");
00819             res = 0;
00820             break;
00821          } else if (input[0] == EIVR_CMD_EXIT) {
00822             ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]);
00823             send_eivr_event(eivr_events, 'E', NULL, chan);
00824             res = 0;
00825             break;
00826          } else if (input[0] == EIVR_CMD_HGUP) {
00827             ast_chan_log(LOG_NOTICE, chan, "Hanging up: %s\n", &input[2]);
00828             send_eivr_event(eivr_events, 'H', NULL, chan);
00829             break;
00830          } else if (input[0] == EIVR_CMD_OPT) {
00831             if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) {
00832                ast_chan_log(LOG_WARNING, chan, "Option called on unanswered channel\n");
00833                send_eivr_event(eivr_events, 'Z', NULL, chan);
00834                continue;
00835             }
00836             if (!strcasecmp(&input[2], "autoclear"))
00837                u->option_autoclear = 1;
00838             else if (!strcasecmp(&input[2], "noautoclear"))
00839                u->option_autoclear = 0;
00840             else
00841                ast_chan_log(LOG_WARNING, chan, "Unknown option requested: %s\n", &input[2]);
00842          }
00843       } else if (eivr_errors_fd && (ready_fd == *eivr_errors_fd)) {
00844          char input[1024];
00845   
00846          if (exception || feof(eivr_errors)) {
00847             ast_chan_log(LOG_ERROR, chan, "Child process went away\n");
00848             break;
00849          }
00850          if (fgets(input, sizeof(input), eivr_errors)) {
00851             ast_chan_log(LOG_NOTICE, chan, "stderr: %s\n", ast_strip(input));
00852          }
00853       } else if ((ready_fd < 0) && ms) { 
00854          if (errno == 0 || errno == EINTR)
00855             continue;
00856  
00857          ast_chan_log(LOG_ERROR, chan, "Wait failed (%s)\n", strerror(errno));
00858          break;
00859       }
00860    }
00861  
00862    exit:
00863    if (test_available_fd > -1) {
00864       close(test_available_fd);
00865    }
00866    if (eivr_events) {
00867       fclose(eivr_events);
00868       *eivr_events_fd = -1;
00869    }
00870    if (eivr_commands) {
00871       fclose(eivr_commands);
00872       *eivr_commands_fd = -1;
00873    }
00874    if (eivr_errors) {
00875       fclose(eivr_errors);
00876       *eivr_errors_fd = -1;
00877    }
00878    return res;
00879 }

int eivr_connect_socket ( struct ast_channel chan,
const char *  host,
int  port 
)
static void* gen_alloc ( struct ast_channel chan,
void *  params 
) [static]

Definition at line 172 of file app_externalivr.c.

References ast_calloc, and gen_state::u.

00173 {
00174    struct ivr_localuser *u = params;
00175    struct gen_state *state;
00176 
00177    if (!(state = ast_calloc(1, sizeof(*state))))
00178       return NULL;
00179 
00180    state->u = u;
00181 
00182    return state;
00183 }

static void gen_closestream ( struct gen_state state  )  [static]

Definition at line 185 of file app_externalivr.c.

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

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

00186 {
00187    if (!state->stream)
00188       return;
00189 
00190    ast_closestream(state->stream);
00191    state->u->chan->stream = NULL;
00192    state->stream = NULL;
00193 }

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

Definition at line 270 of file app_externalivr.c.

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

00271 {
00272    struct gen_state *state = data;
00273    struct ast_frame *f = NULL;
00274    int res = 0;
00275 
00276    state->sample_queue += samples;
00277 
00278    while (state->sample_queue > 0) {
00279       if (!(f = gen_readframe(state)))
00280          return -1;
00281 
00282       res = ast_write(chan, f);
00283       ast_frfree(f);
00284       if (res < 0) {
00285          ast_chan_log(LOG_WARNING, chan, "Failed to write frame: %s\n", strerror(errno));
00286          return -1;
00287       }
00288       state->sample_queue -= f->samples;
00289    }
00290 
00291    return res;
00292 }

static int gen_nextfile ( struct gen_state state  )  [static]

Definition at line 204 of file app_externalivr.c.

References ast_chan_log, AST_LIST_FIRST, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, ast_openstream_full(), ivr_localuser::chan, gen_state::current, errno, gen_closestream(), LOG_WARNING, gen_state::stream, and gen_state::u.

Referenced by gen_readframe().

00205 {
00206    struct ivr_localuser *u = state->u;
00207    char *file_to_stream;
00208 
00209    u->abort_current_sound = 0;
00210    u->playing_silence = 0;
00211    gen_closestream(state);
00212 
00213    while (!state->stream) {
00214       state->current = AST_LIST_FIRST(&u->playlist);
00215       if (state->current) {
00216          file_to_stream = state->current->filename;
00217       } else {
00218          file_to_stream = "silence/10";
00219          u->playing_silence = 1;
00220       }
00221 
00222       if (!(state->stream = ast_openstream_full(u->chan, file_to_stream, u->chan->language, 1))) {
00223          ast_chan_log(LOG_WARNING, u->chan, "File '%s' could not be opened: %s\n", file_to_stream, strerror(errno));
00224          AST_LIST_LOCK(&u->playlist);
00225          AST_LIST_REMOVE_HEAD(&u->playlist, list);
00226          AST_LIST_UNLOCK(&u->playlist);
00227          if (!u->playing_silence) {
00228             continue;
00229          } else {
00230             break;
00231          }
00232       }
00233    }
00234 
00235    return (!state->stream);
00236 }

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

Definition at line 238 of file app_externalivr.c.

References AST_LIST_FIRST, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, ast_readframe(), gen_state::current, f, gen_closestream(), gen_nextfile(), gen_state::stream, and gen_state::u.

Referenced by gen_generate().

00239 {
00240    struct ast_frame *f = NULL;
00241    struct ivr_localuser *u = state->u;
00242 
00243    if (u->abort_current_sound ||
00244       (u->playing_silence && AST_LIST_FIRST(&u->playlist))) {
00245       gen_closestream(state);
00246       AST_LIST_LOCK(&u->playlist);
00247       gen_nextfile(state);
00248       AST_LIST_UNLOCK(&u->playlist);
00249    }
00250 
00251    if (!(state->stream && (f = ast_readframe(state->stream)))) {
00252       if (state->current) {
00253          /* remove finished file from playlist */
00254                         AST_LIST_LOCK(&u->playlist);
00255                         AST_LIST_REMOVE_HEAD(&u->playlist, list);
00256                         AST_LIST_UNLOCK(&u->playlist);
00257          /* add finished file to finishlist */
00258          AST_LIST_LOCK(&u->finishlist);
00259          AST_LIST_INSERT_TAIL(&u->finishlist, state->current, list);
00260          AST_LIST_UNLOCK(&u->finishlist);
00261          state->current = NULL;
00262       }
00263       if (!gen_nextfile(state))
00264          f = ast_readframe(state->stream);
00265    }
00266 
00267    return f;
00268 }

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

Definition at line 195 of file app_externalivr.c.

References ast_free, and gen_closestream().

00196 {
00197    struct gen_state *state = data;
00198 
00199    gen_closestream(state);
00200    ast_free(data);
00201 }

static int load_module ( void   )  [static]

Definition at line 886 of file app_externalivr.c.

References app_exec(), and ast_register_application_xml.

00887 {
00888    return ast_register_application_xml(app, app_exec);
00889 }

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

Definition at line 377 of file app_externalivr.c.

References ast_calloc.

Referenced by eivr_comm().

00378 {
00379    struct playlist_entry *entry;
00380 
00381    if (!(entry = ast_calloc(1, sizeof(*entry) + strlen(filename) + 10))) /* XXX why 10 ? */
00382       return NULL;
00383 
00384    strcpy(entry->filename, filename);
00385 
00386    return entry;
00387 }

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

Definition at line 157 of file app_externalivr.c.

References ast_debug, ast_free, ast_str_append(), ast_str_buffer(), and ast_str_create().

Referenced by eivr_comm().

00159 {
00160    struct ast_str *tmp = ast_str_create(12);
00161 
00162    ast_str_append(&tmp, 0, "%c,%10d", event, (int)time(NULL));
00163    if (data) {
00164       ast_str_append(&tmp, 0, ",%s", data);
00165    }
00166 
00167    fprintf(handle, "%s\n", ast_str_buffer(tmp));
00168    ast_debug(1, "sent '%s'\n", ast_str_buffer(tmp));
00169    ast_free(tmp);
00170 }

static int unload_module ( void   )  [static]

Definition at line 881 of file app_externalivr.c.

References ast_unregister_application().

00882 {
00883    return ast_unregister_application(app);
00884 }


Variable Documentation

const char app[] = "ExternalIVR" [static]

Definition at line 97 of file app_externalivr.c.

struct ast_generator gen [static]

Definition at line 294 of file app_externalivr.c.

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


Generated on 31 Aug 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1