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

Generated on Wed Oct 28 11:50:52 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.6