Wed Mar 3 22:35:40 2010

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

Generated on 3 Mar 2010 for Asterisk - the Open Source PBX by  doxygen 1.6.1