Fixes issue with hangups not being sent and external process never terminating.
[asterisk/asterisk.git] / apps / app_externalivr.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Kevin P. Fleming <kpfleming@digium.com>
7  *
8  * Portions taken from the file-based music-on-hold work
9  * created by Anthony Minessale II in res_musiconhold.c
10  *
11  * See http://www.asterisk.org for more information about
12  * the Asterisk project. Please do not directly contact
13  * any of the maintainers of this project for assistance;
14  * the project provides a web site, mailing lists and IRC
15  * channels for your use.
16  *
17  * This program is free software, distributed under the terms of
18  * the GNU General Public License Version 2. See the LICENSE file
19  * at the top of the source tree.
20  */
21
22 /*! \file
23  *
24  * \brief External IVR application interface
25  *
26  * \author Kevin P. Fleming <kpfleming@digium.com>
27  *
28  * \note Portions taken from the file-based music-on-hold work
29  * created by Anthony Minessale II in res_musiconhold.c
30  *
31  * \ingroup applications
32  */
33
34 #include "asterisk.h"
35
36 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
37
38 #include <signal.h>
39
40 #include "asterisk/lock.h"
41 #include "asterisk/file.h"
42 #include "asterisk/channel.h"
43 #include "asterisk/pbx.h"
44 #include "asterisk/module.h"
45 #include "asterisk/linkedlists.h"
46 #include "asterisk/app.h"
47 #include "asterisk/utils.h"
48 #include "asterisk/tcptls.h"
49 #include "asterisk/astobj2.h"
50
51 static const char *app = "ExternalIVR";
52
53 static const char *synopsis = "Interfaces with an external IVR application";
54 static const char *descrip =
55 "  ExternalIVR(command|ivr://ivrhosti([,arg[,arg...]])[,options]): Either forks a process\n"
56 "to run given command or makes a socket to connect to given host and starts\n"
57 "a generator on the channel. The generator's play list is controlled by the\n"
58 "external application, which can add and clear entries via simple commands\n"
59 "issued over its stdout. The external application will receive all DTMF events\n"
60 "received on the channel, and notification if the channel is hung up. The\n"
61 "application will not be forcibly terminated when the channel is hung up.\n"
62 "See doc/externalivr.txt for a protocol specification.\n"
63 "The 'n' option tells ExternalIVR() not to answer the channel. \n"
64 "The 'i' option tells ExternalIVR() not to send a hangup and exit when the\n"
65 "  channel receives a hangup, instead it sends an 'I' informative message\n"
66 "  meaning that the external application MUST hang up the call with an H command\n"
67 "The 'd' option tells ExternalIVR() to run on a channel that has been hung up\n"
68 "  and will not look for hangups.  The external application must exit with\n"
69 "  an 'E' command.\n";
70
71 /* XXX the parser in gcc 2.95 gets confused if you don't put a space between 'name' and the comma */
72 #define ast_chan_log(level, channel, format, ...) ast_log(level, "%s: " format, channel->name , ## __VA_ARGS__)
73
74 enum {
75         noanswer = (1 << 0),
76         ignore_hangup = (1 << 1),
77         run_dead = (1 << 2),
78 } options_flags;
79
80 AST_APP_OPTIONS(app_opts, {
81         AST_APP_OPTION('n', noanswer),
82         AST_APP_OPTION('i', ignore_hangup),
83         AST_APP_OPTION('d', run_dead),
84 });
85
86 struct playlist_entry {
87         AST_LIST_ENTRY(playlist_entry) list;
88         char filename[1];
89 };
90
91 struct ivr_localuser {
92         struct ast_channel *chan;
93         AST_LIST_HEAD(playlist, playlist_entry) playlist;
94         AST_LIST_HEAD(finishlist, playlist_entry) finishlist;
95         int abort_current_sound;
96         int playing_silence;
97         int option_autoclear;
98         int gen_active;
99 };
100
101
102 struct gen_state {
103         struct ivr_localuser *u;
104         struct ast_filestream *stream;
105         struct playlist_entry *current;
106         int sample_queue;
107 };
108
109 static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u, 
110         int eivr_events_fd, int eivr_commands_fd, int eivr_errors_fd, 
111         const struct ast_str *args, const struct ast_flags flags);
112
113 int eivr_connect_socket(struct ast_channel *chan, const char *host, int port);
114
115 static void send_eivr_event(FILE *handle, const char event, const char *data,
116         const struct ast_channel *chan)
117 {
118         struct ast_str *tmp = ast_str_create(12);
119
120         ast_str_append(&tmp, 0, "%c,%10d", event, (int)time(NULL));
121         if (data) {
122                 ast_str_append(&tmp, 0, ",%s", data);
123         }
124
125         fprintf(handle, "%s\n", ast_str_buffer(tmp));
126         ast_debug(1, "sent '%s'\n", ast_str_buffer(tmp));
127 }
128
129 static void *gen_alloc(struct ast_channel *chan, void *params)
130 {
131         struct ivr_localuser *u = params;
132         struct gen_state *state;
133
134         if (!(state = ast_calloc(1, sizeof(*state))))
135                 return NULL;
136
137         state->u = u;
138
139         return state;
140 }
141
142 static void gen_closestream(struct gen_state *state)
143 {
144         if (!state->stream)
145                 return;
146
147         ast_closestream(state->stream);
148         state->u->chan->stream = NULL;
149         state->stream = NULL;
150 }
151
152 static void gen_release(struct ast_channel *chan, void *data)
153 {
154         struct gen_state *state = data;
155
156         gen_closestream(state);
157         ast_free(data);
158 }
159
160 /* caller has the playlist locked */
161 static int gen_nextfile(struct gen_state *state)
162 {
163         struct ivr_localuser *u = state->u;
164         char *file_to_stream;
165
166         u->abort_current_sound = 0;
167         u->playing_silence = 0;
168         gen_closestream(state);
169
170         while (!state->stream) {
171                 state->current = AST_LIST_REMOVE_HEAD(&u->playlist, list);
172                 if (state->current) {
173                         file_to_stream = state->current->filename;
174                 } else {
175                         file_to_stream = "silence/10";
176                         u->playing_silence = 1;
177                 }
178
179                 if (!(state->stream = ast_openstream_full(u->chan, file_to_stream, u->chan->language, 1))) {
180                         ast_chan_log(LOG_WARNING, u->chan, "File '%s' could not be opened: %s\n", file_to_stream, strerror(errno));
181                         if (!u->playing_silence) {
182                                 continue;
183                         } else {
184                                 break;
185                         }
186                 }
187         }
188
189         return (!state->stream);
190 }
191
192 static struct ast_frame *gen_readframe(struct gen_state *state)
193 {
194         struct ast_frame *f = NULL;
195         struct ivr_localuser *u = state->u;
196
197         if (u->abort_current_sound ||
198                 (u->playing_silence && AST_LIST_FIRST(&u->playlist))) {
199                 gen_closestream(state);
200                 AST_LIST_LOCK(&u->playlist);
201                 gen_nextfile(state);
202                 AST_LIST_UNLOCK(&u->playlist);
203         }
204
205         if (!(state->stream && (f = ast_readframe(state->stream)))) {
206                 if (state->current) {
207                         AST_LIST_LOCK(&u->finishlist);
208                         AST_LIST_INSERT_TAIL(&u->finishlist, state->current, list);
209                         AST_LIST_UNLOCK(&u->finishlist);
210                         state->current = NULL;
211                 }
212                 if (!gen_nextfile(state))
213                         f = ast_readframe(state->stream);
214         }
215
216         return f;
217 }
218
219 static int gen_generate(struct ast_channel *chan, void *data, int len, int samples)
220 {
221         struct gen_state *state = data;
222         struct ast_frame *f = NULL;
223         int res = 0;
224
225         state->sample_queue += samples;
226
227         while (state->sample_queue > 0) {
228                 if (!(f = gen_readframe(state)))
229                         return -1;
230
231                 res = ast_write(chan, f);
232                 ast_frfree(f);
233                 if (res < 0) {
234                         ast_chan_log(LOG_WARNING, chan, "Failed to write frame: %s\n", strerror(errno));
235                         return -1;
236                 }
237                 state->sample_queue -= f->samples;
238         }
239
240         return res;
241 }
242
243 static struct ast_generator gen =
244 {
245         alloc: gen_alloc,
246         release: gen_release,
247         generate: gen_generate,
248 };
249
250 static void ast_eivr_getvariable(struct ast_channel *chan, char *data, char *outbuf, int outbuflen)
251 {
252         /* original input data: "G,var1,var2," */
253         /* data passed as "data":  "var1,var2" */
254
255         char *inbuf, *variable;
256         const char *value;
257         int j;
258         struct ast_str *newstring = ast_str_alloca(outbuflen); 
259
260         outbuf[0] = '\0';
261
262         for (j = 1, inbuf = data; ; j++) {
263                 variable = strsep(&inbuf, ",");
264                 if (variable == NULL) {
265                         int outstrlen = strlen(outbuf);
266                         if (outstrlen && outbuf[outstrlen - 1] == ',') {
267                                 outbuf[outstrlen - 1] = 0;
268                         }
269                         break;
270                 }
271                 
272                 ast_channel_lock(chan);
273                 if (!(value = pbx_builtin_getvar_helper(chan, variable))) {
274                         value = "";
275                 }
276
277                 ast_str_append(&newstring, 0, "%s=%s,", variable, value);
278                 ast_channel_unlock(chan);
279                 ast_copy_string(outbuf, ast_str_buffer(newstring), outbuflen);
280         }
281 }
282
283 static void ast_eivr_setvariable(struct ast_channel *chan, char *data)
284 {
285         char buf[1024];
286         char *value;
287
288         char *inbuf, *variable;
289
290         int j;
291
292         for (j = 1, inbuf = data; ; j++, inbuf = NULL) {
293                 variable = strsep(&inbuf, ",");
294                 ast_debug(1, "Setting up a variable: %s\n", variable);
295                 if (variable) {
296                         /* variable contains "varname=value" */
297                         ast_copy_string(buf, variable, sizeof(buf));
298                         value = strchr(buf, '=');
299                         if (!value) {
300                                 value = "";
301                         } else {
302                                 *value++ = '\0';
303                         }
304                         pbx_builtin_setvar_helper(chan, buf, value);
305                 } else {
306                         break;
307                 }
308         }
309 }
310
311 static struct playlist_entry *make_entry(const char *filename)
312 {
313         struct playlist_entry *entry;
314
315         if (!(entry = ast_calloc(1, sizeof(*entry) + strlen(filename) + 10))) /* XXX why 10 ? */
316                 return NULL;
317
318         strcpy(entry->filename, filename);
319
320         return entry;
321 }
322
323 static int app_exec(struct ast_channel *chan, void *data)
324 {
325         struct ast_flags flags = { 0, };
326         char *opts[0];
327         struct playlist_entry *entry;
328         int child_stdin[2] = { 0, 0 };
329         int child_stdout[2] = { 0, 0 };
330         int child_stderr[2] = { 0, 0 };
331         int res = -1;
332         int pid;
333
334         char hostname[1024];
335         char *port_str = NULL;
336         int port = 0;
337         struct ast_tcptls_session_instance *ser = NULL;
338
339         struct ivr_localuser foo = {
340                 .playlist = AST_LIST_HEAD_INIT_VALUE,
341                 .finishlist = AST_LIST_HEAD_INIT_VALUE,
342                 .gen_active = 0,
343         };
344         struct ivr_localuser *u = &foo;
345
346         char *buf;
347         int j;
348         char *s, **app_args, *e; 
349         struct ast_str *pipe_delim_args = ast_str_create(100);
350
351         AST_DECLARE_APP_ARGS(eivr_args,
352                 AST_APP_ARG(cmd)[32];
353         );
354         AST_DECLARE_APP_ARGS(application_args,
355                 AST_APP_ARG(cmd)[32];
356         );
357
358         u->abort_current_sound = 0;
359         u->chan = chan;
360
361         buf = ast_strdupa(data);
362         AST_STANDARD_APP_ARGS(eivr_args, buf);
363
364         if ((s = strchr(eivr_args.cmd[0], '('))) {
365                 s[0] = ',';
366                 if (( e = strrchr(s, ')')) ) {
367                         *e = '\0';
368                 } else {
369                         ast_log(LOG_ERROR, "Parse error, no closing paren?\n");
370                 }
371                 AST_STANDARD_APP_ARGS(application_args, eivr_args.cmd[0]);
372                 app_args = application_args.argv;
373
374                 /* Put the application + the arguments in a | delimited list */
375                 ast_str_reset(pipe_delim_args);
376                 for (j = 0; application_args.cmd[j] != NULL; j++) {
377                         ast_str_append(&pipe_delim_args, 0, "%s%s", j == 0 ? "" : ",", application_args.cmd[j]);
378                 }
379
380                 /* Parse the ExternalIVR() arguments */
381                 if (option_debug)
382                         ast_debug(1, "Parsing options from: [%s]\n", eivr_args.cmd[1]);
383                 ast_app_parse_options(app_opts, &flags, opts, eivr_args.cmd[1]);
384                 if (option_debug) {
385                         if (ast_test_flag(&flags, noanswer))
386                                 ast_debug(1, "noanswer is set\n");
387                         if (ast_test_flag(&flags, ignore_hangup))
388                                 ast_debug(1, "ignore_hangup is set\n");
389                         if (ast_test_flag(&flags, run_dead))
390                                 ast_debug(1, "run_dead is set\n");
391                 }
392
393         } else {
394                 app_args = eivr_args.argv;
395                 for (j = 0; eivr_args.cmd[j] != NULL; j++) {
396                         ast_str_append(&pipe_delim_args, 0, "%s%s", j == 0 ? "" : "|", eivr_args.cmd[j]);
397                 }
398         }
399         
400         if (ast_strlen_zero(data)) {
401                 ast_log(LOG_WARNING, "ExternalIVR requires a command to execute\n");
402                 return -1;
403         }
404
405         if (!(ast_test_flag(&flags, noanswer))) {
406                 ast_chan_log(LOG_WARNING, chan, "Answering channel and starting generator\n");
407                 if (chan->_state != AST_STATE_UP) {
408                         if (ast_test_flag(&flags, run_dead)) {
409                                 ast_chan_log(LOG_WARNING, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n");
410                                 goto exit;
411                         }
412                         ast_answer(chan);
413                 }
414                 if (ast_activate_generator(chan, &gen, u) < 0) {
415                         ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n");
416                         goto exit;
417                 } else {
418                         u->gen_active = 1;
419                 }
420         }
421
422         if (!strncmp(app_args[0], "ivr://", 6)) {
423                 struct ast_tcptls_session_args ivr_desc = {
424                         .accept_fd = -1,
425                         .name = "IVR",
426                 };
427                 struct ast_hostent hp;
428
429                 /*communicate through socket to server*/
430                 ast_debug(1, "Parsing hostname:port for socket connect from \"%s\"\n", app_args[0]);
431                 ast_copy_string(hostname, app_args[0] + 6, sizeof(hostname));
432                 if ((port_str = strchr(hostname, ':')) != NULL) {
433                         port_str[0] = 0;
434                         port_str += 1;
435                         port = atoi(port_str);
436                 }
437                 if (!port) {
438                         port = 2949;  /* default port, if one is not provided */
439                 }
440
441                 ast_gethostbyname(hostname, &hp);
442                 ivr_desc.local_address.sin_family = AF_INET;
443                 ivr_desc.local_address.sin_port = htons(port);
444                 memcpy(&ivr_desc.local_address.sin_addr.s_addr, hp.hp.h_addr, hp.hp.h_length);
445                 ser = ast_tcptls_client_start(&ivr_desc);
446
447                 if (!ser) {
448                         goto exit;
449                 }
450                 res = eivr_comm(chan, u, ser->fd, ser->fd, -1, pipe_delim_args, flags);
451
452         } else {
453         
454                 if (pipe(child_stdin)) {
455                         ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child input: %s\n", strerror(errno));
456                         goto exit;
457                 }
458                 if (pipe(child_stdout)) {
459                         ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child output: %s\n", strerror(errno));
460                         goto exit;
461                 }
462                 if (pipe(child_stderr)) {
463                         ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child errors: %s\n", strerror(errno));
464                         goto exit;
465                 }
466         
467                 pid = ast_safe_fork(0);
468                 if (pid < 0) {
469                         ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno));
470                         goto exit;
471                 }
472         
473                 if (!pid) {
474                         /* child process */
475                         if (ast_opt_high_priority)
476                                 ast_set_priority(0);
477         
478                         dup2(child_stdin[0], STDIN_FILENO);
479                         dup2(child_stdout[1], STDOUT_FILENO);
480                         dup2(child_stderr[1], STDERR_FILENO);
481                         ast_close_fds_above_n(STDERR_FILENO);
482                         execv(app_args[0], app_args);
483                         fprintf(stderr, "Failed to execute '%s': %s\n", app_args[0], strerror(errno));
484                         _exit(1);
485                 } else {
486                         /* parent process */
487                         close(child_stdin[0]);
488                         child_stdin[0] = 0;
489                         close(child_stdout[1]);
490                         child_stdout[1] = 0;
491                         close(child_stderr[1]);
492                         child_stderr[1] = 0;
493                         res = eivr_comm(chan, u, child_stdin[1], child_stdout[0], child_stderr[0], pipe_delim_args, flags);
494                 }
495         }
496
497         exit:
498         if (u->gen_active)
499                 ast_deactivate_generator(chan);
500
501         if (child_stdin[0])
502                 close(child_stdin[0]);
503
504         if (child_stdin[1])
505                 close(child_stdin[1]);
506
507         if (child_stdout[0])
508                 close(child_stdout[0]);
509
510         if (child_stdout[1])
511                 close(child_stdout[1]);
512
513         if (child_stderr[0])
514                 close(child_stderr[0]);
515
516         if (child_stderr[1])
517                 close(child_stderr[1]);
518         if (ser) {
519                 ao2_ref(ser, -1);
520         }
521         while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list)))
522                 ast_free(entry);
523
524         return res;
525 }
526
527 static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u, 
528                                 int eivr_events_fd, int eivr_commands_fd, int eivr_errors_fd, 
529                                 const struct ast_str *args, const struct ast_flags flags)
530 {
531         struct playlist_entry *entry;
532         struct ast_frame *f;
533         int ms;
534         int exception;
535         int ready_fd;
536         int waitfds[2] = { eivr_commands_fd, eivr_errors_fd };
537         struct ast_channel *rchan;
538         char *command;
539         int res = -1;
540         int test_available_fd = -1;
541         int hangup_info_sent = 0;
542   
543         FILE *eivr_commands = NULL;
544         FILE *eivr_errors = NULL;
545         FILE *eivr_events = NULL;
546
547         if (!(eivr_events = fdopen(eivr_events_fd, "w"))) {
548                 ast_chan_log(LOG_WARNING, chan, "Could not open stream to send events\n");
549                 goto exit;
550         }
551         if (!(eivr_commands = fdopen(eivr_commands_fd, "r"))) {
552                 ast_chan_log(LOG_WARNING, chan, "Could not open stream to receive commands\n");
553                 goto exit;
554         }
555         if (eivr_errors_fd > -1) {  /* if opening a socket connection, error stream will not be used */
556                 if (!(eivr_errors = fdopen(eivr_errors_fd, "r"))) {
557                         ast_chan_log(LOG_WARNING, chan, "Could not open stream to receive errors\n");
558                         goto exit;
559                 }
560         }
561
562         test_available_fd = open("/dev/null", O_RDONLY);
563  
564         setvbuf(eivr_events, NULL, _IONBF, 0);
565         setvbuf(eivr_commands, NULL, _IONBF, 0);
566         if (eivr_errors) {
567                 setvbuf(eivr_errors, NULL, _IONBF, 0);
568         }
569
570         res = 0;
571  
572         while (1) {
573                 if (ast_test_flag(chan, AST_FLAG_ZOMBIE)) {
574                         ast_chan_log(LOG_NOTICE, chan, "Is a zombie\n");
575                         res = -1;
576                         break;
577                 }
578                 if (!hangup_info_sent && !(ast_test_flag(&flags, run_dead)) && ast_check_hangup(chan)) {
579                         if (ast_test_flag(&flags, ignore_hangup)) {
580                                 ast_chan_log(LOG_NOTICE, chan, "Got check_hangup, but ignore_hangup set so sending 'I' command\n");
581                                 send_eivr_event(eivr_events, 'I', "HANGUP", chan);
582                                 hangup_info_sent = 1;
583                         } else {
584                                 ast_chan_log(LOG_NOTICE, chan, "Got check_hangup\n");
585                                 send_eivr_event(eivr_events, 'H', NULL, chan);
586                                 res = -1;
587                                 break;
588                         }
589                 }
590  
591                 ready_fd = 0;
592                 ms = 100;
593                 errno = 0;
594                 exception = 0;
595  
596                 rchan = ast_waitfor_nandfds(&chan, 1, waitfds, (eivr_errors_fd < 0) ? 1 : 2, &exception, &ready_fd, &ms);
597  
598                 if (chan->_state == AST_STATE_UP && !AST_LIST_EMPTY(&u->finishlist)) {
599                         AST_LIST_LOCK(&u->finishlist);
600                         while ((entry = AST_LIST_REMOVE_HEAD(&u->finishlist, list))) {
601                                 send_eivr_event(eivr_events, 'F', entry->filename, chan);
602                                 ast_free(entry);
603                         }
604                         AST_LIST_UNLOCK(&u->finishlist);
605                 }
606  
607                 if (chan->_state == AST_STATE_UP && !(ast_check_hangup(chan)) && rchan) {
608                         /* the channel has something */
609                         f = ast_read(chan);
610                         if (!f) {
611                                 ast_chan_log(LOG_NOTICE, chan, "Returned no frame\n");
612                                 send_eivr_event(eivr_events, 'H', NULL, chan);
613                                 res = -1;
614                                 break;
615                         }
616                         if (f->frametype == AST_FRAME_DTMF) {
617                                 send_eivr_event(eivr_events, f->subclass, NULL, chan);
618                                 if (u->option_autoclear) {
619                                         if (!u->abort_current_sound && !u->playing_silence)
620                                                 send_eivr_event(eivr_events, 'T', NULL, chan);
621                                         AST_LIST_LOCK(&u->playlist);
622                                         while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
623                                                 send_eivr_event(eivr_events, 'D', entry->filename, chan);
624                                                 ast_free(entry);
625                                         }
626                                         if (!u->playing_silence)
627                                                 u->abort_current_sound = 1;
628                                         AST_LIST_UNLOCK(&u->playlist);
629                                 }
630                         } else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) {
631                                 ast_chan_log(LOG_NOTICE, chan, "Got AST_CONTROL_HANGUP\n");
632                                 send_eivr_event(eivr_events, 'H', NULL, chan);
633                                 if (f->data.uint32) {
634                                         chan->hangupcause = f->data.uint32;
635                                 }
636                                 ast_frfree(f);
637                                 res = -1;
638                                 break;
639                         }
640                         ast_frfree(f);
641                 } else if (ready_fd == eivr_commands_fd) {
642                         char input[1024];
643  
644                         if (exception || (dup2(eivr_commands_fd, test_available_fd) == -1) || feof(eivr_commands)) {
645                                 ast_chan_log(LOG_WARNING, chan, "Child process went away\n");
646                                 res = -1;
647                                 break;
648                         }
649   
650                         if (!fgets(input, sizeof(input), eivr_commands))
651                                 continue;
652  
653                         command = ast_strip(input);
654   
655                         if (option_debug)
656                                 ast_debug(1, "got command '%s'\n", input);
657   
658                         if (strlen(input) < 4)
659                                 continue;
660   
661                         if (input[0] == 'P') {
662                                 struct ast_str *tmp = (struct ast_str *) args;
663                                 send_eivr_event(eivr_events, 'P', ast_str_buffer(tmp), chan);
664                         } else if ( input[0] == 'T' ) {
665                                 ast_chan_log(LOG_WARNING, chan, "Answering channel if needed and starting generator\n");
666                                 if (chan->_state != AST_STATE_UP) {
667                                         if (ast_test_flag(&flags, run_dead)) {
668                                                 ast_chan_log(LOG_WARNING, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n");
669                                                 send_eivr_event(eivr_events, 'Z', "ANSWER_FAILURE", chan);
670                                                 continue;
671                                         }
672                                         ast_answer(chan);
673                                 }
674                                 if (!(u->gen_active)) {
675                                         if (ast_activate_generator(chan, &gen, u) < 0) {
676                                                 ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n");
677                                                 send_eivr_event(eivr_events, 'Z', "GENERATOR_FAILURE", chan);
678                                         } else {
679                                                 u->gen_active = 1;
680                                         }
681                                 }
682                         } else if (input[0] == 'S') {
683                                 if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) {
684                                         ast_chan_log(LOG_WARNING, chan, "Queue 'S'et called on unanswered channel\n");
685                                         send_eivr_event(eivr_events, 'Z', NULL, chan);
686                                         continue;
687                                 }
688                                 if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) {
689                                         ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
690                                         send_eivr_event(eivr_events, 'Z', NULL, chan);
691                                         strcpy(&input[2], "exception");
692                                 }
693                                 if (!u->abort_current_sound && !u->playing_silence)
694                                         send_eivr_event(eivr_events, 'T', NULL, chan);
695                                 AST_LIST_LOCK(&u->playlist);
696                                 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
697                                         send_eivr_event(eivr_events, 'D', entry->filename, chan);
698                                         ast_free(entry);
699                                 }
700                                 if (!u->playing_silence)
701                                         u->abort_current_sound = 1;
702                                 entry = make_entry(&input[2]);
703                                 if (entry)
704                                         AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
705                                 AST_LIST_UNLOCK(&u->playlist);
706                         } else if (input[0] == 'A') {
707                                 if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) {
708                                         ast_chan_log(LOG_WARNING, chan, "Queue 'A'ppend called on unanswered channel\n");
709                                         send_eivr_event(eivr_events, 'Z', NULL, chan);
710                                         continue;
711                                 }
712                                 if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) {
713                                         ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
714                                         send_eivr_event(eivr_events, 'Z', NULL, chan);
715                                         strcpy(&input[2], "exception");
716                                 }
717                                 entry = make_entry(&input[2]);
718                                 if (entry) {
719                                         AST_LIST_LOCK(&u->playlist);
720                                         AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
721                                         AST_LIST_UNLOCK(&u->playlist);
722                                 }
723                         } else if (input[0] == 'G') {
724                                 /* A get variable message:  "G,variable1,variable2,..." */
725                                 char response[2048];
726
727                                 ast_chan_log(LOG_NOTICE, chan, "Getting a Variable out of the channel: %s\n", &input[2]);
728                                 ast_eivr_getvariable(chan, &input[2], response, sizeof(response));
729                                 send_eivr_event(eivr_events, 'G', response, chan);
730                         } else if (input[0] == 'V') {
731                                 /* A set variable message:  "V,variablename=foo" */
732                                 ast_chan_log(LOG_NOTICE, chan, "Setting a Variable up: %s\n", &input[2]);
733                                 ast_eivr_setvariable(chan, &input[2]);
734                         } else if (input[0] == 'L') {
735                                 ast_chan_log(LOG_NOTICE, chan, "Log message from EIVR: %s\n", &input[2]);
736                         } else if (input[0] == 'X') {
737                                 ast_chan_log(LOG_NOTICE, chan, "Exiting ExternalIVR: %s\n", &input[2]);
738                                 /*! \todo add deprecation debug message for X command here */
739                                 res = 0;
740                                 break;
741                         } else if (input[0] == 'E') {
742                                 ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]);
743                                 send_eivr_event(eivr_events, 'E', NULL, chan);
744                                 res = 0;
745                                 break;
746                         } else if (input[0] == 'H') {
747                                 ast_chan_log(LOG_NOTICE, chan, "Hanging up: %s\n", &input[2]);
748                                 send_eivr_event(eivr_events, 'H', NULL, chan);
749                                 res = -1;
750                                 break;
751                         } else if (input[0] == 'O') {
752                                 if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) {
753                                         ast_chan_log(LOG_WARNING, chan, "Option called on unanswered channel\n");
754                                         send_eivr_event(eivr_events, 'Z', NULL, chan);
755                                         continue;
756                                 }
757                                 if (!strcasecmp(&input[2], "autoclear"))
758                                         u->option_autoclear = 1;
759                                 else if (!strcasecmp(&input[2], "noautoclear"))
760                                         u->option_autoclear = 0;
761                                 else
762                                         ast_chan_log(LOG_WARNING, chan, "Unknown option requested '%s'\n", &input[2]);
763                         }
764                 } else if (eivr_errors_fd && ready_fd == eivr_errors_fd) {
765                         char input[1024];
766   
767                         if (exception || feof(eivr_errors)) {
768                                 ast_chan_log(LOG_WARNING, chan, "Child process went away\n");
769                                 res = -1;
770                                 break;
771                         }
772                         if (fgets(input, sizeof(input), eivr_errors)) {
773                                 command = ast_strip(input);
774                                 ast_chan_log(LOG_NOTICE, chan, "stderr: %s\n", command);
775                         }
776                 } else if ((ready_fd < 0) && ms) { 
777                         if (errno == 0 || errno == EINTR)
778                                 continue;
779  
780                         ast_chan_log(LOG_WARNING, chan, "Wait failed (%s)\n", strerror(errno));
781                         break;
782                 }
783         }
784   
785  
786 exit:
787  
788         if (test_available_fd > -1) {
789                 close(test_available_fd);
790         }
791
792         if (eivr_events)
793                 fclose(eivr_events);
794  
795         if (eivr_commands)
796                 fclose(eivr_commands);
797
798         if (eivr_errors)
799                 fclose(eivr_errors);
800   
801         return res;
802  
803 }
804
805 static int unload_module(void)
806 {
807         return ast_unregister_application(app);
808 }
809
810 static int load_module(void)
811 {
812         return ast_register_application(app, app_exec, synopsis, descrip);
813 }
814
815 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "External IVR Interface Application");