Thu Apr 28 2011 17:15:14

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 #include "asterisk.h"
00035 
00036 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 232813 $")
00037 
00038 #include <signal.h>
00039 
00040 #include "asterisk/lock.h"
00041 #include "asterisk/file.h"
00042 #include "asterisk/channel.h"
00043 #include "asterisk/pbx.h"
00044 #include "asterisk/module.h"
00045 #include "asterisk/linkedlists.h"
00046 #include "asterisk/app.h"
00047 #include "asterisk/utils.h"
00048 #include "asterisk/tcptls.h"
00049 #include "asterisk/astobj2.h"
00050 
00051 static const char *app = "ExternalIVR";
00052 
00053 static const char *synopsis = "Interfaces with an external IVR application";
00054 static const char *descrip =
00055 "  ExternalIVR(command|ivr://ivrhosti([,arg[,arg...]])[,options]): Either forks a process\n"
00056 "to run given command or makes a socket to connect to given host and starts\n"
00057 "a generator on the channel. The generator's play list is controlled by the\n"
00058 "external application, which can add and clear entries via simple commands\n"
00059 "issued over its stdout. The external application will receive all DTMF events\n"
00060 "received on the channel, and notification if the channel is hung up. The\n"
00061 "application will not be forcibly terminated when the channel is hung up.\n"
00062 "See doc/externalivr.txt for a protocol specification.\n"
00063 "The 'n' option tells ExternalIVR() not to answer the channel. \n"
00064 "The 'i' option tells ExternalIVR() not to send a hangup and exit when the\n"
00065 "  channel receives a hangup, instead it sends an 'I' informative message\n"
00066 "  meaning that the external application MUST hang up the call with an H command\n"
00067 "The 'd' option tells ExternalIVR() to run on a channel that has been hung up\n"
00068 "  and will not look for hangups.  The external application must exit with\n"
00069 "  an 'E' command.\n";
00070 
00071 /* XXX the parser in gcc 2.95 gets confused if you don't put a space between 'name' and the comma */
00072 #define ast_chan_log(level, channel, format, ...) ast_log(level, "%s: " format, channel->name , ## __VA_ARGS__)
00073 
00074 enum {
00075    noanswer = (1 << 0),
00076    ignore_hangup = (1 << 1),
00077    run_dead = (1 << 2),
00078 } options_flags;
00079 
00080 AST_APP_OPTIONS(app_opts, {
00081    AST_APP_OPTION('n', noanswer),
00082    AST_APP_OPTION('i', ignore_hangup),
00083    AST_APP_OPTION('d', run_dead),
00084 });
00085 
00086 struct playlist_entry {
00087    AST_LIST_ENTRY(playlist_entry) list;
00088    char filename[1];
00089 };
00090 
00091 struct ivr_localuser {
00092    struct ast_channel *chan;
00093    AST_LIST_HEAD(playlist, playlist_entry) playlist;
00094    AST_LIST_HEAD(finishlist, playlist_entry) finishlist;
00095    int abort_current_sound;
00096    int playing_silence;
00097    int option_autoclear;
00098    int gen_active;
00099 };
00100 
00101 
00102 struct gen_state {
00103    struct ivr_localuser *u;
00104    struct ast_filestream *stream;
00105    struct playlist_entry *current;
00106    int sample_queue;
00107 };
00108 
00109 static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u, 
00110    int *eivr_events_fd, int *eivr_commands_fd, int *eivr_errors_fd, 
00111    const struct ast_str *args, const struct ast_flags flags);
00112 
00113 int eivr_connect_socket(struct ast_channel *chan, const char *host, int port);
00114 
00115 static void send_eivr_event(FILE *handle, const char event, const char *data,
00116    const struct ast_channel *chan)
00117 {
00118    struct ast_str *tmp = ast_str_create(12);
00119 
00120    ast_str_append(&tmp, 0, "%c,%10d", event, (int)time(NULL));
00121    if (data) {
00122       ast_str_append(&tmp, 0, ",%s", data);
00123    }
00124 
00125    fprintf(handle, "%s\n", ast_str_buffer(tmp));
00126    ast_debug(1, "sent '%s'\n", ast_str_buffer(tmp));
00127 }
00128 
00129 static void *gen_alloc(struct ast_channel *chan, void *params)
00130 {
00131    struct ivr_localuser *u = params;
00132    struct gen_state *state;
00133 
00134    if (!(state = ast_calloc(1, sizeof(*state))))
00135       return NULL;
00136 
00137    state->u = u;
00138 
00139    return state;
00140 }
00141 
00142 static void gen_closestream(struct gen_state *state)
00143 {
00144    if (!state->stream)
00145       return;
00146 
00147    ast_closestream(state->stream);
00148    state->u->chan->stream = NULL;
00149    state->stream = NULL;
00150 }
00151 
00152 static void gen_release(struct ast_channel *chan, void *data)
00153 {
00154    struct gen_state *state = data;
00155 
00156    gen_closestream(state);
00157    ast_free(data);
00158 }
00159 
00160 /* caller has the playlist locked */
00161 static int gen_nextfile(struct gen_state *state)
00162 {
00163    struct ivr_localuser *u = state->u;
00164    char *file_to_stream;
00165 
00166    u->abort_current_sound = 0;
00167    u->playing_silence = 0;
00168    gen_closestream(state);
00169 
00170    while (!state->stream) {
00171       state->current = AST_LIST_REMOVE_HEAD(&u->playlist, list);
00172       if (state->current) {
00173          file_to_stream = state->current->filename;
00174       } else {
00175          file_to_stream = "silence/10";
00176          u->playing_silence = 1;
00177       }
00178 
00179       if (!(state->stream = ast_openstream_full(u->chan, file_to_stream, u->chan->language, 1))) {
00180          ast_chan_log(LOG_WARNING, u->chan, "File '%s' could not be opened: %s\n", file_to_stream, strerror(errno));
00181          if (!u->playing_silence) {
00182             continue;
00183          } else {
00184             break;
00185          }
00186       }
00187    }
00188 
00189    return (!state->stream);
00190 }
00191 
00192 static struct ast_frame *gen_readframe(struct gen_state *state)
00193 {
00194    struct ast_frame *f = NULL;
00195    struct ivr_localuser *u = state->u;
00196 
00197    if (u->abort_current_sound ||
00198       (u->playing_silence && AST_LIST_FIRST(&u->playlist))) {
00199       gen_closestream(state);
00200       AST_LIST_LOCK(&u->playlist);
00201       gen_nextfile(state);
00202       AST_LIST_UNLOCK(&u->playlist);
00203    }
00204 
00205    if (!(state->stream && (f = ast_readframe(state->stream)))) {
00206       if (state->current) {
00207          AST_LIST_LOCK(&u->finishlist);
00208          AST_LIST_INSERT_TAIL(&u->finishlist, state->current, list);
00209          AST_LIST_UNLOCK(&u->finishlist);
00210          state->current = NULL;
00211       }
00212       if (!gen_nextfile(state))
00213          f = ast_readframe(state->stream);
00214    }
00215 
00216    return f;
00217 }
00218 
00219 static int gen_generate(struct ast_channel *chan, void *data, int len, int samples)
00220 {
00221    struct gen_state *state = data;
00222    struct ast_frame *f = NULL;
00223    int res = 0;
00224 
00225    state->sample_queue += samples;
00226 
00227    while (state->sample_queue > 0) {
00228       if (!(f = gen_readframe(state)))
00229          return -1;
00230 
00231       res = ast_write(chan, f);
00232       ast_frfree(f);
00233       if (res < 0) {
00234          ast_chan_log(LOG_WARNING, chan, "Failed to write frame: %s\n", strerror(errno));
00235          return -1;
00236       }
00237       state->sample_queue -= f->samples;
00238    }
00239 
00240    return res;
00241 }
00242 
00243 static struct ast_generator gen =
00244 {
00245    alloc: gen_alloc,
00246    release: gen_release,
00247    generate: gen_generate,
00248 };
00249 
00250 static void ast_eivr_getvariable(struct ast_channel *chan, char *data, char *outbuf, int outbuflen)
00251 {
00252    /* original input data: "G,var1,var2," */
00253    /* data passed as "data":  "var1,var2" */
00254 
00255    char *inbuf, *variable;
00256    const char *value;
00257    int j;
00258    struct ast_str *newstring = ast_str_alloca(outbuflen); 
00259 
00260    outbuf[0] = '\0';
00261 
00262    for (j = 1, inbuf = data; ; j++) {
00263       variable = strsep(&inbuf, ",");
00264       if (variable == NULL) {
00265          int outstrlen = strlen(outbuf);
00266          if (outstrlen && outbuf[outstrlen - 1] == ',') {
00267             outbuf[outstrlen - 1] = 0;
00268          }
00269          break;
00270       }
00271       
00272       ast_channel_lock(chan);
00273       if (!(value = pbx_builtin_getvar_helper(chan, variable))) {
00274          value = "";
00275       }
00276 
00277       ast_str_append(&newstring, 0, "%s=%s,", variable, value);
00278       ast_channel_unlock(chan);
00279       ast_copy_string(outbuf, ast_str_buffer(newstring), outbuflen);
00280    }
00281 }
00282 
00283 static void ast_eivr_setvariable(struct ast_channel *chan, char *data)
00284 {
00285    char *value;
00286 
00287    char *inbuf = ast_strdupa(data), *variable;
00288 
00289    for (variable = strsep(&inbuf, ","); variable; variable = strsep(&inbuf, ",")) {
00290       ast_debug(1, "Setting up a variable: %s\n", variable);
00291       /* variable contains "varname=value" */
00292       value = strchr(variable, '=');
00293       if (!value) {
00294          value = "";
00295       } else {
00296          *value++ = '\0';
00297       }
00298       pbx_builtin_setvar_helper(chan, variable, value);
00299    }
00300 }
00301 
00302 static struct playlist_entry *make_entry(const char *filename)
00303 {
00304    struct playlist_entry *entry;
00305 
00306    if (!(entry = ast_calloc(1, sizeof(*entry) + strlen(filename) + 10))) /* XXX why 10 ? */
00307       return NULL;
00308 
00309    strcpy(entry->filename, filename);
00310 
00311    return entry;
00312 }
00313 
00314 static int app_exec(struct ast_channel *chan, void *data)
00315 {
00316    struct ast_flags flags = { 0, };
00317    char *opts[0];
00318    struct playlist_entry *entry;
00319    int child_stdin[2] = { -1, -1 };
00320    int child_stdout[2] = { -1, -1 };
00321    int child_stderr[2] = { -1, -1 };
00322    int res = -1;
00323    int pid;
00324 
00325    char hostname[1024];
00326    char *port_str = NULL;
00327    int port = 0;
00328    struct ast_tcptls_session_instance *ser = NULL;
00329 
00330    struct ivr_localuser foo = {
00331       .playlist = AST_LIST_HEAD_INIT_VALUE,
00332       .finishlist = AST_LIST_HEAD_INIT_VALUE,
00333       .gen_active = 0,
00334    };
00335    struct ivr_localuser *u = &foo;
00336 
00337    char *buf;
00338    int j;
00339    char *s, **app_args, *e; 
00340    struct ast_str *pipe_delim_args = ast_str_create(100);
00341 
00342    AST_DECLARE_APP_ARGS(eivr_args,
00343       AST_APP_ARG(cmd)[32];
00344    );
00345    AST_DECLARE_APP_ARGS(application_args,
00346       AST_APP_ARG(cmd)[32];
00347    );
00348 
00349    u->abort_current_sound = 0;
00350    u->chan = chan;
00351 
00352    if (ast_strlen_zero(data)) {
00353       ast_log(LOG_WARNING, "ExternalIVR requires a command to execute\n");
00354       return -1;
00355    }
00356 
00357    buf = ast_strdupa(data);
00358    AST_STANDARD_APP_ARGS(eivr_args, buf);
00359 
00360    if ((s = strchr(eivr_args.cmd[0], '('))) {
00361       s[0] = ',';
00362       if (( e = strrchr(s, ')')) ) {
00363          *e = '\0';
00364       } else {
00365          ast_log(LOG_ERROR, "Parse error, no closing paren?\n");
00366       }
00367       AST_STANDARD_APP_ARGS(application_args, eivr_args.cmd[0]);
00368       app_args = application_args.argv;
00369 
00370       /* Put the application + the arguments in a | delimited list */
00371       ast_str_reset(pipe_delim_args);
00372       for (j = 0; application_args.cmd[j] != NULL; j++) {
00373          ast_str_append(&pipe_delim_args, 0, "%s%s", j == 0 ? "" : ",", application_args.cmd[j]);
00374       }
00375 
00376       /* Parse the ExternalIVR() arguments */
00377       if (option_debug)
00378          ast_debug(1, "Parsing options from: [%s]\n", eivr_args.cmd[1]);
00379       ast_app_parse_options(app_opts, &flags, opts, eivr_args.cmd[1]);
00380       if (option_debug) {
00381          if (ast_test_flag(&flags, noanswer))
00382             ast_debug(1, "noanswer is set\n");
00383          if (ast_test_flag(&flags, ignore_hangup))
00384             ast_debug(1, "ignore_hangup is set\n");
00385          if (ast_test_flag(&flags, run_dead))
00386             ast_debug(1, "run_dead is set\n");
00387       }
00388 
00389    } else {
00390       app_args = eivr_args.argv;
00391       for (j = 0; eivr_args.cmd[j] != NULL; j++) {
00392          ast_str_append(&pipe_delim_args, 0, "%s%s", j == 0 ? "" : "|", eivr_args.cmd[j]);
00393       }
00394    }
00395    
00396    if (ast_strlen_zero(data)) {
00397       ast_log(LOG_WARNING, "ExternalIVR requires a command to execute\n");
00398       return -1;
00399    }
00400 
00401    if (!(ast_test_flag(&flags, noanswer))) {
00402       ast_chan_log(LOG_WARNING, chan, "Answering channel and starting generator\n");
00403       if (chan->_state != AST_STATE_UP) {
00404          if (ast_test_flag(&flags, run_dead)) {
00405             ast_chan_log(LOG_WARNING, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n");
00406             goto exit;
00407          }
00408          ast_answer(chan);
00409       }
00410       if (ast_activate_generator(chan, &gen, u) < 0) {
00411          ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n");
00412          goto exit;
00413       } else {
00414          u->gen_active = 1;
00415       }
00416    }
00417 
00418    if (!strncmp(app_args[0], "ivr://", 6)) {
00419       struct ast_tcptls_session_args ivr_desc = {
00420          .accept_fd = -1,
00421          .name = "IVR",
00422       };
00423       struct ast_hostent hp;
00424 
00425       /*communicate through socket to server*/
00426       ast_debug(1, "Parsing hostname:port for socket connect from \"%s\"\n", app_args[0]);
00427       ast_copy_string(hostname, app_args[0] + 6, sizeof(hostname));
00428       if ((port_str = strchr(hostname, ':')) != NULL) {
00429          port_str[0] = 0;
00430          port_str += 1;
00431          port = atoi(port_str);
00432       }
00433       if (!port) {
00434          port = 2949;  /* default port, if one is not provided */
00435       }
00436 
00437       ast_gethostbyname(hostname, &hp);
00438       ivr_desc.local_address.sin_family = AF_INET;
00439       ivr_desc.local_address.sin_port = htons(port);
00440       memcpy(&ivr_desc.local_address.sin_addr.s_addr, hp.hp.h_addr, hp.hp.h_length);
00441       if (!(ser = ast_tcptls_client_create(&ivr_desc)) || !(ser = ast_tcptls_client_start(ser))) {
00442          goto exit;
00443       }
00444       res = eivr_comm(chan, u, &ser->fd, &ser->fd, NULL, pipe_delim_args, flags);
00445 
00446    } else {
00447       if (pipe(child_stdin)) {
00448          ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child input: %s\n", strerror(errno));
00449          goto exit;
00450       }
00451       if (pipe(child_stdout)) {
00452          ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child output: %s\n", strerror(errno));
00453          goto exit;
00454       }
00455       if (pipe(child_stderr)) {
00456          ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child errors: %s\n", strerror(errno));
00457          goto exit;
00458       }
00459    
00460       pid = ast_safe_fork(0);
00461       if (pid < 0) {
00462          ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno));
00463          goto exit;
00464       }
00465    
00466       if (!pid) {
00467          /* child process */
00468          if (ast_opt_high_priority)
00469             ast_set_priority(0);
00470    
00471          dup2(child_stdin[0], STDIN_FILENO);
00472          dup2(child_stdout[1], STDOUT_FILENO);
00473          dup2(child_stderr[1], STDERR_FILENO);
00474          ast_close_fds_above_n(STDERR_FILENO);
00475          execv(app_args[0], app_args);
00476          fprintf(stderr, "Failed to execute '%s': %s\n", app_args[0], strerror(errno));
00477          _exit(1);
00478       } else {
00479          /* parent process */
00480          close(child_stdin[0]);
00481          child_stdin[0] = -1;
00482          close(child_stdout[1]);
00483          child_stdout[1] = -1;
00484          close(child_stderr[1]);
00485          child_stderr[1] = -1;
00486          res = eivr_comm(chan, u, &child_stdin[1], &child_stdout[0], &child_stderr[0], pipe_delim_args, flags);
00487       }
00488    }
00489 
00490    exit:
00491    if (u->gen_active) {
00492       ast_deactivate_generator(chan);
00493    }
00494    if (child_stdin[0] > -1) {
00495       close(child_stdin[0]);
00496    }
00497    if (child_stdin[1] > -1) {
00498       close(child_stdin[1]);
00499    }
00500    if (child_stdout[0] > -1) {
00501       close(child_stdout[0]);
00502    }
00503    if (child_stdout[1] > -1) {
00504       close(child_stdout[1]);
00505    }
00506    if (child_stderr[0] > -1) {
00507       close(child_stderr[0]);
00508    }
00509    if (child_stderr[1] > -1) {
00510       close(child_stderr[1]);
00511    }
00512    if (ser) {
00513       ao2_ref(ser, -1);
00514    }
00515    while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
00516       ast_free(entry);
00517    }
00518    return res;
00519 }
00520 
00521 static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u, 
00522             int *eivr_events_fd, int *eivr_commands_fd, int *eivr_errors_fd, 
00523             const struct ast_str *args, const struct ast_flags flags)
00524 {
00525    struct playlist_entry *entry;
00526    struct ast_frame *f;
00527    int ms;
00528    int exception;
00529    int ready_fd;
00530    int waitfds[2] = { *eivr_commands_fd, (eivr_errors_fd) ? *eivr_errors_fd : -1 };
00531    struct ast_channel *rchan;
00532    char *command;
00533    int res = -1;
00534    int test_available_fd = -1;
00535    int hangup_info_sent = 0;
00536   
00537    FILE *eivr_commands = NULL;
00538    FILE *eivr_errors = NULL;
00539    FILE *eivr_events = NULL;
00540 
00541    if (!(eivr_events = fdopen(*eivr_events_fd, "w"))) {
00542       ast_chan_log(LOG_WARNING, chan, "Could not open stream to send events\n");
00543       goto exit;
00544    }
00545    if (!(eivr_commands = fdopen(*eivr_commands_fd, "r"))) {
00546       ast_chan_log(LOG_WARNING, chan, "Could not open stream to receive commands\n");
00547       goto exit;
00548    }
00549    if (eivr_errors_fd) {  /* if opening a socket connection, error stream will not be used */
00550       if (!(eivr_errors = fdopen(*eivr_errors_fd, "r"))) {
00551          ast_chan_log(LOG_WARNING, chan, "Could not open stream to receive errors\n");
00552          goto exit;
00553       }
00554    }
00555 
00556    test_available_fd = open("/dev/null", O_RDONLY);
00557  
00558    setvbuf(eivr_events, NULL, _IONBF, 0);
00559    setvbuf(eivr_commands, NULL, _IONBF, 0);
00560    if (eivr_errors) {
00561       setvbuf(eivr_errors, NULL, _IONBF, 0);
00562    }
00563 
00564    res = 0;
00565  
00566    while (1) {
00567       if (ast_test_flag(chan, AST_FLAG_ZOMBIE)) {
00568          ast_chan_log(LOG_NOTICE, chan, "Is a zombie\n");
00569          res = -1;
00570          break;
00571       }
00572       if (!hangup_info_sent && !(ast_test_flag(&flags, run_dead)) && ast_check_hangup(chan)) {
00573          if (ast_test_flag(&flags, ignore_hangup)) {
00574             ast_chan_log(LOG_NOTICE, chan, "Got check_hangup, but ignore_hangup set so sending 'I' command\n");
00575             send_eivr_event(eivr_events, 'I', "HANGUP", chan);
00576             hangup_info_sent = 1;
00577          } else {
00578             ast_chan_log(LOG_NOTICE, chan, "Got check_hangup\n");
00579             send_eivr_event(eivr_events, 'H', NULL, chan);
00580             res = -1;
00581             break;
00582          }
00583       }
00584  
00585       ready_fd = 0;
00586       ms = 100;
00587       errno = 0;
00588       exception = 0;
00589  
00590       rchan = ast_waitfor_nandfds(&chan, 1, waitfds, (eivr_errors_fd) ? 2 : 1, &exception, &ready_fd, &ms);
00591  
00592       if (chan->_state == AST_STATE_UP && !AST_LIST_EMPTY(&u->finishlist)) {
00593          AST_LIST_LOCK(&u->finishlist);
00594          while ((entry = AST_LIST_REMOVE_HEAD(&u->finishlist, list))) {
00595             send_eivr_event(eivr_events, 'F', entry->filename, chan);
00596             ast_free(entry);
00597          }
00598          AST_LIST_UNLOCK(&u->finishlist);
00599       }
00600  
00601       if (chan->_state == AST_STATE_UP && !(ast_check_hangup(chan)) && rchan) {
00602          /* the channel has something */
00603          f = ast_read(chan);
00604          if (!f) {
00605             ast_chan_log(LOG_NOTICE, chan, "Returned no frame\n");
00606             send_eivr_event(eivr_events, 'H', NULL, chan);
00607             res = -1;
00608             break;
00609          }
00610          if (f->frametype == AST_FRAME_DTMF) {
00611             send_eivr_event(eivr_events, f->subclass, NULL, chan);
00612             if (u->option_autoclear) {
00613                if (!u->abort_current_sound && !u->playing_silence)
00614                   send_eivr_event(eivr_events, 'T', NULL, chan);
00615                AST_LIST_LOCK(&u->playlist);
00616                while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
00617                   send_eivr_event(eivr_events, 'D', entry->filename, chan);
00618                   ast_free(entry);
00619                }
00620                if (!u->playing_silence)
00621                   u->abort_current_sound = 1;
00622                AST_LIST_UNLOCK(&u->playlist);
00623             }
00624          } else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) {
00625             ast_chan_log(LOG_NOTICE, chan, "Got AST_CONTROL_HANGUP\n");
00626             send_eivr_event(eivr_events, 'H', NULL, chan);
00627             if (f->data.uint32) {
00628                chan->hangupcause = f->data.uint32;
00629             }
00630             ast_frfree(f);
00631             res = -1;
00632             break;
00633          }
00634          ast_frfree(f);
00635       } else if (ready_fd == *eivr_commands_fd) {
00636          char input[1024];
00637  
00638          if (exception || (dup2(*eivr_commands_fd, test_available_fd) == -1) || feof(eivr_commands)) {
00639             ast_chan_log(LOG_WARNING, chan, "Child process went away\n");
00640             res = -1;
00641             break;
00642          }
00643   
00644          if (!fgets(input, sizeof(input), eivr_commands))
00645             continue;
00646  
00647          command = ast_strip(input);
00648   
00649          if (option_debug)
00650             ast_debug(1, "got command '%s'\n", input);
00651   
00652          if (strlen(input) < 4)
00653             continue;
00654   
00655          if (input[0] == 'P') {
00656             struct ast_str *tmp = (struct ast_str *) args;
00657             send_eivr_event(eivr_events, 'P', ast_str_buffer(tmp), chan);
00658          } else if ( input[0] == 'T' ) {
00659             ast_chan_log(LOG_WARNING, chan, "Answering channel if needed and starting generator\n");
00660             if (chan->_state != AST_STATE_UP) {
00661                if (ast_test_flag(&flags, run_dead)) {
00662                   ast_chan_log(LOG_WARNING, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n");
00663                   send_eivr_event(eivr_events, 'Z', "ANSWER_FAILURE", chan);
00664                   continue;
00665                }
00666                ast_answer(chan);
00667             }
00668             if (!(u->gen_active)) {
00669                if (ast_activate_generator(chan, &gen, u) < 0) {
00670                   ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n");
00671                   send_eivr_event(eivr_events, 'Z', "GENERATOR_FAILURE", chan);
00672                } else {
00673                   u->gen_active = 1;
00674                }
00675             }
00676          } else if (input[0] == 'S') {
00677             if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) {
00678                ast_chan_log(LOG_WARNING, chan, "Queue 'S'et called on unanswered channel\n");
00679                send_eivr_event(eivr_events, 'Z', NULL, chan);
00680                continue;
00681             }
00682             if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) {
00683                ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
00684                send_eivr_event(eivr_events, 'Z', NULL, chan);
00685                strcpy(&input[2], "exception");
00686             }
00687             if (!u->abort_current_sound && !u->playing_silence)
00688                send_eivr_event(eivr_events, 'T', NULL, chan);
00689             AST_LIST_LOCK(&u->playlist);
00690             while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
00691                send_eivr_event(eivr_events, 'D', entry->filename, chan);
00692                ast_free(entry);
00693             }
00694             if (!u->playing_silence)
00695                u->abort_current_sound = 1;
00696             entry = make_entry(&input[2]);
00697             if (entry)
00698                AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
00699             AST_LIST_UNLOCK(&u->playlist);
00700          } else if (input[0] == 'A') {
00701             if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) {
00702                ast_chan_log(LOG_WARNING, chan, "Queue 'A'ppend called on unanswered channel\n");
00703                send_eivr_event(eivr_events, 'Z', NULL, chan);
00704                continue;
00705             }
00706             if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) {
00707                ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
00708                send_eivr_event(eivr_events, 'Z', NULL, chan);
00709                strcpy(&input[2], "exception");
00710             }
00711             entry = make_entry(&input[2]);
00712             if (entry) {
00713                AST_LIST_LOCK(&u->playlist);
00714                AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
00715                AST_LIST_UNLOCK(&u->playlist);
00716             }
00717          } else if (input[0] == 'G') {
00718             /* A get variable message:  "G,variable1,variable2,..." */
00719             char response[2048];
00720 
00721             ast_chan_log(LOG_NOTICE, chan, "Getting a Variable out of the channel: %s\n", &input[2]);
00722             ast_eivr_getvariable(chan, &input[2], response, sizeof(response));
00723             send_eivr_event(eivr_events, 'G', response, chan);
00724          } else if (input[0] == 'V') {
00725             /* A set variable message:  "V,variablename=foo" */
00726             ast_chan_log(LOG_NOTICE, chan, "Setting a Variable up: %s\n", &input[2]);
00727             ast_eivr_setvariable(chan, &input[2]);
00728          } else if (input[0] == 'L') {
00729             ast_chan_log(LOG_NOTICE, chan, "Log message from EIVR: %s\n", &input[2]);
00730          } else if (input[0] == 'X') {
00731             ast_chan_log(LOG_NOTICE, chan, "Exiting ExternalIVR: %s\n", &input[2]);
00732             /*! \todo add deprecation debug message for X command here */
00733             res = 0;
00734             break;
00735          } else if (input[0] == 'E') {
00736             ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]);
00737             send_eivr_event(eivr_events, 'E', NULL, chan);
00738             res = 0;
00739             break;
00740          } else if (input[0] == 'H') {
00741             ast_chan_log(LOG_NOTICE, chan, "Hanging up: %s\n", &input[2]);
00742             send_eivr_event(eivr_events, 'H', NULL, chan);
00743             res = -1;
00744             break;
00745          } else if (input[0] == 'O') {
00746             if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) {
00747                ast_chan_log(LOG_WARNING, chan, "Option called on unanswered channel\n");
00748                send_eivr_event(eivr_events, 'Z', NULL, chan);
00749                continue;
00750             }
00751             if (!strcasecmp(&input[2], "autoclear"))
00752                u->option_autoclear = 1;
00753             else if (!strcasecmp(&input[2], "noautoclear"))
00754                u->option_autoclear = 0;
00755             else
00756                ast_chan_log(LOG_WARNING, chan, "Unknown option requested '%s'\n", &input[2]);
00757          }
00758       } else if (eivr_errors_fd && (ready_fd == *eivr_errors_fd)) {
00759          char input[1024];
00760   
00761          if (exception || feof(eivr_errors)) {
00762             ast_chan_log(LOG_WARNING, chan, "Child process went away\n");
00763             res = -1;
00764             break;
00765          }
00766          if (fgets(input, sizeof(input), eivr_errors)) {
00767             command = ast_strip(input);
00768             ast_chan_log(LOG_NOTICE, chan, "stderr: %s\n", command);
00769          }
00770       } else if ((ready_fd < 0) && ms) { 
00771          if (errno == 0 || errno == EINTR)
00772             continue;
00773  
00774          ast_chan_log(LOG_WARNING, chan, "Wait failed (%s)\n", strerror(errno));
00775          break;
00776       }
00777    }
00778  
00779    exit:
00780    if (test_available_fd > -1) {
00781       close(test_available_fd);
00782    }
00783    if (eivr_events) {
00784       fclose(eivr_events);
00785       *eivr_events_fd = -1;
00786    }
00787    if (eivr_commands) {
00788       fclose(eivr_commands);
00789       *eivr_commands_fd = -1;
00790    }
00791    if (eivr_errors) {
00792       fclose(eivr_errors);
00793       *eivr_errors_fd = -1;
00794    }
00795    return res;
00796 }
00797 
00798 static int unload_module(void)
00799 {
00800    return ast_unregister_application(app);
00801 }
00802 
00803 static int load_module(void)
00804 {
00805    return ast_register_application(app, app_exec, synopsis, descrip);
00806 }
00807 
00808 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "External IVR Interface Application");