app_externalivr.c File Reference

External IVR application interface. More...

#include "asterisk.h"
#include <signal.h>
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/linkedlists.h"
#include "asterisk/app.h"
#include "asterisk/utils.h"
#include "asterisk/tcptls.h"
#include "asterisk/astobj2.h"

Include dependency graph for app_externalivr.c:

Go to the source code of this file.

Data Structures

struct  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, ast_channel_name(channel) , ## __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_IRPT   'I'
#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'
#define EXTERNALIVR_PORT   2949

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)
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_EXTENDED (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 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_app_option app_opts [128] = { [ 'n' ] = { .flag = noanswer }, [ 'i' ] = { .flag = ignore_hangup }, [ 'd' ] = { .flag = run_dead },}
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, ast_channel_name(channel) , ## __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 113 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_IRPT   'I'

Definition at line 108 of file app_externalivr.c.

Referenced by eivr_comm().

#define EIVR_CMD_LOG   'L'

Definition at line 109 of file app_externalivr.c.

Referenced by eivr_comm().

#define EIVR_CMD_OPT   'O'

Definition at line 110 of file app_externalivr.c.

Referenced by eivr_comm().

#define EIVR_CMD_PARM   'P'

Definition at line 111 of file app_externalivr.c.

Referenced by eivr_comm().

#define EIVR_CMD_SQUE   'S'

Definition at line 112 of file app_externalivr.c.

Referenced by eivr_comm().

#define EIVR_CMD_SVAR   'V'

Definition at line 114 of file app_externalivr.c.

Referenced by eivr_comm().

#define EIVR_CMD_XIT   'X'

Definition at line 115 of file app_externalivr.c.

Referenced by eivr_comm().

#define EXTERNALIVR_PORT   2949

Definition at line 117 of file app_externalivr.c.

Referenced by app_exec().


Enumeration Type Documentation

Enumerator:
noanswer 
ignore_hangup 
run_dead 

Definition at line 119 of file app_externalivr.c.

00119                    {
00120    noanswer = (1 << 0),
00121    ignore_hangup = (1 << 1),
00122    run_dead = (1 << 2),
00123 };


Function Documentation

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

Definition at line 390 of file app_externalivr.c.

References ivr_localuser::abort_current_sound, ast_tcptls_session_args::accept_fd, ao2_ref, app_opts, ast_activate_generator(), AST_AF_UNSPEC, ast_answer(), AST_APP_ARG, ast_app_parse_options(), ast_chan_log, ast_close_fds_above_n(), ast_deactivate_generator(), ast_debug, AST_DECLARE_APP_ARGS, ast_free, AST_LIST_HEAD_INIT_VALUE, AST_LIST_REMOVE_HEAD, ast_log, ast_opt_high_priority, ast_safe_fork(), ast_set_priority(), ast_sockaddr_copy(), ast_sockaddr_port, ast_sockaddr_resolve(), ast_sockaddr_set_port, 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, buf, ivr_localuser::chan, e, eivr_comm(), errno, EXTERNALIVR_PORT, ast_tcptls_session_instance::fd, ivr_localuser::gen_active, host, ignore_hangup, LOG_ERROR, noanswer, NULL, ivr_localuser::playlist, ast_tcptls_session_args::remote_address, and run_dead.

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

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

Definition at line 302 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(), NULL, pbx_builtin_getvar_helper(), strsep(), and value.

Referenced by eivr_comm().

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

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

Definition at line 354 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, NULL, and TIMELEN_MILLISECONDS.

Referenced by eivr_comm().

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

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

Definition at line 335 of file app_externalivr.c.

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

Referenced by eivr_comm().

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

AST_MODULE_INFO_STANDARD_EXTENDED ( 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 606 of file app_externalivr.c.

References ivr_localuser::abort_current_sound, ast_activate_generator(), ast_answer(), ast_chan_log, ast_channel_flags(), ast_channel_hangupcause_set(), ast_channel_language(), 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_IRPT, EIVR_CMD_LOG, EIVR_CMD_OPT, EIVR_CMD_PARM, EIVR_CMD_SQUE, EIVR_CMD_SVAR, EIVR_CMD_XIT, errno, f, playlist_entry::filename, ivr_localuser::finishlist, ast_frame::frametype, ivr_localuser::gen_active, ignore_hangup, input(), ast_frame_subclass::integer, LOG_ERROR, LOG_NOTICE, LOG_WARNING, make_entry(), NULL, ivr_localuser::option_autoclear, ivr_localuser::playing_silence, ivr_localuser::playlist, run_dead, send_eivr_event(), ast_frame::subclass, tmp(), and ast_frame::uint32.

Referenced by app_exec().

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

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

Definition at line 173 of file app_externalivr.c.

References ast_calloc, NULL, and gen_state::u.

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

static void gen_closestream ( struct gen_state state  )  [static]

Definition at line 186 of file app_externalivr.c.

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

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

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

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

Definition at line 271 of file app_externalivr.c.

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

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

static int gen_nextfile ( struct gen_state state  )  [static]

Definition at line 205 of file app_externalivr.c.

References ivr_localuser::abort_current_sound, ast_chan_log, ast_channel_language(), AST_LIST_FIRST, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, ast_openstream_full(), ivr_localuser::chan, gen_state::current, errno, playlist_entry::filename, gen_closestream(), LOG_WARNING, ivr_localuser::playing_silence, ivr_localuser::playlist, gen_state::stream, and gen_state::u.

Referenced by gen_readframe().

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

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

Definition at line 239 of file app_externalivr.c.

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

Referenced by gen_generate().

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

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

Definition at line 196 of file app_externalivr.c.

References ast_free, and gen_closestream().

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

static int load_module ( void   )  [static]

Definition at line 915 of file app_externalivr.c.

References app_exec, and ast_register_application_xml.

00916 {
00917    return ast_register_application_xml(app, app_exec);
00918 }

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

Definition at line 378 of file app_externalivr.c.

References ast_calloc, playlist_entry::filename, and NULL.

Referenced by eivr_comm().

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

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

Definition at line 158 of file app_externalivr.c.

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

Referenced by eivr_comm().

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

static int unload_module ( void   )  [static]

Definition at line 910 of file app_externalivr.c.

References ast_unregister_application().

00911 {
00912    return ast_unregister_application(app);
00913 }


Variable Documentation

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

Definition at line 97 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 129 of file app_externalivr.c.

struct ast_generator gen [static]

Definition at line 295 of file app_externalivr.c.

Referenced by reload_config(), and set_config().


Generated on Thu Apr 16 06:28:27 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6