Creating tag for the release of asterisk-1.6.1-beta1
[asterisk-bristuff.git] / apps / app_externalivr.c
bloba73a19dfb23c7ecfba6609bb98712751897f7f70
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 ao2_ref(ser, -1);
520 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list)))
521 ast_free(entry);
523 return res;
526 static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u,
527 int eivr_events_fd, int eivr_commands_fd, int eivr_errors_fd,
528 const struct ast_str *args, const struct ast_flags flags)
530 struct playlist_entry *entry;
531 struct ast_frame *f;
532 int ms;
533 int exception;
534 int ready_fd;
535 int waitfds[2] = { eivr_commands_fd, eivr_errors_fd };
536 struct ast_channel *rchan;
537 char *command;
538 int res = -1;
539 int test_available_fd = -1;
540 int hangup_info_sent = 0;
542 FILE *eivr_commands = NULL;
543 FILE *eivr_errors = NULL;
544 FILE *eivr_events = NULL;
546 if (!(eivr_events = fdopen(eivr_events_fd, "w"))) {
547 ast_chan_log(LOG_WARNING, chan, "Could not open stream to send events\n");
548 goto exit;
550 if (!(eivr_commands = fdopen(eivr_commands_fd, "r"))) {
551 ast_chan_log(LOG_WARNING, chan, "Could not open stream to receive commands\n");
552 goto exit;
554 if (eivr_errors_fd > -1) { /* if opening a socket connection, error stream will not be used */
555 if (!(eivr_errors = fdopen(eivr_errors_fd, "r"))) {
556 ast_chan_log(LOG_WARNING, chan, "Could not open stream to receive errors\n");
557 goto exit;
561 test_available_fd = open("/dev/null", O_RDONLY);
563 setvbuf(eivr_events, NULL, _IONBF, 0);
564 setvbuf(eivr_commands, NULL, _IONBF, 0);
565 if (eivr_errors) {
566 setvbuf(eivr_errors, NULL, _IONBF, 0);
569 res = 0;
571 while (1) {
572 if (ast_test_flag(chan, AST_FLAG_ZOMBIE)) {
573 ast_chan_log(LOG_NOTICE, chan, "Is a zombie\n");
574 res = -1;
575 break;
577 if (!hangup_info_sent && !(ast_test_flag(&flags, run_dead)) && ast_check_hangup(chan)) {
578 if (ast_test_flag(&flags, ignore_hangup)) {
579 ast_chan_log(LOG_NOTICE, chan, "Got check_hangup, but ignore_hangup set so sending 'I' command\n");
580 send_eivr_event(eivr_events, 'I', "HANGUP", chan);
581 hangup_info_sent = 1;
582 } else {
583 ast_chan_log(LOG_NOTICE, chan, "Got check_hangup\n");
584 send_eivr_event(eivr_events, 'H', NULL, chan);
585 res = -1;
586 break;
590 ready_fd = 0;
591 ms = 100;
592 errno = 0;
593 exception = 0;
595 rchan = ast_waitfor_nandfds(&chan, 1, waitfds, (eivr_errors_fd < 0) ? 1 : 2, &exception, &ready_fd, &ms);
597 if (chan->_state == AST_STATE_UP && !AST_LIST_EMPTY(&u->finishlist)) {
598 AST_LIST_LOCK(&u->finishlist);
599 while ((entry = AST_LIST_REMOVE_HEAD(&u->finishlist, list))) {
600 send_eivr_event(eivr_events, 'F', entry->filename, chan);
601 ast_free(entry);
603 AST_LIST_UNLOCK(&u->finishlist);
606 if (chan->_state == AST_STATE_UP && !(ast_check_hangup(chan)) && rchan) {
607 /* the channel has something */
608 f = ast_read(chan);
609 if (!f) {
610 ast_chan_log(LOG_NOTICE, chan, "Returned no frame\n");
611 send_eivr_event(eivr_events, 'H', NULL, chan);
612 res = -1;
613 break;
615 if (f->frametype == AST_FRAME_DTMF) {
616 send_eivr_event(eivr_events, f->subclass, NULL, chan);
617 if (u->option_autoclear) {
618 if (!u->abort_current_sound && !u->playing_silence)
619 send_eivr_event(eivr_events, 'T', NULL, chan);
620 AST_LIST_LOCK(&u->playlist);
621 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
622 send_eivr_event(eivr_events, 'D', entry->filename, chan);
623 ast_free(entry);
625 if (!u->playing_silence)
626 u->abort_current_sound = 1;
627 AST_LIST_UNLOCK(&u->playlist);
629 } else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) {
630 ast_chan_log(LOG_NOTICE, chan, "Got AST_CONTROL_HANGUP\n");
631 send_eivr_event(eivr_events, 'H', NULL, chan);
632 if (f->data.uint32) {
633 chan->hangupcause = f->data.uint32;
635 ast_frfree(f);
636 res = -1;
637 break;
639 ast_frfree(f);
640 } else if (ready_fd == eivr_commands_fd) {
641 char input[1024];
643 if (exception || (dup2(eivr_commands_fd, test_available_fd) == -1) || feof(eivr_commands)) {
644 ast_chan_log(LOG_WARNING, chan, "Child process went away\n");
645 res = -1;
646 break;
649 if (!fgets(input, sizeof(input), eivr_commands))
650 continue;
652 command = ast_strip(input);
654 if (option_debug)
655 ast_debug(1, "got command '%s'\n", input);
657 if (strlen(input) < 4)
658 continue;
660 if (input[0] == 'P') {
661 send_eivr_event(eivr_events, 'P', args->str, chan);
662 } else if ( input[0] == 'T' ) {
663 ast_chan_log(LOG_WARNING, chan, "Answering channel if needed and starting generator\n");
664 if (chan->_state != AST_STATE_UP) {
665 if (ast_test_flag(&flags, run_dead)) {
666 ast_chan_log(LOG_WARNING, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n");
667 send_eivr_event(eivr_events, 'Z', "ANSWER_FAILURE", chan);
668 continue;
670 ast_answer(chan);
672 if (!(u->gen_active)) {
673 if (ast_activate_generator(chan, &gen, u) < 0) {
674 ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n");
675 send_eivr_event(eivr_events, 'Z', "GENERATOR_FAILURE", chan);
676 } else {
677 u->gen_active = 1;
680 } else if (input[0] == 'S') {
681 if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) {
682 ast_chan_log(LOG_WARNING, chan, "Queue 'S'et called on unanswered channel\n");
683 send_eivr_event(eivr_events, 'Z', NULL, chan);
684 continue;
686 if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) {
687 ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
688 send_eivr_event(eivr_events, 'Z', NULL, chan);
689 strcpy(&input[2], "exception");
691 if (!u->abort_current_sound && !u->playing_silence)
692 send_eivr_event(eivr_events, 'T', NULL, chan);
693 AST_LIST_LOCK(&u->playlist);
694 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
695 send_eivr_event(eivr_events, 'D', entry->filename, chan);
696 ast_free(entry);
698 if (!u->playing_silence)
699 u->abort_current_sound = 1;
700 entry = make_entry(&input[2]);
701 if (entry)
702 AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
703 AST_LIST_UNLOCK(&u->playlist);
704 } else if (input[0] == 'A') {
705 if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) {
706 ast_chan_log(LOG_WARNING, chan, "Queue 'A'ppend called on unanswered channel\n");
707 send_eivr_event(eivr_events, 'Z', NULL, chan);
708 continue;
710 if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) {
711 ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
712 send_eivr_event(eivr_events, 'Z', NULL, chan);
713 strcpy(&input[2], "exception");
715 entry = make_entry(&input[2]);
716 if (entry) {
717 AST_LIST_LOCK(&u->playlist);
718 AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
719 AST_LIST_UNLOCK(&u->playlist);
721 } else if (input[0] == 'G') {
722 /* A get variable message: "G,variable1,variable2,..." */
723 char response[2048];
725 ast_chan_log(LOG_NOTICE, chan, "Getting a Variable out of the channel: %s\n", &input[2]);
726 ast_eivr_getvariable(chan, &input[2], response, sizeof(response));
727 send_eivr_event(eivr_events, 'G', response, chan);
728 } else if (input[0] == 'V') {
729 /* A set variable message: "V,variablename=foo" */
730 ast_chan_log(LOG_NOTICE, chan, "Setting a Variable up: %s\n", &input[2]);
731 ast_eivr_setvariable(chan, &input[2]);
732 } else if (input[0] == 'L') {
733 ast_chan_log(LOG_NOTICE, chan, "Log message from EIVR: %s\n", &input[2]);
734 } else if (input[0] == 'X') {
735 ast_chan_log(LOG_NOTICE, chan, "Exiting ExternalIVR: %s\n", &input[2]);
736 /*! \todo add deprecation debug message for X command here */
737 res = 0;
738 break;
739 } else if (input[0] == 'E') {
740 ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]);
741 send_eivr_event(eivr_events, 'E', NULL, chan);
742 res = 0;
743 break;
744 } else if (input[0] == 'H') {
745 ast_chan_log(LOG_NOTICE, chan, "Hanging up: %s\n", &input[2]);
746 send_eivr_event(eivr_events, 'H', NULL, chan);
747 res = -1;
748 break;
749 } else if (input[0] == 'O') {
750 if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) {
751 ast_chan_log(LOG_WARNING, chan, "Option called on unanswered channel\n");
752 send_eivr_event(eivr_events, 'Z', NULL, chan);
753 continue;
755 if (!strcasecmp(&input[2], "autoclear"))
756 u->option_autoclear = 1;
757 else if (!strcasecmp(&input[2], "noautoclear"))
758 u->option_autoclear = 0;
759 else
760 ast_chan_log(LOG_WARNING, chan, "Unknown option requested '%s'\n", &input[2]);
762 } else if (eivr_errors_fd && ready_fd == eivr_errors_fd) {
763 char input[1024];
765 if (exception || feof(eivr_errors)) {
766 ast_chan_log(LOG_WARNING, chan, "Child process went away\n");
767 res = -1;
768 break;
770 if (fgets(input, sizeof(input), eivr_errors)) {
771 command = ast_strip(input);
772 ast_chan_log(LOG_NOTICE, chan, "stderr: %s\n", command);
774 } else if ((ready_fd < 0) && ms) {
775 if (errno == 0 || errno == EINTR)
776 continue;
778 ast_chan_log(LOG_WARNING, chan, "Wait failed (%s)\n", strerror(errno));
779 break;
784 exit:
786 if (test_available_fd > -1) {
787 close(test_available_fd);
790 if (eivr_events)
791 fclose(eivr_events);
793 if (eivr_commands)
794 fclose(eivr_commands);
796 if (eivr_errors)
797 fclose(eivr_errors);
799 return res;
803 static int unload_module(void)
805 return ast_unregister_application(app);
808 static int load_module(void)
810 return ast_register_application(app, app_exec, synopsis, descrip);
813 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "External IVR Interface Application");