Fri Jun 19 12:09:25 2009

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

Generated on Fri Jun 19 12:09:25 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7