Sat Feb 11 06:33:41 2012

Asterisk developer's documentation


app_externalivr.c File Reference

External IVR application interface. More...

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

Include dependency graph for app_externalivr.c:

Go to the source code of this file.

Data Structures

struct  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_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 void __reg_module (void)
static void __unreg_module (void)
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)
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_LOAD_ORDER , .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 = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEFAULT, }
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 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 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 void __reg_module ( void   )  [static]

Definition at line 890 of file app_externalivr.c.

static void __unreg_module ( void   )  [static]

Definition at line 890 of file app_externalivr.c.

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, ivr_localuser::abort_current_sound, ast_tcptls_session_args::accept_fd, ao2_ref, app_opts, ast_activate_generator(), ast_answer(), AST_APP_ARG, ast_app_parse_options(), ast_chan_log, ast_close_fds_above_n(), ast_copy_string(), ast_deactivate_generator(), ast_debug, AST_DECLARE_APP_ARGS, ast_free, ast_gethostbyname(), AST_LIST_HEAD_INIT_VALUE, AST_LIST_REMOVE_HEAD, ast_log(), ast_opt_high_priority, ast_safe_fork(), ast_set_priority(), ast_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, ivr_localuser::gen_active, hostname, ast_hostent::hp, ignore_hangup, LOG_ERROR, noanswer, ivr_localuser::playlist, ast_tcptls_session_args::remote_address, and run_dead.

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

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

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

References ast_channel::_state, ivr_localuser::abort_current_sound, ast_activate_generator(), ast_answer(), ast_chan_log, 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_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, ast_channel::hangupcause, ignore_hangup, input(), ast_frame_subclass::integer, LOG_ERROR, LOG_NOTICE, LOG_WARNING, make_entry(), ivr_localuser::option_autoclear, ivr_localuser::playing_silence, ivr_localuser::playlist, run_dead, send_eivr_event(), ast_frame::subclass, and ast_frame::uint32.

Referenced by app_exec().

