Thu Apr 28 2011 16:56:50

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  ivr_localuser::finishlist
struct  gen_state
struct  ivr_localuser
struct  ivr_localuser::playlist
struct  playlist_entry

Defines

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

Enumerations

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

Functions

static void __reg_module (void)
static void __unreg_module (void)
static int app_exec (struct ast_channel *chan, void *data)
static void ast_eivr_getvariable (struct ast_channel *chan, char *data, char *outbuf, int outbuflen)
static void ast_eivr_setvariable (struct ast_channel *chan, char *data)
static int eivr_comm (struct ast_channel *chan, struct ivr_localuser *u, int *eivr_events_fd, int *eivr_commands_fd, int *eivr_errors_fd, const struct ast_str *args, const struct ast_flags flags)
int eivr_connect_socket (struct ast_channel *chan, const char *host, int port)
static void * gen_alloc (struct ast_channel *chan, void *params)
static void gen_closestream (struct gen_state *state)
static int gen_generate (struct ast_channel *chan, void *data, int len, int samples)
static int gen_nextfile (struct gen_state *state)
static struct ast_framegen_readframe (struct gen_state *state)
static void gen_release (struct ast_channel *chan, void *data)
static int load_module (void)
static struct playlist_entrymake_entry (const char *filename)
static void send_eivr_event (FILE *handle, const char event, const char *data, const struct ast_channel *chan)
static int unload_module (void)

Variables

static struct ast_module_info
__MODULE_INFO_SECTION 
__mod_info = { __MODULE_INFO_GLOBALS .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "External IVR Interface Application" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, }
static const char * app = "ExternalIVR"
static struct ast_app_option app_opts [128] = { [ 'n' ] = { .flag = noanswer }, [ 'i' ] = { .flag = ignore_hangup }, [ 'd' ] = { .flag = run_dead },}
static struct ast_module_infoast_module_info = &__mod_info
static const char * descrip = " an 'E' command.\n"
static struct ast_generator gen
enum { ... }  options_flags
static const char * synopsis = "Interfaces with an external IVR application"

Detailed Description

External IVR application interface.

Author:
Kevin P. Fleming <kpfleming@digium.com>
Note:
Portions taken from the file-based music-on-hold work created by Anthony Minessale II in res_musiconhold.c

Definition in file app_externalivr.c.


Define Documentation

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

Definition at line 72 of file app_externalivr.c.

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


Enumeration Type Documentation

anonymous enum
Enumerator:
noanswer 
ignore_hangup 
run_dead 

Definition at line 74 of file app_externalivr.c.

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

Function Documentation

static void __reg_module ( void  ) [static]

Definition at line 808 of file app_externalivr.c.

static void __unreg_module ( void  ) [static]

Definition at line 808 of file app_externalivr.c.

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

Definition at line 314 of file app_externalivr.c.

References ast_channel::_state, ivr_localuser::abort_current_sound, ast_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_STANDARD_APP_ARGS, AST_STATE_UP, ast_str_append(), ast_str_create(), ast_str_reset(), ast_strdupa, ast_strlen_zero(), ast_tcptls_client_create(), ast_tcptls_client_start(), ast_test_flag, buf, chan, ivr_localuser::chan, eivr_comm(), errno, ast_tcptls_session_instance::fd, ivr_localuser::gen_active, hostname, ast_hostent::hp, ignore_hangup, ast_tcptls_session_args::local_address, LOG_ERROR, LOG_WARNING, noanswer, option_debug, ivr_localuser::playlist, run_dead, and s.

Referenced by load_module().

