Sat Apr 26 2014 22:01:26

Asterisk developer's documentation


app_externalivr.c
Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2005, Digium, Inc.
00005  *
00006  * Kevin P. Fleming <kpfleming@digium.com>
00007  *
00008  * Portions taken from the file-based music-on-hold work
00009  * created by Anthony Minessale II in res_musiconhold.c
00010  *
00011  * See http://www.asterisk.org for more information about
00012  * the Asterisk project. Please do not directly contact
00013  * any of the maintainers of this project for assistance;
00014  * the project provides a web site, mailing lists and IRC
00015  * channels for your use.
00016  *
00017  * This program is free software, distributed under the terms of
00018  * the GNU General Public License Version 2. See the LICENSE file
00019  * at the top of the source tree.
00020  */
00021 
00022 /*! \file
00023  *
00024  * \brief External IVR application interface
00025  *
00026  * \author Kevin P. Fleming <kpfleming@digium.com>
00027  *
00028  * \note Portions taken from the file-based music-on-hold work
00029  * created by Anthony Minessale II in res_musiconhold.c
00030  *
00031  * \ingroup applications
00032  */
00033 
00034 /*** MODULEINFO
00035    <support_level>extended</support_level>
00036  ***/
00037 
00038 #include "asterisk.h"
00039 
00040 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 369142 $")
00041 
00042 #include <signal.h>
00043 
00044 #include "asterisk/lock.h"
00045 #include "asterisk/file.h"
00046 #include "asterisk/channel.h"
00047 #include "asterisk/pbx.h"
00048 #include "asterisk/module.h"
00049 #include "asterisk/linkedlists.h"
00050 #include "asterisk/app.h"
00051 #include "asterisk/utils.h"
00052 #include "asterisk/tcptls.h"
00053 #include "asterisk/astobj2.h"
00054 
00055 /*** DOCUMENTATION
00056    <application name="ExternalIVR" language="en_US">
00057       <synopsis>
00058          Interfaces with an external IVR application.
00059       </synopsis>
00060       <syntax>
00061          <parameter name="command|ivr://host" required="true" hasparams="true">
00062             <argument name="arg1" />
00063             <argument name="arg2" multiple="yes" />
00064          </parameter>
00065          <parameter name="options">
00066             <optionlist>
00067                <option name="n">
00068                   <para>Tells ExternalIVR() not to answer the channel.</para>
00069                </option>
00070                <option name="i">
00071                   <para>Tells ExternalIVR() not to send a hangup and exit when the
00072                   channel receives a hangup, instead it sends an <literal>I</literal>
00073                   informative message meaning that the external application MUST hang
00074                   up the call with an <literal>H</literal> command.</para>
00075                </option>
00076                <option name="d">
00077                   <para>Tells ExternalIVR() to run on a channel that has been hung up
00078                   and will not look for hangups. The external application must exit with
00079                   an <literal>E</literal> command.</para>
00080                </option>
00081             </optionlist>
00082          </parameter>
00083       </syntax>
00084       <description>
00085          <para>Either forks a process to run given command or makes a socket to connect
00086          to given host and starts a generator on the channel. The generator's play list
00087          is controlled by the external application, which can add and clear entries via
00088          simple commands issued over its stdout. The external application will receive
00089          all DTMF events received on the channel, and notification if the channel is
00090          hung up. The received on the channel, and notification if the channel is hung
00091          up. The application will not be forcibly terminated when the channel is hung up.
00092          For more information see <filename>doc/AST.pdf</filename>.</para>
00093       </description>
00094    </application>
00095  ***/
00096 
00097 static const char app[] = "ExternalIVR";
00098 
00099 /* XXX the parser in gcc 2.95 gets confused if you don't put a space between 'name' and the comma */
00100 #define ast_chan_log(level, channel, format, ...) ast_log(level, "%s: " format, ast_channel_name(channel) , ## __VA_ARGS__)
00101 
00102 /* Commands */
00103 #define EIVR_CMD_APND 'A' /* append to prompt queue */
00104 #define EIVR_CMD_DTMF 'D' /* send DTMF */
00105 #define EIVR_CMD_EXIT 'E' /* exit */
00106 #define EIVR_CMD_GET  'G' /* get channel varable(s) */
00107 #define EIVR_CMD_HGUP 'H' /* hangup */
00108 #define EIVR_CMD_IRPT 'I' /* interrupt */
00109 #define EIVR_CMD_LOG  'L' /* log message */
00110 #define EIVR_CMD_OPT  'O' /* option */
00111 #define EIVR_CMD_PARM 'P' /* return supplied params */
00112 #define EIVR_CMD_SQUE 'S' /* (re)set prompt queue */
00113 #define EIVR_CMD_ANS  'T' /* answer channel */
00114 #define EIVR_CMD_SVAR 'V' /* set channel varable(s) */
00115 #define EIVR_CMD_XIT  'X' /* exit **depricated** */
00116 
00117 #define EXTERNALIVR_PORT 2949
00118 
00119 enum options_flags {
00120    noanswer = (1 << 0),
00121    ignore_hangup = (1 << 1),
00122    run_dead = (1 << 2),
00123 };
00124 
00125 AST_APP_OPTIONS(app_opts, {
00126    AST_APP_OPTION('n', noanswer),
00127    AST_APP_OPTION('i', ignore_hangup),
00128    AST_APP_OPTION('d', run_dead),
00129 });
00130 
00131 struct playlist_entry {
00132    AST_LIST_ENTRY(playlist_entry) list;
00133    char filename[1];
00134 };
00135 
00136 struct ivr_localuser {
00137    struct ast_channel *chan;
00138    AST_LIST_HEAD(playlist, playlist_entry) playlist;
00139    AST_LIST_HEAD(finishlist, playlist_entry) finishlist;
00140    int abort_current_sound;
00141    int playing_silence;
00142    int option_autoclear;
00143    int gen_active;
00144 };
00145 
00146 
00147 struct gen_state {
00148    struct ivr_localuser *u;
00149    struct ast_filestream *stream;
00150    struct playlist_entry *current;
00151    int sample_queue;
00152 };
00153 
00154 static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u, 
00155    int *eivr_events_fd, int *eivr_commands_fd, int *eivr_errors_fd, 
00156    const struct ast_str *args, const struct ast_flags flags);
00157 
00158 static void send_eivr_event(FILE *handle, const char event, const char *data,
00159    const struct ast_channel *chan)
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 }
00172 
00173 static void *gen_alloc(struct ast_channel *chan, void *params)
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 }
00185 
00186 static void gen_closestream(struct gen_state *state)
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 }
00195 
00196 static void gen_release(struct ast_channel *chan, void *data)
00197 {
00198    struct gen_state *state = data;
00199 
00200    gen_closestream(state);
00201    ast_free(data);
00202 }
00203 
00204 /* caller has the playlist locked */
00205 static int gen_nextfile(struct gen_state *state)
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 }
00238 
00239 static struct ast_frame *gen_readframe(struct gen_state *state)
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 }
00270 
00271 static int gen_generate(struct ast_channel *chan, void *data, int len, int 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 }
00294 
00295 static struct ast_generator gen =
00296 {
00297    .alloc = gen_alloc,
00298    .release = gen_release,
00299    .generate = gen_generate,
00300 };
00301 
00302 static void ast_eivr_getvariable(struct ast_channel *chan, char *data, char *outbuf, int outbuflen)
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 }
00334 
00335 static void ast_eivr_setvariable(struct ast_channel *chan, char *data)
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 }
00353 
00354 static void ast_eivr_senddtmf(struct ast_channel *chan, char *vdata)
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 }
00377 
00378 static struct playlist_entry *make_entry(const char *filename)
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 }
00389 
00390 static int app_exec(struct ast_channel *chan, const char *data)
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       if (i == num_addrs) {
00523          ast_chan_log(LOG_ERROR, chan, "Could not connect to any host.  ExternalIVR failed.\n");
00524          goto exit;
00525       }
00526 
00527       res = eivr_comm(chan, u, &ser->fd, &ser->fd, NULL, comma_delim_args, flags);
00528 
00529    } else {
00530       if (pipe(child_stdin)) {
00531          ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child input: %s\n", strerror(errno));
00532          goto exit;
00533       }
00534       if (pipe(child_stdout)) {
00535          ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child output: %s\n", strerror(errno));
00536          goto exit;
00537       }
00538       if (pipe(child_stderr)) {
00539          ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child errors: %s\n", strerror(errno));
00540          goto exit;
00541       }
00542    
00543       pid = ast_safe_fork(0);
00544       if (pid < 0) {
00545          ast_log(LOG_ERROR, "Failed to fork(): %s\n", strerror(errno));
00546          goto exit;
00547       }
00548    
00549       if (!pid) {
00550          /* child process */
00551          if (ast_opt_high_priority)
00552             ast_set_priority(0);
00553    
00554          dup2(child_stdin[0], STDIN_FILENO);
00555          dup2(child_stdout[1], STDOUT_FILENO);
00556          dup2(child_stderr[1], STDERR_FILENO);
00557          ast_close_fds_above_n(STDERR_FILENO);
00558          execv(app_args[0], app_args);
00559          fprintf(stderr, "Failed to execute '%s': %s\n", app_args[0], strerror(errno));
00560          _exit(1);
00561       } else {
00562          /* parent process */
00563          close(child_stdin[0]);
00564          child_stdin[0] = -1;
00565          close(child_stdout[1]);
00566          child_stdout[1] = -1;
00567          close(child_stderr[1]);
00568          child_stderr[1] = -1;
00569          res = eivr_comm(chan, u, &child_stdin[1], &child_stdout[0], &child_stderr[0], comma_delim_args, flags);
00570       }
00571    }
00572 
00573    exit:
00574    if (u->gen_active) {
00575       ast_deactivate_generator(chan);
00576    }
00577    if (child_stdin[0] > -1) {
00578       close(child_stdin[0]);
00579    }
00580    if (child_stdin[1] > -1) {
00581       close(child_stdin[1]);
00582    }
00583    if (child_stdout[0] > -1) {
00584       close(child_stdout[0]);
00585    }
00586    if (child_stdout[1] > -1) {
00587       close(child_stdout[1]);
00588    }
00589    if (child_stderr[0] > -1) {
00590       close(child_stderr[0]);
00591    }
00592    if (child_stderr[1] > -1) {
00593       close(child_stderr[1]);
00594    }
00595    if (ser) {
00596       ao2_ref(ser, -1);
00597    }
00598    while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
00599       ast_free(entry);
00600    }
00601    return res;
00602 }
00603 
00604 static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u, 
00605             int *eivr_events_fd, int *eivr_commands_fd, int *eivr_errors_fd, 
00606             const struct ast_str *args, const struct ast_flags flags)
00607 {
00608    struct playlist_entry *entry;
00609    struct ast_frame *f;
00610    int ms;
00611    int exception;
00612    int ready_fd;
00613    int waitfds[2] = { *eivr_commands_fd, (eivr_errors_fd) ? *eivr_errors_fd : -1 };
00614    struct ast_channel *rchan;
00615    int res = -1;
00616    int test_available_fd = -1;
00617    int hangup_info_sent = 0;
00618   
00619    FILE *eivr_commands = NULL;
00620    FILE *eivr_errors = NULL;
00621    FILE *eivr_events = NULL;
00622 
00623    if (!(eivr_events = fdopen(*eivr_events_fd, "w"))) {
00624       ast_chan_log(LOG_ERROR, chan, "Could not open stream to send events\n");
00625       goto exit;
00626    }
00627    if (!(eivr_commands = fdopen(*eivr_commands_fd, "r"))) {
00628       ast_chan_log(LOG_ERROR, chan, "Could not open stream to receive commands\n");
00629       goto exit;
00630    }
00631    if (eivr_errors_fd) {  /* if opening a socket connection, error stream will not be used */
00632       if (!(eivr_errors = fdopen(*eivr_errors_fd, "r"))) {
00633          ast_chan_log(LOG_ERROR, chan, "Could not open stream to receive errors\n");
00634          goto exit;
00635       }
00636    }
00637 
00638    test_available_fd = open("/dev/null", O_RDONLY);
00639  
00640    setvbuf(eivr_events, NULL, _IONBF, 0);
00641    setvbuf(eivr_commands, NULL, _IONBF, 0);
00642    if (eivr_errors) {
00643       setvbuf(eivr_errors, NULL, _IONBF, 0);
00644    }
00645 
00646    while (1) {
00647       if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE)) {
00648          ast_chan_log(LOG_ERROR, chan, "Is a zombie\n");
00649          break;
00650       }
00651       if (!hangup_info_sent && !(ast_test_flag(&flags, run_dead)) && ast_check_hangup(chan)) {
00652          if (ast_test_flag(&flags, ignore_hangup)) {
00653             ast_verb(3, "Got check_hangup, but ignore_hangup set so sending 'I' command\n");
00654             send_eivr_event(eivr_events, 'I', "HANGUP", chan);
00655             hangup_info_sent = 1;
00656          } else {
00657             ast_verb(3, "Got check_hangup\n");
00658             send_eivr_event(eivr_events, 'H', NULL, chan);
00659             break;
00660          }
00661       }
00662  
00663       ready_fd = 0;
00664       ms = 100;
00665       errno = 0;
00666       exception = 0;
00667  
00668       rchan = ast_waitfor_nandfds(&chan, 1, waitfds, (eivr_errors_fd) ? 2 : 1, &exception, &ready_fd, &ms);
00669  
00670       if (ast_channel_state(chan) == AST_STATE_UP && !AST_LIST_EMPTY(&u->finishlist)) {
00671          AST_LIST_LOCK(&u->finishlist);
00672          while ((entry = AST_LIST_REMOVE_HEAD(&u->finishlist, list))) {
00673             send_eivr_event(eivr_events, 'F', entry->filename, chan);
00674             ast_free(entry);
00675          }
00676          AST_LIST_UNLOCK(&u->finishlist);
00677       }
00678  
00679       if (ast_channel_state(chan) == AST_STATE_UP && !(ast_check_hangup(chan)) && rchan) {
00680          /* the channel has something */
00681          f = ast_read(chan);
00682          if (!f) {
00683             ast_verb(3, "Returned no frame\n");
00684             send_eivr_event(eivr_events, 'H', NULL, chan);
00685             break;
00686          }
00687          if (f->frametype == AST_FRAME_DTMF) {
00688             send_eivr_event(eivr_events, f->subclass.integer, NULL, chan);
00689             if (u->option_autoclear) {
00690                AST_LIST_LOCK(&u->playlist);
00691                if (!u->abort_current_sound && !u->playing_silence) {
00692                   /* send interrupted file as T data */
00693                   if ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
00694                      send_eivr_event(eivr_events, 'T', entry->filename, chan);
00695                      ast_free(entry);
00696                   }
00697                }
00698                while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
00699                   send_eivr_event(eivr_events, 'D', entry->filename, chan);
00700                   ast_free(entry);
00701                }
00702                if (!u->playing_silence)
00703                   u->abort_current_sound = 1;
00704                AST_LIST_UNLOCK(&u->playlist);
00705             }
00706          } else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP)) {
00707             ast_verb(3, "Got AST_CONTROL_HANGUP\n");
00708             send_eivr_event(eivr_events, 'H', NULL, chan);
00709             if (f->data.uint32) {
00710                ast_channel_hangupcause_set(chan, f->data.uint32);
00711             }
00712             ast_frfree(f);
00713             break;
00714          }
00715          ast_frfree(f);
00716       } else if (ready_fd == *eivr_commands_fd) {
00717          char input[1024];
00718  
00719          if (exception || (dup2(*eivr_commands_fd, test_available_fd) == -1) || feof(eivr_commands)) {
00720             ast_chan_log(LOG_ERROR, chan, "Child process went away\n");
00721             break;
00722          }
00723   
00724          if (!fgets(input, sizeof(input), eivr_commands)) {
00725             continue;
00726          }
00727 
00728          ast_strip(input);
00729          ast_verb(4, "got command '%s'\n", input);
00730 
00731          if (strlen(input) < 3) {
00732             continue;
00733          }
00734 
00735          if (input[0] == EIVR_CMD_PARM) {
00736             struct ast_str *tmp = (struct ast_str *) args;
00737             send_eivr_event(eivr_events, 'P', ast_str_buffer(tmp), chan);
00738          } else if (input[0] == EIVR_CMD_DTMF) {
00739             ast_verb(4, "Sending DTMF: %s\n", &input[2]);
00740             ast_eivr_senddtmf(chan, &input[2]);
00741          } else if (input[0] == EIVR_CMD_ANS) {
00742             ast_verb(3, "Answering channel if needed and starting generator\n");
00743             if (ast_channel_state(chan) != AST_STATE_UP) {
00744                if (ast_test_flag(&flags, run_dead)) {
00745                   ast_chan_log(LOG_WARNING, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n");
00746                   send_eivr_event(eivr_events, 'Z', "ANSWER_FAILURE", chan);
00747                   continue;
00748                }
00749                if (ast_answer(chan)) {
00750                   ast_chan_log(LOG_WARNING, chan, "Failed to answer channel\n");
00751                   send_eivr_event(eivr_events, 'Z', "ANSWER_FAILURE", chan);
00752                   continue;
00753                }
00754             }
00755             if (!(u->gen_active)) {
00756                if (ast_activate_generator(chan, &gen, u) < 0) {
00757                   ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n");
00758                   send_eivr_event(eivr_events, 'Z', "GENERATOR_FAILURE", chan);
00759                } else {
00760                   u->gen_active = 1;
00761                }
00762             }
00763          } else if (input[0] == EIVR_CMD_IRPT) {
00764             if (ast_channel_state(chan) != AST_STATE_UP || ast_check_hangup(chan)) {
00765                ast_chan_log(LOG_WARNING, chan, "Queue 'I'nterrupt called on unanswered channel\n");
00766                send_eivr_event(eivr_events, 'Z', NULL, chan);
00767                continue;
00768             }
00769             AST_LIST_LOCK(&u->playlist);
00770             if (!u->abort_current_sound && !u->playing_silence) {
00771                /* send interrupted file as T data */
00772                if ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
00773                   send_eivr_event(eivr_events, 'T', entry->filename, chan);
00774                   ast_free(entry);
00775                }
00776             }
00777             while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
00778                send_eivr_event(eivr_events, 'D', entry->filename, chan);
00779                ast_free(entry);
00780             }
00781             if (!u->playing_silence) {
00782                u->abort_current_sound = 1;
00783             }
00784             AST_LIST_UNLOCK(&u->playlist);
00785          } else if (input[0] == EIVR_CMD_SQUE) {
00786             if (ast_channel_state(chan) != AST_STATE_UP || ast_check_hangup(chan)) {
00787                ast_chan_log(LOG_WARNING, chan, "Queue re'S'et called on unanswered channel\n");
00788                send_eivr_event(eivr_events, 'Z', NULL, chan);
00789                continue;
00790             }
00791             if (!ast_fileexists(&input[2], NULL, ast_channel_language(u->chan))) {
00792                ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
00793                send_eivr_event(eivr_events, 'Z', &input[2], chan);
00794             } else {
00795                AST_LIST_LOCK(&u->playlist);
00796                if (!u->abort_current_sound && !u->playing_silence) {
00797                   /* send interrupted file as T data */
00798                   if ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
00799                      send_eivr_event(eivr_events, 'T', entry->filename, chan);
00800                      ast_free(entry);
00801                   }
00802                }
00803                while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
00804                   send_eivr_event(eivr_events, 'D', entry->filename, chan);
00805                   ast_free(entry);
00806                }
00807                if (!u->playing_silence) {
00808                   u->abort_current_sound = 1;
00809                }
00810                entry = make_entry(&input[2]);
00811                if (entry) {
00812                   AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
00813                }
00814                AST_LIST_UNLOCK(&u->playlist);
00815             }
00816          } else if (input[0] == EIVR_CMD_APND) {
00817             if (ast_channel_state(chan) != AST_STATE_UP || ast_check_hangup(chan)) {
00818                ast_chan_log(LOG_WARNING, chan, "Queue 'A'ppend called on unanswered channel\n");
00819                send_eivr_event(eivr_events, 'Z', NULL, chan);
00820                continue;
00821             }
00822             if (!ast_fileexists(&input[2], NULL, ast_channel_language(u->chan))) {
00823                ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
00824                send_eivr_event(eivr_events, 'Z', &input[2], chan);
00825             } else {
00826                entry = make_entry(&input[2]);
00827                if (entry) {
00828                   AST_LIST_LOCK(&u->playlist);
00829                   AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
00830                   AST_LIST_UNLOCK(&u->playlist);
00831                }
00832             }
00833          } else if (input[0] == EIVR_CMD_GET) {
00834             char response[2048];
00835             ast_verb(4, "Retriving Variables from channel: %s\n", &input[2]);
00836             ast_eivr_getvariable(chan, &input[2], response, sizeof(response));
00837             send_eivr_event(eivr_events, 'G', response, chan);
00838          } else if (input[0] == EIVR_CMD_SVAR) {
00839             ast_verb(4, "Setting Variables in channel: %s\n", &input[2]);
00840             ast_eivr_setvariable(chan, &input[2]);
00841          } else if (input[0] == EIVR_CMD_LOG) {
00842             ast_chan_log(LOG_NOTICE, chan, "Log message from EIVR: %s\n", &input[2]);
00843          } else if (input[0] == EIVR_CMD_XIT) {
00844             ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]);
00845             ast_chan_log(LOG_WARNING, chan, "e'X'it command is depricated, use 'E'xit instead\n");
00846             res = 0;
00847             break;
00848          } else if (input[0] == EIVR_CMD_EXIT) {
00849             ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]);
00850             send_eivr_event(eivr_events, 'E', NULL, chan);
00851             res = 0;
00852             break;
00853          } else if (input[0] == EIVR_CMD_HGUP) {
00854             ast_chan_log(LOG_NOTICE, chan, "Hanging up: %s\n", &input[2]);
00855             send_eivr_event(eivr_events, 'H', NULL, chan);
00856             break;
00857          } else if (input[0] == EIVR_CMD_OPT) {
00858             if (ast_channel_state(chan) != AST_STATE_UP || ast_check_hangup(chan)) {
00859                ast_chan_log(LOG_WARNING, chan, "Option called on unanswered channel\n");
00860                send_eivr_event(eivr_events, 'Z', NULL, chan);
00861                continue;
00862             }
00863             if (!strcasecmp(&input[2], "autoclear"))
00864                u->option_autoclear = 1;
00865             else if (!strcasecmp(&input[2], "noautoclear"))
00866                u->option_autoclear = 0;
00867             else
00868                ast_chan_log(LOG_WARNING, chan, "Unknown option requested: %s\n", &input[2]);
00869          }
00870       } else if (eivr_errors_fd && (ready_fd == *eivr_errors_fd)) {
00871          char input[1024];
00872   
00873          if (exception || feof(eivr_errors)) {
00874             ast_chan_log(LOG_ERROR, chan, "Child process went away\n");
00875             break;
00876          }
00877          if (fgets(input, sizeof(input), eivr_errors)) {
00878             ast_chan_log(LOG_NOTICE, chan, "stderr: %s\n", ast_strip(input));
00879          }
00880       } else if ((ready_fd < 0) && ms) { 
00881          if (errno == 0 || errno == EINTR)
00882             continue;
00883  
00884          ast_chan_log(LOG_ERROR, chan, "Wait failed (%s)\n", strerror(errno));
00885          break;
00886       }
00887    }
00888  
00889    exit:
00890    if (test_available_fd > -1) {
00891       close(test_available_fd);
00892    }
00893    if (eivr_events) {
00894       fclose(eivr_events);
00895       *eivr_events_fd = -1;
00896    }
00897    if (eivr_commands) {
00898       fclose(eivr_commands);
00899       *eivr_commands_fd = -1;
00900    }
00901    if (eivr_errors) {
00902       fclose(eivr_errors);
00903       *eivr_errors_fd = -1;
00904    }
00905    return res;
00906 }
00907 
00908 static int unload_module(void)
00909 {
00910    return ast_unregister_application(app);
00911 }
00912 
00913 static int load_module(void)
00914 {
00915    return ast_register_application_xml(app, app_exec);
00916 }
00917 
00918 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "External IVR Interface Application");