Fri Jul 15 2011 11:56:58

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