{
   struct ast_flags flags = { 0, };
   char *opts[0];
   struct playlist_entry *entry;
   int child_stdin[2] = { -1, -1 };
   int child_stdout[2] = { -1, -1 };
   int child_stderr[2] = { -1, -1 };
   int res = -1;
   int pid;

   char hostname[1024];
   char *port_str = NULL;
   int port = 0;
   struct ast_tcptls_session_instance *ser = NULL;

   struct ivr_localuser foo = {
      .playlist = AST_LIST_HEAD_INIT_VALUE,
      .finishlist = AST_LIST_HEAD_INIT_VALUE,
      .gen_active = 0,
   };
   struct ivr_localuser *u = &foo;

   char *buf;
   int j;
   char *s, **app_args, *e; 
   struct ast_str *pipe_delim_args = ast_str_create(100);

   AST_DECLARE_APP_ARGS(eivr_args,
      AST_APP_ARG(cmd)[32];
   );
   AST_DECLARE_APP_ARGS(application_args,
      AST_APP_ARG(cmd)[32];
   );

   u->abort_current_sound = 0;
   u->chan = chan;

   if (ast_strlen_zero(data)) {
      ast_log(LOG_WARNING, "ExternalIVR requires a command to execute\n");
      return -1;
   }

   buf = ast_strdupa(data);
   AST_STANDARD_APP_ARGS(eivr_args, buf);

   if ((s = strchr(eivr_args.cmd[0], '('))) {
      s[0] = ',';
      if (( e = strrchr(s, ')')) ) {
         *e = '\0';
      } else {
         ast_log(LOG_ERROR, "Parse error, no closing paren?\n");
      }
      AST_STANDARD_APP_ARGS(application_args, eivr_args.cmd[0]);
      app_args = application_args.argv;

      /* Put the application + the arguments in a | delimited list */
      ast_str_reset(pipe_delim_args);
      for (j = 0; application_args.cmd[j] != NULL; j++) {
         ast_str_append(&pipe_delim_args, 0, "%s%s", j == 0 ? "" : ",", application_args.cmd[j]);
      }

      /* Parse the ExternalIVR() arguments */
      if (option_debug)
         ast_debug(1, "Parsing options from: [%s]\n", eivr_args.cmd[1]);
      ast_app_parse_options(app_opts, &flags, opts, eivr_args.cmd[1]);
      if (option_debug) {
         if (ast_test_flag(&flags, noanswer))
            ast_debug(1, "noanswer is set\n");
         if (ast_test_flag(&flags, ignore_hangup))
            ast_debug(1, "ignore_hangup is set\n");
         if (ast_test_flag(&flags, run_dead))
            ast_debug(1, "run_dead is set\n");
      }

   } else {
      app_args = eivr_args.argv;
      for (j = 0; eivr_args.cmd[j] != NULL; j++) {
         ast_str_append(&pipe_delim_args, 0, "%s%s", j == 0 ? "" : "|", eivr_args.cmd[j]);
      }
   }
   
   if (ast_strlen_zero(data)) {
      ast_log(LOG_WARNING, "ExternalIVR requires a command to execute\n");
      return -1;
   }

   if (!(ast_test_flag(&flags, noanswer))) {
      ast_chan_log(LOG_WARNING, chan, "Answering channel and starting generator\n");
      if (chan->_state != AST_STATE_UP) {
         if (ast_test_flag(&flags, run_dead)) {
            ast_chan_log(LOG_WARNING, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n");
            goto exit;
         }
         ast_answer(chan);
      }
      if (ast_activate_generator(chan, &gen, u) < 0) {
         ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n");
         goto exit;
      } else {
         u->gen_active = 1;
      }
   }

   if (!strncmp(app_args[0], "ivr://", 6)) {
      struct ast_tcptls_session_args ivr_desc = {
         .accept_fd = -1,
         .name = "IVR",
      };
      struct ast_hostent hp;

      /*communicate through socket to server*/
      ast_debug(1, "Parsing hostname:port for socket connect from \"%s\"\n", app_args[0]);
      ast_copy_string(hostname, app_args[0] + 6, sizeof(hostname));
      if ((port_str = strchr(hostname, ':')) != NULL) {
         port_str[0] = 0;
         port_str += 1;
         port = atoi(port_str);
      }
      if (!port) {
         port = 2949;  /* default port, if one is not provided */
      }

      ast_gethostbyname(hostname, &hp);
      ivr_desc.local_address.sin_family = AF_INET;
      ivr_desc.local_address.sin_port = htons(port);
      memcpy(&ivr_desc.local_address.sin_addr.s_addr, hp.hp.h_addr, hp.hp.h_length);
      if (!(ser = ast_tcptls_client_create(&ivr_desc)) || !(ser = ast_tcptls_client_start(ser))) {
         goto exit;
      }
      res = eivr_comm(chan, u, &ser->fd, &ser->fd, NULL, pipe_delim_args, flags);

   } else {
      if (pipe(child_stdin)) {
         ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child input: %s\n", strerror(errno));
         goto exit;
      }
      if (pipe(child_stdout)) {
         ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child output: %s\n", strerror(errno));
         goto exit;
      }
      if (pipe(child_stderr)) {
         ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child errors: %s\n", strerror(errno));
         goto exit;
      }
   
      pid = ast_safe_fork(0);
      if (pid < 0) {
         ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno));
         goto exit;
      }
   
      if (!pid) {
         /* child process */
         if (ast_opt_high_priority)
            ast_set_priority(0);
   
         dup2(child_stdin[0], STDIN_FILENO);
         dup2(child_stdout[1], STDOUT_FILENO);
         dup2(child_stderr[1], STDERR_FILENO);
         ast_close_fds_above_n(STDERR_FILENO);
         execv(app_args[0], app_args);
         fprintf(stderr, "Failed to execute '%s': %s\n", app_args[0], strerror(errno));
         _exit(1);
      } else {
         /* parent process */
         close(child_stdin[0]);
         child_stdin[0] = -1;
         close(child_stdout[1]);
         child_stdout[1] = -1;
         close(child_stderr[1]);
         child_stderr[1] = -1;
         res = eivr_comm(chan, u, &child_stdin[1], &child_stdout[0], &child_stderr[0], pipe_delim_args, flags);
      }
   }

   exit:
   if (u->gen_active) {
      ast_deactivate_generator(chan);
   }
   if (child_stdin[0] > -1) {
      close(child_stdin[0]);
   }
   if (child_stdin[1] > -1) {
      close(child_stdin[1]);
   }
   if (child_stdout[0] > -1) {
      close(child_stdout[0]);
   }
   if (child_stdout[1] > -1) {
      close(child_stdout[1]);
   }
   if (child_stderr[0] > -1) {
      close(child_stderr[0]);
   }
   if (child_stderr[1] > -1) {
      close(child_stderr[1]);
   }
   if (ser) {
      ao2_ref(ser, -1);
   }
   while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
      ast_free(entry);
   }
   return res;
}
static void ast_eivr_getvariable ( struct ast_channel chan,
char *  data,
char *  outbuf,
int  outbuflen 
) [static]