00603 {
00604    struct playlist_entry *entry;
00605    struct ast_frame *f;
00606    int ms;
00607    int exception;
00608    int ready_fd;
00609    int waitfds[2] = { *eivr_commands_fd, (eivr_errors_fd) ? *eivr_errors_fd : -1 };
00610    struct ast_channel *rchan;
00611    int res = -1;
00612    int test_available_fd = -1;
00613    int hangup_info_sent = 0;
00614   
00615    FILE *eivr_commands = NULL;
00616    FILE *eivr_errors = NULL;
00617    FILE *eivr_events = NULL;
00618 
00619    if (!(eivr_events = fdopen(*eivr_events_fd, "w"))) {
00620       ast_chan_log(LOG_ERROR, chan, "Could not open stream to send events\n");
00621       goto exit;
00622    }
00623    if (!(eivr_commands = fdopen(*eivr_commands_fd, "r"))) {
00624       ast_chan_log(LOG_ERROR, chan, "Could not open stream to receive commands\n");
00625       goto exit;
00626    }
00627    if (eivr_errors_fd) {  /* if opening a socket connection, error stream will not be used */
00628       if (!(eivr_errors = fdopen(*eivr_errors_fd, "r"))) {
00629          ast_chan_log(LOG_ERROR, chan, "Could not open stream to receive errors\n");
00630          goto exit;
00631       }
00632    }
00633 
00634    test_available_fd = open("/dev/null", O_RDONLY);
00635  
00636    setvbuf(eivr_events, NULL, _IONBF, 0);
00637    setvbuf(eivr_commands, NULL, _IONBF, 0);
00638    if (eivr_errors) {
00639       setvbuf(eivr_errors, NULL, _IONBF, 0);
00640    }
00641 
00642    while (1) {
00643       if (ast_test_flag(chan, AST_FLAG_ZOMBIE)) {
00644          ast_chan_log(LOG_ERROR, chan, "Is a zombie\n");
00645          break;
00646       }
00647       if (!hangup_info_sent && !(ast_test_flag(&flags, run_dead)) && ast_check_hangup(chan)) {
00648          if (ast_test_flag(&flags, ignore_hangup)) {
00649             ast_verb(3, "Got check_hangup, but ignore_hangup set so sending 'I' command\n");
00650             send_eivr_event(eivr_events, 'I', "HANGUP", chan);
00651             hangup_info_sent = 1;
00652          } else {
00653             ast_verb(3, "Got check_hangup\n");
00654             send_eivr_event(eivr_events, 'H', NULL, chan);
00655             break;
00656          }
00657       }
00658  
00659       ready_fd = 0;
00660       ms = 100;
00661       errno = 0;
00662       exception = 0;
00663  
00664       rchan = ast_waitfor_nandfds(&chan, 1, waitfds, (eivr_errors_fd) ? 2 : 1, &exception, &ready_fd, &ms);
00665  
00666       if (chan->_state == AST_STATE_UP && !AST_LIST_EMPTY(&u->finishlist)) {
00667          AST_LIST_LOCK(&u->finishlist);
00668          while ((entry = AST_LIST_REMOVE_HEAD(&u->finishlist, list))) {
00669             send_eivr_event(eivr_events, 'F', entry->filename, chan);
00670             ast_free(entry);
00671          }
00672          AST_LIST_UNLOCK(&u->finishlist);
00673       }
00674  
00675       if (chan->_state == AST_STATE_UP && !(ast_check_hangup(chan)) && rchan) {
00676          /* the channel has something */
00677          f = ast_read(chan);
00678          if (!f) {
00679             ast_verb(3, "Returned no frame\n");
00680             send_eivr_event(eivr_events, 'H', NULL, chan);
00681             break;
00682          }
00683          if (f->frametype == AST_FRAME_DTMF) {
00684             send_eivr_event(eivr_events, f->subclass.integer, NULL, chan);
00685             if (u->option_autoclear) {
00686                if (!u->abort_current_sound && !u->playing_silence) {
00687                   /* send interrupted file as T data */
00688                   entry = AST_LIST_REMOVE_HEAD(&u->playlist, list);
00689                   send_eivr_event(eivr_events, 'T', entry->filename, chan);
00690                   ast_free(entry);
00691                }
00692                AST_LIST_LOCK(&u->playlist);
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, ast_channel_language(u->chan))) {
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                   entry = AST_LIST_REMOVE_HEAD(&u->playlist, list);
00772                   send_eivr_event(eivr_events, 'T', entry->filename, chan);
00773                   ast_free(entry);
00774                }
00775                while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
00776                   send_eivr_event(eivr_events, 'D', entry->filename, chan);
00777                   ast_free(entry);
00778                }
00779                if (!u->playing_silence) {
00780                   u->abort_current_sound = 1;
00781                }
00782                entry = make_entry(&input[2]);
00783                if (entry) {
00784                   AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
00785                }
00786                AST_LIST_UNLOCK(&u->playlist);
00787             }
00788          } else if (input[0] == EIVR_CMD_APND) {
00789             if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) {
00790                ast_chan_log(LOG_WARNING, chan, "Queue 'A'ppend called on unanswered channel\n");
00791                send_eivr_event(eivr_events, 'Z', NULL, chan);
00792                continue;
00793             }
00794             if (!ast_fileexists(&input[2], NULL, ast_channel_language(u->chan))) {
00795                ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
00796                send_eivr_event(eivr_events, 'Z', &input[2], chan);
00797             } else {
00798                entry = make_entry(&input[2]);
00799                if (entry) {
00800                   AST_LIST_LOCK(&u->playlist);
00801                   AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
00802                   AST_LIST_UNLOCK(&u->playlist);
00803                }
00804             }
00805          } else if (input[0] == EIVR_CMD_GET) {
00806             char response[2048];
00807             ast_verb(4, "Retriving Variables from channel: %s\n", &input[2]);
00808             ast_eivr_getvariable(chan, &input[2], response, sizeof(response));
00809             send_eivr_event(eivr_events, 'G', response, chan);
00810          } else if (input[0] == EIVR_CMD_SVAR) {
00811             ast_verb(4, "Setting Variables in channel: %s\n", &input[2]);
00812             ast_eivr_setvariable(chan, &input[2]);
00813          } else if (input[0] == EIVR_CMD_LOG) {
00814             ast_chan_log(LOG_NOTICE, chan, "Log message from EIVR: %s\n", &input[2]);
00815          } else if (input[0] == EIVR_CMD_XIT) {
00816             ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]);
00817             ast_chan_log(LOG_WARNING, chan, "e'X'it command is depricated, use 'E'xit instead\n");
00818             res = 0;
00819             break;
00820          } else if (input[0] == EIVR_CMD_EXIT) {
00821             ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]);
00822             send_eivr_event(eivr_events, 'E', NULL, chan);
00823             res = 0;
00824             break;
00825          } else if (input[0] == EIVR_CMD_HGUP) {
00826             ast_chan_log(LOG_NOTICE, chan, "Hanging up: %s\n", &input[2]);
00827             send_eivr_event(eivr_events, 'H', NULL, chan);
00828             break;
00829          } else if (input[0] == EIVR_CMD_OPT) {
00830             if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) {
00831                ast_chan_log(LOG_WARNING, chan, "Option called on unanswered channel\n");
00832                send_eivr_event(eivr_events, 'Z', NULL, chan);
00833                continue;
00834             }
00835             if (!strcasecmp(&input[2], "autoclear"))
00836                u->option_autoclear = 1;
00837             else if (!strcasecmp(&input[2], "noautoclear"))
00838                u->option_autoclear = 0;
00839             else
00840                ast_chan_log(LOG_WARNING, chan, "Unknown option requested: %s\n", &input[2]);
00841          }
00842       } else if (eivr_errors_fd && (ready_fd == *eivr_errors_fd)) {
00843          char input[1024];
00844   
00845          if (exception || feof(eivr_errors)) {
00846             ast_chan_log(LOG_ERROR, chan, "Child process went away\n");
00847             break;
00848          }
00849          if (fgets(input, sizeof(input), eivr_errors)) {
00850             ast_chan_log(LOG_NOTICE, chan, "stderr: %s\n", ast_strip(input));
00851          }
00852       } else if ((ready_fd < 0) && ms) { 
00853          if (errno == 0 || errno == EINTR)
00854             continue;
00855  
00856          ast_chan_log(LOG_ERROR, chan, "Wait failed (%s)\n", strerror(errno));
00857          break;
00858       }
00859    }
00860  
00861    exit:
00862    if (test_available_fd > -1) {
00863       close(test_available_fd);
00864    }
00865    if (eivr_events) {
00866       fclose(eivr_events);
00867       *eivr_events_fd = -1;
00868    }
00869    if (eivr_commands) {
00870       fclose(eivr_commands);
00871       *eivr_commands_fd = -1;
00872    }
00873    if (eivr_errors) {
00874       fclose(eivr_errors);
00875       *eivr_errors_fd = -1;
00876    }
00877    return res;
00878 }

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

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, ast_channel_language(u->chan), 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 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(), ivr_localuser::playing_silence, ivr_localuser::playlist, 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 885 of file app_externalivr.c.

References app_exec, and ast_register_application_xml.

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

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

Definition at line 377 of file app_externalivr.c.

References ast_calloc, and playlist_entry::filename.

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

References ast_unregister_application().

00881 {
00882    return ast_unregister_application(app);
00883 }


Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .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 = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEFAULT, } [static]

Definition at line 890 of file app_externalivr.c.

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

Definition at line 890 of file app_externalivr.c.

struct ast_generator gen [static]

Definition at line 294 of file app_externalivr.c.

Referenced by reload_config(), and set_config().


Generated on Sat Feb 11 06:33:42 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6