Blocked revisions 117809 via svnmerge
[asterisk-bristuff.git] / apps / app_externalivr.c
blob7d802579a540cc50d3a99988cd59c88610a36c65
1 /*
2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2005, Digium, Inc.
6 * Kevin P. Fleming <kpfleming@digium.com>
8 * Portions taken from the file-based music-on-hold work
9 * created by Anthony Minessale II in res_musiconhold.c
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.
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.
22 /*! \file
24 * \brief External IVR application interface
26 * \author Kevin P. Fleming <kpfleming@digium.com>
28 * \note Portions taken from the file-based music-on-hold work
29 * created by Anthony Minessale II in res_musiconhold.c
31 * \ingroup applications
34 #include "asterisk.h"
36 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
38 #include <signal.h>
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"
50 static const char *app = "ExternalIVR";
52 static const char *synopsis = "Interfaces with an external IVR application";
53 static const char *descrip =
54 " ExternalIVR(command|ivr://ivrhosti([,arg[,arg...]])[,options]): Either forks a process\n"
55 "to run given command or makes a socket to connect to given host and starts\n"
56 "a generator on the channel. The generator's play list is controlled by the\n"
57 "external application, which can add and clear entries via simple commands\n"
58 "issued over its stdout. The external application will receive all DTMF events\n"
59 "received on the channel, and notification if the channel is hung up. The\n"
60 "application will not be forcibly terminated when the channel is hung up.\n"
61 "See doc/externalivr.txt for a protocol specification.\n"
62 "The 'n' option tells ExternalIVR() not to answer the channel. \n"
63 "The 'i' option tells ExternalIVR() not to send a hangup and exit when the\n"
64 " channel receives a hangup, instead it sends an 'I' informative message\n"
65 " meaning that the external application MUST hang up the call with an H command\n"
66 "The 'd' option tells ExternalIVR() to run on a channel that has been hung up\n"
67 " and will not look for hangups. The external application must exit with\n"
68 " an 'E' command.\n";
70 /* XXX the parser in gcc 2.95 gets confused if you don't put a space between 'name' and the comma */
71 #define ast_chan_log(level, channel, format, ...) ast_log(level, "%s: " format, channel->name , ## __VA_ARGS__)
73 enum {
74 noanswer = (1 << 0),
75 ignore_hangup = (1 << 1),
76 run_dead = (1 << 2),
77 } options_flags;
79 AST_APP_OPTIONS(app_opts, {
80 AST_APP_OPTION('n', noanswer),
81 AST_APP_OPTION('i', ignore_hangup),
82 AST_APP_OPTION('d', run_dead),
83 });
85 struct playlist_entry {
86 AST_LIST_ENTRY(playlist_entry) list;
87 char filename[1];
90 struct ivr_localuser {
91 struct ast_channel *chan;
92 AST_LIST_HEAD(playlist, playlist_entry) playlist;
93 AST_LIST_HEAD(finishlist, playlist_entry) finishlist;
94 int abort_current_sound;
95 int playing_silence;
96 int option_autoclear;
97 int gen_active;
101 struct gen_state {
102 struct ivr_localuser *u;
103 struct ast_filestream *stream;
104 struct playlist_entry *current;
105 int sample_queue;
108 static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u,
109 int eivr_events_fd, int eivr_commands_fd, int eivr_errors_fd,
110 const struct ast_str *args, const struct ast_flags flags);
112 int eivr_connect_socket(struct ast_channel *chan, const char *host, int port);
114 static void send_eivr_event(FILE *handle, const char event, const char *data,
115 const struct ast_channel *chan)
117 struct ast_str *tmp = ast_str_create(12);
119 ast_str_append(&tmp, 0, "%c,%10d", event, (int)time(NULL));
120 if (data) {
121 ast_str_append(&tmp, 0, "%s", data);
124 fprintf(handle, "%s\n", tmp->str);
125 ast_debug(1, "sent '%s'\n", tmp->str);
128 static void *gen_alloc(struct ast_channel *chan, void *params)
130 struct ivr_localuser *u = params;
131 struct gen_state *state;
133 if (!(state = ast_calloc(1, sizeof(*state))))
134 return NULL;
136 state->u = u;
138 return state;
141 static void gen_closestream(struct gen_state *state)
143 if (!state->stream)
144 return;
146 ast_closestream(state->stream);
147 state->u->chan->stream = NULL;
148 state->stream = NULL;
151 static void gen_release(struct ast_channel *chan, void *data)
153 struct gen_state *state = data;
155 gen_closestream(state);
156 ast_free(data);
159 /* caller has the playlist locked */
160 static int gen_nextfile(struct gen_state *state)
162 struct ivr_localuser *u = state->u;
163 char *file_to_stream;
165 u->abort_current_sound = 0;
166 u->playing_silence = 0;
167 gen_closestream(state);
169 while (!state->stream) {
170 state->current = AST_LIST_REMOVE_HEAD(&u->playlist, list);
171 if (state->current) {
172 file_to_stream = state->current->filename;
173 } else {
174 file_to_stream = "silence/10";
175 u->playing_silence = 1;
178 if (!(state->stream = ast_openstream_full(u->chan, file_to_stream, u->chan->language, 1))) {
179 ast_chan_log(LOG_WARNING, u->chan, "File '%s' could not be opened: %s\n", file_to_stream, strerror(errno));
180 if (!u->playing_silence) {
181 continue;
182 } else {
183 break;
188 return (!state->stream);
191 static struct ast_frame *gen_readframe(struct gen_state *state)
193 struct ast_frame *f = NULL;
194 struct ivr_localuser *u = state->u;
196 if (u->abort_current_sound ||
197 (u->playing_silence && AST_LIST_FIRST(&u->playlist))) {
198 gen_closestream(state);
199 AST_LIST_LOCK(&u->playlist);
200 gen_nextfile(state);
201 AST_LIST_UNLOCK(&u->playlist);
204 if (!(state->stream && (f = ast_readframe(state->stream)))) {
205 if (state->current) {
206 AST_LIST_LOCK(&u->finishlist);
207 AST_LIST_INSERT_TAIL(&u->finishlist, state->current, list);
208 AST_LIST_UNLOCK(&u->finishlist);
209 state->current = NULL;
211 if (!gen_nextfile(state))
212 f = ast_readframe(state->stream);
215 return f;
218 static int gen_generate(struct ast_channel *chan, void *data, int len, int samples)
220 struct gen_state *state = data;
221 struct ast_frame *f = NULL;
222 int res = 0;
224 state->sample_queue += samples;
226 while (state->sample_queue > 0) {
227 if (!(f = gen_readframe(state)))
228 return -1;
230 res = ast_write(chan, f);
231 ast_frfree(f);
232 if (res < 0) {
233 ast_chan_log(LOG_WARNING, chan, "Failed to write frame: %s\n", strerror(errno));
234 return -1;
236 state->sample_queue -= f->samples;
239 return res;
242 static struct ast_generator gen =
244 alloc: gen_alloc,
245 release: gen_release,
246 generate: gen_generate,
249 static void ast_eivr_getvariable(struct ast_channel *chan, char *data, char *outbuf, int outbuflen)
251 /* original input data: "G,var1,var2," */
252 /* data passed as "data": "var1,var2" */
254 char *inbuf, *variable;
255 const char *value;
256 int j;
257 struct ast_str *newstring = ast_str_alloca(outbuflen);
259 outbuf[0] = '\0';
261 for (j = 1, inbuf = data; ; j++) {
262 variable = strsep(&inbuf, ",");
263 if (variable == NULL) {
264 int outstrlen = strlen(outbuf);
265 if (outstrlen && outbuf[outstrlen - 1] == ',') {
266 outbuf[outstrlen - 1] = 0;
268 break;
271 ast_channel_lock(chan);
272 if (!(value = pbx_builtin_getvar_helper(chan, variable))) {
273 value = "";
276 ast_str_append(&newstring, 0, "%s=%s,", variable, value);
277 ast_channel_unlock(chan);
278 ast_copy_string(outbuf, newstring->str, outbuflen);
282 static void ast_eivr_setvariable(struct ast_channel *chan, char *data)
284 char buf[1024];
285 char *value;
287 char *inbuf, *variable;
289 int j;
291 for (j = 1, inbuf = data; ; j++, inbuf = NULL) {
292 variable = strsep(&inbuf, ",");
293 ast_debug(1, "Setting up a variable: %s\n", variable);
294 if (variable) {
295 /* variable contains "varname=value" */
296 ast_copy_string(buf, variable, sizeof(buf));
297 value = strchr(buf, '=');
298 if (!value) {
299 value = "";
300 } else {
301 *value++ = '\0';
303 pbx_builtin_setvar_helper(chan, buf, value);
304 } else {
305 break;
310 static struct playlist_entry *make_entry(const char *filename)
312 struct playlist_entry *entry;
314 if (!(entry = ast_calloc(1, sizeof(*entry) + strlen(filename) + 10))) /* XXX why 10 ? */
315 return NULL;
317 strcpy(entry->filename, filename);
319 return entry;
322 static int app_exec(struct ast_channel *chan, void *data)
324 struct ast_flags flags;
325 char *opts[0];
326 struct playlist_entry *entry;
327 int child_stdin[2] = { 0, 0 };
328 int child_stdout[2] = { 0, 0 };
329 int child_stderr[2] = { 0, 0 };
330 int res = -1;
331 int pid;
333 char hostname[1024];
334 char *port_str = NULL;
335 int port = 0;
336 struct ast_tcptls_session_instance *ser = NULL;
338 struct ivr_localuser foo = {
339 .playlist = AST_LIST_HEAD_INIT_VALUE,
340 .finishlist = AST_LIST_HEAD_INIT_VALUE,
341 .gen_active = 0,
343 struct ivr_localuser *u = &foo;
345 char *buf;
346 int j;
347 char *s, **app_args, *e;
348 struct ast_str *pipe_delim_args = ast_str_create(100);
350 AST_DECLARE_APP_ARGS(eivr_args,
351 AST_APP_ARG(cmd)[32];
353 AST_DECLARE_APP_ARGS(application_args,
354 AST_APP_ARG(cmd)[32];
357 u->abort_current_sound = 0;
358 u->chan = chan;
360 buf = ast_strdupa(data);
361 AST_STANDARD_APP_ARGS(eivr_args, buf);
363 if ((s = strchr(eivr_args.cmd[0], '('))) {
364 s[0] = ',';
365 if (( e = strrchr(s, ')')) ) {
366 *e = '\0';
367 } else {
368 ast_log(LOG_ERROR, "Parse error, no closing paren?\n");
370 AST_STANDARD_APP_ARGS(application_args, eivr_args.cmd[0]);
371 app_args = application_args.argv;
373 /* Put the application + the arguments in a | delimited list */
374 ast_str_reset(pipe_delim_args);
375 for (j = 0; application_args.cmd[j] != NULL; j++) {
376 ast_str_append(&pipe_delim_args, 0, "%s%s", j == 0 ? "" : "|", application_args.cmd[j]);
379 /* Parse the ExternalIVR() arguments */
380 if (option_debug)
381 ast_debug(1, "Parsing options from: [%s]\n", eivr_args.cmd[1]);
382 ast_app_parse_options(app_opts, &flags, opts, eivr_args.cmd[1]);
383 if (option_debug) {
384 if (ast_test_flag(&flags, noanswer))
385 ast_debug(1, "noanswer is set\n");
386 if (ast_test_flag(&flags, ignore_hangup))
387 ast_debug(1, "ignore_hangup is set\n");
388 if (ast_test_flag(&flags, run_dead))
389 ast_debug(1, "run_dead is set\n");
392 } else {
393 app_args = eivr_args.argv;
394 for (j = 0; eivr_args.cmd[j] != NULL; j++) {
395 ast_str_append(&pipe_delim_args, 0, "%s%s", j == 0 ? "" : "|", eivr_args.cmd[j]);
399 if (ast_strlen_zero(data)) {
400 ast_log(LOG_WARNING, "ExternalIVR requires a command to execute\n");
401 return -1;
404 if (!(ast_test_flag(&flags, noanswer))) {
405 ast_chan_log(LOG_WARNING, chan, "Answering channel and starting generator\n");
406 if (chan->_state != AST_STATE_UP) {
407 if (ast_test_flag(&flags, run_dead)) {
408 ast_chan_log(LOG_WARNING, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n");
409 goto exit;
411 ast_answer(chan);
413 if (ast_activate_generator(chan, &gen, u) < 0) {
414 ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n");
415 goto exit;
416 } else {
417 u->gen_active = 1;
421 if (!strncmp(app_args[0], "ivr://", 6)) {
422 struct server_args ivr_desc = {
423 .accept_fd = -1,
424 .name = "IVR",
426 struct ast_hostent hp;
428 /*communicate through socket to server*/
429 ast_debug(1, "Parsing hostname:port for socket connect from \"%s\"\n", app_args[0]);
430 ast_copy_string(hostname, app_args[0] + 6, sizeof(hostname));
431 if ((port_str = strchr(hostname, ':')) != NULL) {
432 port_str[0] = 0;
433 port_str += 1;
434 port = atoi(port_str);
436 if (!port) {
437 port = 2949; /* default port, if one is not provided */
440 ast_gethostbyname(hostname, &hp);
441 ivr_desc.sin.sin_family = AF_INET;
442 ivr_desc.sin.sin_port = htons(port);
443 memmove(&ivr_desc.sin.sin_addr.s_addr, hp.hp.h_addr, hp.hp.h_length);
444 ser = ast_tcptls_client_start(&ivr_desc);
446 if (!ser) {
447 goto exit;
449 res = eivr_comm(chan, u, ser->fd, ser->fd, -1, pipe_delim_args, flags);
451 } else {
453 if (pipe(child_stdin)) {
454 ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child input: %s\n", strerror(errno));
455 goto exit;
457 if (pipe(child_stdout)) {
458 ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child output: %s\n", strerror(errno));
459 goto exit;
461 if (pipe(child_stderr)) {
462 ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child errors: %s\n", strerror(errno));
463 goto exit;
466 pid = ast_safe_fork(0);
467 if (pid < 0) {
468 ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno));
469 goto exit;
472 if (!pid) {
473 /* child process */
474 if (ast_opt_high_priority)
475 ast_set_priority(0);
477 dup2(child_stdin[0], STDIN_FILENO);
478 dup2(child_stdout[1], STDOUT_FILENO);
479 dup2(child_stderr[1], STDERR_FILENO);
480 ast_close_fds_above_n(STDERR_FILENO);
481 execv(app_args[0], app_args);
482 fprintf(stderr, "Failed to execute '%s': %s\n", app_args[0], strerror(errno));
483 _exit(1);
484 } else {
485 /* parent process */
486 close(child_stdin[0]);
487 child_stdin[0] = 0;
488 close(child_stdout[1]);
489 child_stdout[1] = 0;
490 close(child_stderr[1]);
491 child_stderr[1] = 0;
492 res = eivr_comm(chan, u, child_stdin[1], child_stdout[0], child_stderr[0], pipe_delim_args, flags);
496 exit:
497 if (u->gen_active)
498 ast_deactivate_generator(chan);
500 if (child_stdin[0])
501 close(child_stdin[0]);
503 if (child_stdin[1])
504 close(child_stdin[1]);
506 if (child_stdout[0])
507 close(child_stdout[0]);
509 if (child_stdout[1])
510 close(child_stdout[1]);
512 if (child_stderr[0])
513 close(child_stderr[0]);
515 if (child_stderr[1])
516 close(child_stderr[1]);
517 if (ser) {
518 fclose(ser->f);
519 ast_tcptls_session_instance_destroy(ser);
521 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list)))
522 ast_free(entry);
524 return res;
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)
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;
543 FILE *eivr_commands = NULL;
544 FILE *eivr_errors = NULL;
545 FILE *eivr_events = NULL;
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;
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;
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;
562 test_available_fd = open("/dev/null", O_RDONLY);
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);
570 res = 0;
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;
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;
591 ready_fd = 0;
592 ms = 100;
593 errno = 0;
594 exception = 0;
596 rchan = ast_waitfor_nandfds(&chan, 1, waitfds, (eivr_errors_fd < 0) ? 1 : 2, &exception, &ready_fd, &ms);
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);
604 AST_LIST_UNLOCK(&u->finishlist);
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;
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);
626 if (!u->playing_silence)
627 u->abort_current_sound = 1;
628 AST_LIST_UNLOCK(&u->playlist);
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;
636 ast_frfree(f);
637 res = -1;
638 break;
640 ast_frfree(f);
641 } else if (ready_fd == eivr_commands_fd) {
642 char input[1024];
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;
650 if (!fgets(input, sizeof(input), eivr_commands))
651 continue;
653 command = ast_strip(input);
655 if (option_debug)
656 ast_debug(1, "got command '%s'\n", input);
658 if (strlen(input) < 4)
659 continue;
661 if (input[0] == 'P') {
662 send_eivr_event(eivr_events, 'P', args->str, chan);
663 } else if ( input[0] == 'T' ) {
664 ast_chan_log(LOG_WARNING, chan, "Answering channel if needed and starting generator\n");
665 if (chan->_state != AST_STATE_UP) {
666 if (ast_test_flag(&flags, run_dead)) {
667 ast_chan_log(LOG_WARNING, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n");
668 send_eivr_event(eivr_events, 'Z', "ANSWER_FAILURE", chan);
669 continue;
671 ast_answer(chan);
673 if (!(u->gen_active)) {
674 if (ast_activate_generator(chan, &gen, u) < 0) {
675 ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n");
676 send_eivr_event(eivr_events, 'Z', "GENERATOR_FAILURE", chan);
677 } else {
678 u->gen_active = 1;
681 } else if (input[0] == 'S') {
682 if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) {
683 ast_chan_log(LOG_WARNING, chan, "Queue 'S'et called on unanswered channel\n");
684 send_eivr_event(eivr_events, 'Z', NULL, chan);
685 continue;
687 if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) {
688 ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
689 send_eivr_event(eivr_events, 'Z', NULL, chan);
690 strcpy(&input[2], "exception");
692 if (!u->abort_current_sound && !u->playing_silence)
693 send_eivr_event(eivr_events, 'T', NULL, chan);
694 AST_LIST_LOCK(&u->playlist);
695 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
696 send_eivr_event(eivr_events, 'D', entry->filename, chan);
697 ast_free(entry);
699 if (!u->playing_silence)
700 u->abort_current_sound = 1;
701 entry = make_entry(&input[2]);
702 if (entry)
703 AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
704 AST_LIST_UNLOCK(&u->playlist);
705 } else if (input[0] == 'A') {
706 if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) {
707 ast_chan_log(LOG_WARNING, chan, "Queue 'A'ppend called on unanswered channel\n");
708 send_eivr_event(eivr_events, 'Z', NULL, chan);
709 continue;
711 if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) {
712 ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
713 send_eivr_event(eivr_events, 'Z', NULL, chan);
714 strcpy(&input[2], "exception");
716 entry = make_entry(&input[2]);
717 if (entry) {
718 AST_LIST_LOCK(&u->playlist);
719 AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
720 AST_LIST_UNLOCK(&u->playlist);
722 } else if (input[0] == 'G') {
723 /* A get variable message: "G,variable1,variable2,..." */
724 char response[2048];
726 ast_chan_log(LOG_NOTICE, chan, "Getting a Variable out of the channel: %s\n", &input[2]);
727 ast_eivr_getvariable(chan, &input[2], response, sizeof(response));
728 send_eivr_event(eivr_events, 'G', response, chan);
729 } else if (input[0] == 'V') {
730 /* A set variable message: "V,variablename=foo" */
731 ast_chan_log(LOG_NOTICE, chan, "Setting a Variable up: %s\n", &input[2]);
732 ast_eivr_setvariable(chan, &input[2]);
733 } else if (input[0] == 'L') {
734 ast_chan_log(LOG_NOTICE, chan, "Log message from EIVR: %s\n", &input[2]);
735 } else if (input[0] == 'X') {
736 ast_chan_log(LOG_NOTICE, chan, "Exiting ExternalIVR: %s\n", &input[2]);
737 /*! \todo add deprecation debug message for X command here */
738 res = 0;
739 break;
740 } else if (input[0] == 'E') {
741 ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]);
742 send_eivr_event(eivr_events, 'E', NULL, chan);
743 res = 0;
744 break;
745 } else if (input[0] == 'H') {
746 ast_chan_log(LOG_NOTICE, chan, "Hanging up: %s\n", &input[2]);
747 send_eivr_event(eivr_events, 'H', NULL, chan);
748 res = -1;
749 break;
750 } else if (input[0] == 'O') {
751 if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) {
752 ast_chan_log(LOG_WARNING, chan, "Option called on unanswered channel\n");
753 send_eivr_event(eivr_events, 'Z', NULL, chan);
754 continue;
756 if (!strcasecmp(&input[2], "autoclear"))
757 u->option_autoclear = 1;
758 else if (!strcasecmp(&input[2], "noautoclear"))
759 u->option_autoclear = 0;
760 else
761 ast_chan_log(LOG_WARNING, chan, "Unknown option requested '%s'\n", &input[2]);
763 } else if (eivr_errors_fd && ready_fd == eivr_errors_fd) {
764 char input[1024];
766 if (exception || feof(eivr_errors)) {
767 ast_chan_log(LOG_WARNING, chan, "Child process went away\n");
768 res = -1;
769 break;
771 if (fgets(input, sizeof(input), eivr_errors)) {
772 command = ast_strip(input);
773 ast_chan_log(LOG_NOTICE, chan, "stderr: %s\n", command);
775 } else if ((ready_fd < 0) && ms) {
776 if (errno == 0 || errno == EINTR)
777 continue;
779 ast_chan_log(LOG_WARNING, chan, "Wait failed (%s)\n", strerror(errno));
780 break;
785 exit:
787 if (test_available_fd > -1) {
788 close(test_available_fd);
791 if (eivr_events)
792 fclose(eivr_events);
794 if (eivr_commands)
795 fclose(eivr_commands);
797 if (eivr_errors)
798 fclose(eivr_errors);
800 return res;
804 static int unload_module(void)
806 return ast_unregister_application(app);
809 static int load_module(void)
811 return ast_register_application(app, app_exec, synopsis, descrip);
814 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "External IVR Interface Application");