Definition at line 250 of file app_externalivr.c.

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

Referenced by eivr_comm().

{
   /* original input data: "G,var1,var2," */
   /* data passed as "data":  "var1,var2" */

   char *inbuf, *variable;
   const char *value;
   int j;
   struct ast_str *newstring = ast_str_alloca(outbuflen); 

   outbuf[0] = '\0';

   for (j = 1, inbuf = data; ; j++) {
      variable = strsep(&inbuf, ",");
      if (variable == NULL) {
         int outstrlen = strlen(outbuf);
         if (outstrlen && outbuf[outstrlen - 1] == ',') {
            outbuf[outstrlen - 1] = 0;
         }
         break;
      }
      
      ast_channel_lock(chan);
      if (!(value = pbx_builtin_getvar_helper(chan, variable))) {
         value = "";
      }

      ast_str_append(&newstring, 0, "%s=%s,", variable, value);
      ast_channel_unlock(chan);
      ast_copy_string(outbuf, ast_str_buffer(newstring), outbuflen);
   }
}
static void ast_eivr_setvariable ( struct ast_channel chan,
char *  data 
) [static]

Definition at line 283 of file app_externalivr.c.

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

Referenced by eivr_comm().

{
   char *value;

   char *inbuf = ast_strdupa(data), *variable;

   for (variable = strsep(&inbuf, ","); variable; variable = strsep(&inbuf, ",")) {
      ast_debug(1, "Setting up a variable: %s\n", variable);
      /* variable contains "varname=value" */
      value = strchr(variable, '=');
      if (!value) {
         value = "";
      } else {
         *value++ = '\0';
      }
      pbx_builtin_setvar_helper(chan, variable, value);
   }
}
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]

Todo:
add deprecation debug message for X command here

Definition at line 521 of file app_externalivr.c.

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

Referenced by app_exec().

{
   struct playlist_entry *entry;
   struct ast_frame *f;
   int ms;
   int exception;
   int ready_fd;
   int waitfds[2] = { *eivr_commands_fd, (eivr_errors_fd) ? *eivr_errors_fd : -1 };
   struct ast_channel *rchan;
   char *command;
   int res = -1;
   int test_available_fd = -1;
   int hangup_info_sent = 0;
  
   FILE *eivr_commands = NULL;
   FILE *eivr_errors = NULL;
   FILE *eivr_events = NULL;

   if (!(eivr_events = fdopen(*eivr_events_fd, "w"))) {
      ast_chan_log(LOG_WARNING, chan, "Could not open stream to send events\n");
      goto exit;
   }
   if (!(eivr_commands = fdopen(*eivr_commands_fd, "r"))) {
      ast_chan_log(LOG_WARNING, chan, "Could not open stream to receive commands\n");
      goto exit;
   }
   if (eivr_errors_fd) {  /* if opening a socket connection, error stream will not be used */
      if (!(eivr_errors = fdopen(*eivr_errors_fd, "r"))) {
         ast_chan_log(LOG_WARNING, chan, "Could not open stream to receive errors\n");
         goto exit;
      }
   }

   test_available_fd = open("/dev/null", O_RDONLY);
 
   setvbuf(eivr_events, NULL, _IONBF, 0);
   setvbuf(eivr_commands, NULL, _IONBF, 0);
   if (eivr_errors) {
      setvbuf(eivr_errors, NULL, _IONBF, 0);
   }

   res = 0;
 
   while (1) {
      if (ast_test_flag(chan, AST_FLAG_ZOMBIE)) {
         ast_chan_log(LOG_NOTICE, chan, "Is a zombie\n");
         res = -1;
         break;
      }
      if (!hangup_info_sent && !(ast_test_flag(&flags, run_dead)) && ast_check_hangup(chan)) {
         if (ast_test_flag(&flags, ignore_hangup)) {
            ast_chan_log(LOG_NOTICE, chan, "Got check_hangup, but ignore_hangup set so sending 'I' command\n");
            send_eivr_event(eivr_events, 'I', "HANGUP", chan);
            hangup_info_sent = 1;
         } else {
            ast_chan_log(LOG_NOTICE, chan, "Got check_hangup\n");
            send_eivr_event(eivr_events, 'H', NULL, chan);
            res = -1;
            break;
         }
      }
 
      ready_fd = 0;
      ms = 100;
      errno = 0;
      exception = 0;
 
      rchan = ast_waitfor_nandfds(&chan, 1, waitfds, (eivr_errors_fd) ? 2 : 1, &exception, &ready_fd, &ms);
 
      if (chan->_state == AST_STATE_UP && !AST_LIST_EMPTY(&u->finishlist)) {
         AST_LIST_LOCK(&u->finishlist);
         while ((entry = AST_LIST_REMOVE_HEAD(&u->finishlist, list))) {
            send_eivr_event(eivr_events, 'F', entry->filename, chan);
            ast_free(entry);
         }
         AST_LIST_UNLOCK(&u->finishlist);
      }
 
      if (chan->_state == AST_STATE_UP && !(ast_check_hangup(chan)) && rchan) {
         /* the channel has something */
         f = ast_read(chan);
         if (!f) {
            ast_chan_log(LOG_NOTICE, chan, "Returned no frame\n");
            send_eivr_event(eivr_events, 'H', NULL, chan);
            res = -1;
            break;
         }
         if (f->frametype == AST_FRAME_DTMF) {
            send_eivr_event(eivr_events, f->subclass, NULL, chan);
            if (u->option_autoclear) {
               if (!u->abort_current_sound && !u->playing_silence)
                  send_eivr_event(eivr_events, 'T', NULL, chan);
               AST_LIST_LOCK(&u->playlist);
               while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
                  send_eivr_event(eivr_events, 'D', entry->filename, chan);
                  ast_free(entry);
               }
               if (!u->playing_silence)
                  u->abort_current_sound = 1;
               AST_LIST_UNLOCK(&u->playlist);
            }
         } else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) {
            ast_chan_log(LOG_NOTICE, chan, "Got AST_CONTROL_HANGUP\n");
            send_eivr_event(eivr_events, 'H', NULL, chan);
            if (f->data.uint32) {
               chan->hangupcause = f->data.uint32;
            }
            ast_frfree(f);
            res = -1;
            break;
         }
         ast_frfree(f);
      } else if (ready_fd == *eivr_commands_fd) {
         char input[1024];
 
         if (exception || (dup2(*eivr_commands_fd, test_available_fd) == -1) || feof(eivr_commands)) {
            ast_chan_log(LOG_WARNING, chan, "Child process went away\n");
            res = -1;
            break;
         }
  
         if (!fgets(input, sizeof(input), eivr_commands))
            continue;
 
         command = ast_strip(input);
  
         if (option_debug)
            ast_debug(1, "got command '%s'\n", input);
  
         if (strlen(input) < 4)
            continue;
  
         if (input[0] == 'P') {
            struct ast_str *tmp = (struct ast_str *) args;
            send_eivr_event(eivr_events, 'P', ast_str_buffer(tmp), chan);
         } else if ( input[0] == 'T' ) {
            ast_chan_log(LOG_WARNING, chan, "Answering channel if needed and starting generator\n");
            if (chan->_state != AST_STATE_UP) {
               if (ast_test_flag(&flags, run_dead)) {
                  ast_chan_log(LOG_WARNING, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n");
                  send_eivr_event(eivr_events, 'Z', "ANSWER_FAILURE", chan);
                  continue;
               }
               ast_answer(chan);
            }
            if (!(u->gen_active)) {
               if (ast_activate_generator(chan, &gen, u) < 0) {
                  ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n");
                  send_eivr_event(eivr_events, 'Z', "GENERATOR_FAILURE", chan);
               } else {
                  u->gen_active = 1;
               }
            }
         } else if (input[0] == 'S') {
            if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) {
               ast_chan_log(LOG_WARNING, chan, "Queue 'S'et called on unanswered channel\n");
               send_eivr_event(eivr_events, 'Z', NULL, chan);
               continue;
            }
            if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) {
               ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
               send_eivr_event(eivr_events, 'Z', NULL, chan);
               strcpy(&input[2], "exception");
            }
            if (!u->abort_current_sound && !u->playing_silence)
               send_eivr_event(eivr_events, 'T', NULL, chan);
            AST_LIST_LOCK(&u->playlist);
            while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
               send_eivr_event(eivr_events, 'D', entry->filename, chan);
               ast_free(entry);
            }
            if (!u->playing_silence)
               u->abort_current_sound = 1;
            entry = make_entry(&input[2]);
            if (entry)
               AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
            AST_LIST_UNLOCK(&u->playlist);
         } else if (input[0] == 'A') {
            if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) {
               ast_chan_log(LOG_WARNING, chan, "Queue 'A'ppend called on unanswered channel\n");
               send_eivr_event(eivr_events, 'Z', NULL, chan);
               continue;
            }
            if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) {
               ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
               send_eivr_event(eivr_events, 'Z', NULL, chan);
               strcpy(&input[2], "exception");
            }
            entry = make_entry(&input[2]);
            if (entry) {
               AST_LIST_LOCK(&u->playlist);
               AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
               AST_LIST_UNLOCK(&u->playlist);
            }
         } else if (input[0] == 'G') {
            /* A get variable message:  "G,variable1,variable2,..." */
            char response[2048];

            ast_chan_log(LOG_NOTICE, chan, "Getting a Variable out of the channel: %s\n", &input[2]);
            ast_eivr_getvariable(chan, &input[2], response, sizeof(response));
            send_eivr_event(eivr_events, 'G', response, chan);
         } else if (input[0] == 'V') {
            /* A set variable message:  "V,variablename=foo" */
            ast_chan_log(LOG_NOTICE, chan, "Setting a Variable up: %s\n", &input[2]);
            ast_eivr_setvariable(chan, &input[2]);
         } else if (input[0] == 'L') {
            ast_chan_log(LOG_NOTICE, chan, "Log message from EIVR: %s\n", &input[2]);
         } else if (input[0] == 'X') {
            ast_chan_log(LOG_NOTICE, chan, "Exiting ExternalIVR: %s\n", &input[2]);
            /*! \todo add deprecation debug message for X command here */
            res = 0;
            break;
         } else if (input[0] == 'E') {
            ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]);
            send_eivr_event(eivr_events, 'E', NULL, chan);
            res = 0;
            break;
         } else if (input[0] == 'H') {
            ast_chan_log(LOG_NOTICE, chan, "Hanging up: %s\n", &input[2]);
            send_eivr_event(eivr_events, 'H', NULL, chan);
            res = -1;
            break;
         } else if (input[0] == 'O') {
            if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) {
               ast_chan_log(LOG_WARNING, chan, "Option called on unanswered channel\n");
               send_eivr_event(eivr_events, 'Z', NULL, chan);
               continue;
            }
            if (!strcasecmp(&input[2], "autoclear"))
               u->option_autoclear = 1;
            else if (!strcasecmp(&input[2], "noautoclear"))
               u->option_autoclear = 0;
            else
               ast_chan_log(LOG_WARNING, chan, "Unknown option requested '%s'\n", &input[2]);
         }
      } else if (eivr_errors_fd && (ready_fd == *eivr_errors_fd)) {
         char input[1024];
  
         if (exception || feof(eivr_errors)) {
            ast_chan_log(LOG_WARNING, chan, "Child process went away\n");
            res = -1;
            break;
         }
         if (fgets(input, sizeof(input), eivr_errors)) {
            command = ast_strip(input);
            ast_chan_log(LOG_NOTICE, chan, "stderr: %s\n", command);
         }
      } else if ((ready_fd < 0) && ms) { 
         if (errno == 0 || errno == EINTR)
            continue;
 
         ast_chan_log(LOG_WARNING, chan, "Wait failed (%s)\n", strerror(errno));
         break;
      }
   }
 
   exit:
   if (test_available_fd > -1) {
      close(test_available_fd);
   }
   if (eivr_events) {
      fclose(eivr_events);
      *eivr_events_fd = -1;
   }
   if (eivr_commands) {
      fclose(eivr_commands);
      *eivr_commands_fd = -1;
   }
   if (eivr_errors) {
      fclose(eivr_errors);
      *eivr_errors_fd = -1;
   }
   return res;
}
int eivr_connect_socket ( struct ast_channel chan,
const char *  host,
int  port 
)
static void* gen_alloc ( struct ast_channel chan,
void *  params 
) [static]

Definition at line 129 of file app_externalivr.c.

References ast_calloc, and gen_state::u.

{
   struct ivr_localuser *u = params;
   struct gen_state *state;

   if (!(state = ast_calloc(1, sizeof(*state))))
      return NULL;

   state->u = u;

   return state;
}
static void gen_closestream ( struct gen_state state) [static]

Definition at line 142 of file app_externalivr.c.

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

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

{
   if (!state->stream)
      return;

   ast_closestream(state->stream);
   state->u->chan->stream = NULL;
   state->stream = NULL;
}
static int gen_generate ( struct ast_channel chan,
void *  data,
int  len,
int  samples 
) [static]

Definition at line 219 of file app_externalivr.c.

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

{
   struct gen_state *state = data;
   struct ast_frame *f = NULL;
   int res = 0;

   state->sample_queue += samples;

   while (state->sample_queue > 0) {
      if (!(f = gen_readframe(state)))
         return -1;

      res = ast_write(chan, f);
      ast_frfree(f);
      if (res < 0) {
         ast_chan_log(LOG_WARNING, chan, "Failed to write frame: %s\n", strerror(errno));
         return -1;
      }
      state->sample_queue -= f->samples;
   }

   return res;
}
static int gen_nextfile ( struct gen_state state) [static]

Definition at line 161 of file app_externalivr.c.

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

Referenced by gen_readframe().

{
   struct ivr_localuser *u = state->u;
   char *file_to_stream;

   u->abort_current_sound = 0;
   u->playing_silence = 0;
   gen_closestream(state);

   while (!state->stream) {
      state->current = AST_LIST_REMOVE_HEAD(&u->playlist, list);
      if (state->current) {
         file_to_stream = state->current->filename;
      } else {
         file_to_stream = "silence/10";
         u->playing_silence = 1;
      }

      if (!(state->stream = ast_openstream_full(u->chan, file_to_stream, u->chan->language, 1))) {
         ast_chan_log(LOG_WARNING, u->chan, "File '%s' could not be opened: %s\n", file_to_stream, strerror(errno));
         if (!u->playing_silence) {
            continue;
         } else {
            break;
         }
      }
   }

   return (!state->stream);
}
static void gen_release ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 152 of file app_externalivr.c.

References ast_free, and gen_closestream().

{
   struct gen_state *state = data;

   gen_closestream(state);
   ast_free(data);
}
static int load_module ( void  ) [static]

Definition at line 803 of file app_externalivr.c.

References app_exec(), and ast_register_application.

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

Definition at line 302 of file app_externalivr.c.

References ast_calloc, and playlist_entry::filename.

Referenced by eivr_comm().

{
   struct playlist_entry *entry;

   if (!(entry = ast_calloc(1, sizeof(*entry) + strlen(filename) + 10))) /* XXX why 10 ? */
      return NULL;

   strcpy(entry->filename, filename);

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

Definition at line 115 of file app_externalivr.c.

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

Referenced by eivr_comm().

{
   struct ast_str *tmp = ast_str_create(12);

   ast_str_append(&tmp, 0, "%c,%10d", event, (int)time(NULL));
   if (data) {
      ast_str_append(&tmp, 0, ",%s", data);
   }

   fprintf(handle, "%s\n", ast_str_buffer(tmp));
   ast_debug(1, "sent '%s'\n", ast_str_buffer(tmp));
}
static int unload_module ( void  ) [static]

Definition at line 798 of file app_externalivr.c.

References ast_unregister_application().


Variable Documentation

struct ast_module_info __MODULE_INFO_SECTION __mod_info = { __MODULE_INFO_GLOBALS .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "External IVR Interface Application" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, } [static]

Definition at line 808 of file app_externalivr.c.

const char* app = "ExternalIVR" [static]

Definition at line 51 of file app_externalivr.c.

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

Definition at line 84 of file app_externalivr.c.

Referenced by app_exec().

Definition at line 808 of file app_externalivr.c.

const char* descrip = " an 'E' command.\n" [static]

Definition at line 54 of file app_externalivr.c.

Referenced by aji_handle_presence().

struct ast_generator gen [static]

Definition at line 243 of file app_externalivr.c.

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

enum { ... } options_flags
const char* synopsis = "Interfaces with an external IVR application" [static]