diff options
author | jpeeler <jpeeler@f38db490-d61c-443f-a65b-d21fe96a405b> | 2008-02-13 21:04:31 +0000 |
---|---|---|
committer | jpeeler <jpeeler@f38db490-d61c-443f-a65b-d21fe96a405b> | 2008-02-13 21:04:31 +0000 |
commit | a839cc623e9260785ff12295206b669e5b4db1f1 (patch) | |
tree | f5e3fa188c8d2156ed2f5883f9a695c6be29c450 /apps/app_externalivr.c | |
parent | 6f5756236895340ff15286801b5da4b4c6c9cab9 (diff) |
(closes issue #11825)
Reported by: ctooley
Patches:
additional_eivr_commands.patch uploaded by ctooley (license 136)
Tested by: ctooley
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@103662 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'apps/app_externalivr.c')
-rw-r--r-- | apps/app_externalivr.c | 549 |
1 files changed, 319 insertions, 230 deletions
diff --git a/apps/app_externalivr.c b/apps/app_externalivr.c index 8092fd92e..dadb82e68 100644 --- a/apps/app_externalivr.c +++ b/apps/app_externalivr.c @@ -85,7 +85,11 @@ struct gen_state { int sample_queue; }; -static void send_child_event(FILE *handle, const char event, const char *data, +static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u, + int eivr_events_fd, int eivr_commands_fd, int eivr_errors_fd, + const char *args); + +static void send_eivr_event(FILE *handle, const char event, const char *data, const struct ast_channel *chan) { char tmp[256]; @@ -222,6 +226,69 @@ static struct ast_generator gen = generate: gen_generate, }; +static void ast_eivr_getvariable(struct ast_channel *chan, char *data, char *outbuf, int outbuflen) +{ + // original input data: "G,var1,var2," + // data passed as "data": "var1,var2" + char *inbuf, *variable; + + const char *value; + char *saveptr; + int j; + + outbuf[0] = 0; + + for (j = 1, inbuf = data; ; j++, inbuf = NULL) { + variable = strtok_r(inbuf, ",", &saveptr); + if (variable == NULL) { + int outstrlen = strlen(outbuf); + if(outstrlen && outbuf[outstrlen - 1] == ',') { + outbuf[outstrlen - 1] = 0; + } + break; + } + + value = pbx_builtin_getvar_helper(chan, variable); + if(!value) + value = ""; + strncat(outbuf,variable,outbuflen); + strncat(outbuf,"=",outbuflen); + strncat(outbuf,value,outbuflen); + strncat(outbuf,",",outbuflen); + } +}; + +static void ast_eivr_setvariable(struct ast_channel *chan, char *data) +{ + char buf[1024]; + char *value; + + char *inbuf, *variable; + + char *saveptr; + int j; + + for(j=1, inbuf=data; ; j++, inbuf=NULL) { + variable = strtok_r(inbuf, ",", &saveptr); + ast_chan_log(LOG_DEBUG, chan, "Setting up a variable: %s\n", variable); + if(variable) { + //variable contains "varname=value" + strncpy(buf, variable, sizeof(buf)); + value = strchr(buf, '='); + if(!value) + value=""; + else { + value[0] = 0; + value++; + } + pbx_builtin_setvar_helper(chan, buf, value); + } + else break; + + } + +}; + static struct playlist_entry *make_entry(const char *filename) { struct playlist_entry *entry; @@ -243,10 +310,7 @@ static int app_exec(struct ast_channel *chan, void *data) int res = -1; int gen_active = 0; int pid; - char *buf, *command; - FILE *child_commands = NULL; - FILE *child_errors = NULL; - FILE *child_events = NULL; + char *buf, *pipe_delim_argbuf, *pdargbuf_ptr; struct ivr_localuser foo = { .playlist = AST_LIST_HEAD_INIT_VALUE, .finishlist = AST_LIST_HEAD_INIT_VALUE, @@ -271,25 +335,26 @@ static int app_exec(struct ast_channel *chan, void *data) buf = ast_strdupa(data); AST_STANDARD_APP_ARGS(args, buf); + //copy args and replace commas with pipes + pipe_delim_argbuf = ast_strdupa(data); + while((pdargbuf_ptr = strchr(pipe_delim_argbuf, ',')) != NULL) + pdargbuf_ptr[0] = '|'; + if (pipe(child_stdin)) { ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child input: %s\n", strerror(errno)); goto exit; } - if (pipe(child_stdout)) { ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child output: %s\n", strerror(errno)); goto exit; } - if (pipe(child_stderr)) { ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child errors: %s\n", strerror(errno)); goto exit; } - if (chan->_state != AST_STATE_UP) { ast_answer(chan); } - if (ast_activate_generator(chan, &gen, u) < 0) { ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n"); goto exit; @@ -322,17 +387,6 @@ static int app_exec(struct ast_channel *chan, void *data) _exit(1); } else { /* parent process */ - int child_events_fd = child_stdin[1]; - int child_commands_fd = child_stdout[0]; - int child_errors_fd = child_stderr[0]; - struct ast_frame *f; - int ms; - int exception; - int ready_fd; - int waitfds[2] = { child_errors_fd, child_commands_fd }; - struct ast_channel *rchan; - - pthread_sigmask(SIG_SETMASK, &oldset, NULL); close(child_stdin[0]); child_stdin[0] = 0; @@ -340,228 +394,263 @@ static int app_exec(struct ast_channel *chan, void *data) child_stdout[1] = 0; close(child_stderr[1]); child_stderr[1] = 0; + res = eivr_comm(chan, u, child_stdin[1], child_stdout[0], child_stderr[0], pipe_delim_argbuf); - if (!(child_events = fdopen(child_events_fd, "w"))) { - ast_chan_log(LOG_WARNING, chan, "Could not open stream for child events\n"); - goto exit; - } + exit: + if (gen_active) + ast_deactivate_generator(chan); - if (!(child_commands = fdopen(child_commands_fd, "r"))) { - ast_chan_log(LOG_WARNING, chan, "Could not open stream for child commands\n"); - goto exit; - } + if (child_stdin[0]) + close(child_stdin[0]); - if (!(child_errors = fdopen(child_errors_fd, "r"))) { - ast_chan_log(LOG_WARNING, chan, "Could not open stream for child errors\n"); - goto exit; - } + if (child_stdin[1]) + close(child_stdin[1]); - setvbuf(child_events, NULL, _IONBF, 0); - setvbuf(child_commands, NULL, _IONBF, 0); - setvbuf(child_errors, NULL, _IONBF, 0); + if (child_stdout[0]) + close(child_stdout[0]); - res = 0; + if (child_stdout[1]) + close(child_stdout[1]); - while (1) { - if (ast_test_flag(chan, AST_FLAG_ZOMBIE)) { - ast_chan_log(LOG_NOTICE, chan, "Is a zombie\n"); - res = -1; - break; - } + if (child_stderr[0]) + close(child_stderr[0]); - if (ast_check_hangup(chan)) { - ast_chan_log(LOG_NOTICE, chan, "Got check_hangup\n"); - send_child_event(child_events, 'H', NULL, chan); - res = -1; - break; - } + if (child_stderr[1]) + close(child_stderr[1]); - ready_fd = 0; - ms = 100; - errno = 0; - exception = 0; + while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) + ast_free(entry); - rchan = ast_waitfor_nandfds(&chan, 1, waitfds, 2, &exception, &ready_fd, &ms); - - if (!AST_LIST_EMPTY(&u->finishlist)) { - AST_LIST_LOCK(&u->finishlist); - while ((entry = AST_LIST_REMOVE_HEAD(&u->finishlist, list))) { - send_child_event(child_events, 'F', entry->filename, chan); - ast_free(entry); - } - AST_LIST_UNLOCK(&u->finishlist); - } - - if (rchan) { - /* the channel has something */ - f = ast_read(chan); - if (!f) { - ast_chan_log(LOG_NOTICE, chan, "Returned no frame\n"); - send_child_event(child_events, 'H', NULL, chan); - res = -1; - break; - } - - if (f->frametype == AST_FRAME_DTMF) { - send_child_event(child_events, f->subclass, NULL, chan); - if (u->option_autoclear) { - if (!u->abort_current_sound && !u->playing_silence) - send_child_event(child_events, 'T', NULL, chan); - AST_LIST_LOCK(&u->playlist); - while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) { - send_child_event(child_events, 'D', entry->filename, chan); - ast_free(entry); - } - if (!u->playing_silence) - u->abort_current_sound = 1; - AST_LIST_UNLOCK(&u->playlist); - } - } else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) { - ast_chan_log(LOG_NOTICE, chan, "Got AST_CONTROL_HANGUP\n"); - send_child_event(child_events, 'H', NULL, chan); - ast_frfree(f); - res = -1; - break; - } - ast_frfree(f); - } else if (ready_fd == child_commands_fd) { - char input[1024]; - - if (exception || feof(child_commands)) { - ast_chan_log(LOG_WARNING, chan, "Child process went away\n"); - res = -1; - break; - } - - if (!fgets(input, sizeof(input), child_commands)) - continue; - - command = ast_strip(input); - - if (option_debug) - ast_chan_log(LOG_DEBUG, chan, "got command '%s'\n", input); - - if (strlen(input) < 4) - continue; - - if (input[0] == 'S') { - if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) { - ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]); - send_child_event(child_events, 'Z', NULL, chan); - strcpy(&input[2], "exception"); - } - if (!u->abort_current_sound && !u->playing_silence) - send_child_event(child_events, 'T', NULL, chan); - AST_LIST_LOCK(&u->playlist); - while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) { - send_child_event(child_events, 'D', entry->filename, chan); - ast_free(entry); - } - if (!u->playing_silence) - u->abort_current_sound = 1; - entry = make_entry(&input[2]); - if (entry) - AST_LIST_INSERT_TAIL(&u->playlist, entry, list); - AST_LIST_UNLOCK(&u->playlist); - } else if (input[0] == 'A') { - if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) { - ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]); - send_child_event(child_events, 'Z', NULL, chan); - strcpy(&input[2], "exception"); - } - entry = make_entry(&input[2]); - if (entry) { - AST_LIST_LOCK(&u->playlist); - AST_LIST_INSERT_TAIL(&u->playlist, entry, list); - AST_LIST_UNLOCK(&u->playlist); - } - } else if (input[0] == 'E') { - ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]); - send_child_event(child_events, 'E', NULL, chan); - res = 0; - break; - } else if (input[0] == 'H') { - ast_chan_log(LOG_NOTICE, chan, "Hanging up: %s\n", &input[2]); - send_child_event(child_events, 'H', NULL, chan); - res = -1; - break; - } else if (input[0] == 'O') { - if (!strcasecmp(&input[2], "autoclear")) - u->option_autoclear = 1; - else if (!strcasecmp(&input[2], "noautoclear")) - u->option_autoclear = 0; - else - ast_chan_log(LOG_WARNING, chan, "Unknown option requested '%s'\n", &input[2]); - } else if (input[0] == 'V') { - char *c; - c = strchr(&input[2], '='); - if (!c) { - send_child_event(child_events, 'Z', NULL, chan); - } else { - *c++ = '\0'; - pbx_builtin_setvar_helper(chan, &input[2], c); - } - } - } else if (ready_fd == child_errors_fd) { - char input[1024]; - - if (exception || feof(child_errors)) { - ast_chan_log(LOG_WARNING, chan, "Child process went away\n"); - res = -1; - break; - } - - if (fgets(input, sizeof(input), child_errors)) { - command = ast_strip(input); - ast_chan_log(LOG_NOTICE, chan, "stderr: %s\n", command); - } - } else if ((ready_fd < 0) && ms) { - if (errno == 0 || errno == EINTR) - continue; - - ast_chan_log(LOG_WARNING, chan, "Wait failed (%s)\n", strerror(errno)); - break; - } - } + return res; } - - exit: - if (gen_active) - ast_deactivate_generator(chan); - - if (child_events) - fclose(child_events); - - if (child_commands) - fclose(child_commands); - - if (child_errors) - fclose(child_errors); - - if (child_stdin[0]) - close(child_stdin[0]); - - if (child_stdin[1]) - close(child_stdin[1]); - - if (child_stdout[0]) - close(child_stdout[0]); - - if (child_stdout[1]) - close(child_stdout[1]); - - if (child_stderr[0]) - close(child_stderr[0]); - - if (child_stderr[1]) - close(child_stderr[1]); - - while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) - ast_free(entry); - - return res; } +static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u, + int eivr_events_fd, int eivr_commands_fd, int eivr_errors_fd, + const char *args) +{ + struct playlist_entry *entry; + struct ast_frame *f; + int ms; + int exception; + int ready_fd; + int waitfds[2] = { eivr_commands_fd, eivr_errors_fd }; + struct ast_channel *rchan; + char *command; + int res = -1; + + FILE *eivr_commands = NULL; + FILE *eivr_errors = NULL; + FILE *eivr_events = NULL; + + if (!(eivr_events = fdopen(eivr_events_fd, "w"))) { + ast_chan_log(LOG_WARNING, chan, "Could not open stream to send events\n"); + goto exit; + } + if (!(eivr_commands = fdopen(eivr_commands_fd, "r"))) { + ast_chan_log(LOG_WARNING, chan, "Could not open stream to receive commands\n"); + goto exit; + } + if(eivr_errors_fd) { /*if opening a socket connection, error stream will not be used*/ + if (!(eivr_errors = fdopen(eivr_errors_fd, "r"))) { + ast_chan_log(LOG_WARNING, chan, "Could not open stream to receive errors\n"); + goto exit; + } + } + + setvbuf(eivr_events, NULL, _IONBF, 0); + setvbuf(eivr_commands, NULL, _IONBF, 0); + if(eivr_errors) + setvbuf(eivr_errors, NULL, _IONBF, 0); + + res = 0; + + while (1) { + if (ast_test_flag(chan, AST_FLAG_ZOMBIE)) { + ast_chan_log(LOG_NOTICE, chan, "Is a zombie\n"); + res = -1; + break; + } + if (ast_check_hangup(chan)) { + ast_chan_log(LOG_NOTICE, chan, "Got check_hangup\n"); + send_eivr_event(eivr_events, 'H', NULL, chan); + res = -1; + break; + } + + ready_fd = 0; + ms = 100; + errno = 0; + exception = 0; + + rchan = ast_waitfor_nandfds(&chan, 1, waitfds, 2, &exception, &ready_fd, &ms); + + if (!AST_LIST_EMPTY(&u->finishlist)) { + AST_LIST_LOCK(&u->finishlist); + while ((entry = AST_LIST_REMOVE_HEAD(&u->finishlist, list))) { + send_eivr_event(eivr_events, 'F', entry->filename, chan); + ast_free(entry); + } + AST_LIST_UNLOCK(&u->finishlist); + } + + if (rchan) { + /* the channel has something */ + f = ast_read(chan); + if (!f) { + ast_chan_log(LOG_NOTICE, chan, "Returned no frame\n"); + send_eivr_event(eivr_events, 'H', NULL, chan); + res = -1; + break; + } + if (f->frametype == AST_FRAME_DTMF) { + send_eivr_event(eivr_events, f->subclass, NULL, chan); + if (u->option_autoclear) { + if (!u->abort_current_sound && !u->playing_silence) + send_eivr_event(eivr_events, 'T', NULL, chan); + AST_LIST_LOCK(&u->playlist); + while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) { + send_eivr_event(eivr_events, 'D', entry->filename, chan); + ast_free(entry); + } + if (!u->playing_silence) + u->abort_current_sound = 1; + AST_LIST_UNLOCK(&u->playlist); + } + } else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) { + ast_chan_log(LOG_NOTICE, chan, "Got AST_CONTROL_HANGUP\n"); + send_eivr_event(eivr_events, 'H', NULL, chan); + ast_frfree(f); + res = -1; + break; + } + ast_frfree(f); + } else if (ready_fd == eivr_commands_fd) { + char input[1024]; + + if (exception || feof(eivr_commands)) { + ast_chan_log(LOG_WARNING, chan, "Child process went away\n"); + res = -1; + break; + } + + if (!fgets(input, sizeof(input), eivr_commands)) + continue; + + command = ast_strip(input); + + if (option_debug) + ast_chan_log(LOG_DEBUG, chan, "got command '%s'\n", input); + + if (strlen(input) < 4) + continue; + + if (input[0] == 'P') { + send_eivr_event(eivr_events, 'P', args, chan); + + } else if (input[0] == 'S') { + if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) { + ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]); + send_eivr_event(eivr_events, 'Z', NULL, chan); + strcpy(&input[2], "exception"); + } + if (!u->abort_current_sound && !u->playing_silence) + send_eivr_event(eivr_events, 'T', NULL, chan); + AST_LIST_LOCK(&u->playlist); + while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) { + send_eivr_event(eivr_events, 'D', entry->filename, chan); + ast_free(entry); + } + if (!u->playing_silence) + u->abort_current_sound = 1; + entry = make_entry(&input[2]); + if (entry) + AST_LIST_INSERT_TAIL(&u->playlist, entry, list); + AST_LIST_UNLOCK(&u->playlist); + } else if (input[0] == 'A') { + if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) { + ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]); + send_eivr_event(eivr_events, 'Z', NULL, chan); + strcpy(&input[2], "exception"); + } + entry = make_entry(&input[2]); + if (entry) { + AST_LIST_LOCK(&u->playlist); + AST_LIST_INSERT_TAIL(&u->playlist, entry, list); + AST_LIST_UNLOCK(&u->playlist); + } + } else if (input[0] == 'G') { + // A get variable message: "G,variable1,variable2,..." + char response[2048]; + ast_chan_log(LOG_NOTICE, chan, "Getting a Variable out of the channel: %s\n", &input[2]); + ast_eivr_getvariable(chan, &input[2], response, sizeof(response)); + send_eivr_event(eivr_events, 'G', response, chan); + } else if (input[0] == 'V') { + // A set variable message: "V,variablename=foo" + ast_chan_log(LOG_NOTICE, chan, "Setting a Variable up: %s\n", &input[2]); + ast_eivr_setvariable(chan, &input[2]); + } else if (input[0] == 'L') { + ast_chan_log(LOG_NOTICE, chan, "Log message from EIVR: %s\n", &input[2]); + } else if (input[0] == 'X') { + ast_chan_log(LOG_NOTICE, chan, "Exiting ExternalIVR: %s\n", &input[2]); + //TODO: add deprecation debug message for X command here + res = 0; + break; + } else if (input[0] == 'E') { + ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]); + send_eivr_event(eivr_events, 'E', NULL, chan); + res = 0; + break; + } else if (input[0] == 'H') { + ast_chan_log(LOG_NOTICE, chan, "Hanging up: %s\n", &input[2]); + send_eivr_event(eivr_events, 'H', NULL, chan); + res = -1; + break; + } else if (input[0] == 'O') { + if (!strcasecmp(&input[2], "autoclear")) + u->option_autoclear = 1; + else if (!strcasecmp(&input[2], "noautoclear")) + u->option_autoclear = 0; + else + ast_chan_log(LOG_WARNING, chan, "Unknown option requested '%s'\n", &input[2]); + } + } else if (eivr_errors_fd && ready_fd == eivr_errors_fd) { + char input[1024]; + + if (exception || feof(eivr_errors)) { + ast_chan_log(LOG_WARNING, chan, "Child process went away\n"); + res = -1; + break; + } + if (fgets(input, sizeof(input), eivr_errors)) { + command = ast_strip(input); + ast_chan_log(LOG_NOTICE, chan, "stderr: %s\n", command); + } + } else if ((ready_fd < 0) && ms) { + if (errno == 0 || errno == EINTR) + continue; + + ast_chan_log(LOG_WARNING, chan, "Wait failed (%s)\n", strerror(errno)); + break; + } + } + + +exit: + + if (eivr_events) + fclose(eivr_events); + + if (eivr_commands) + fclose(eivr_commands); + + if (eivr_errors) + fclose(eivr_errors); + + return res; + + } + static int unload_module(void) { return ast_unregister_application(app); |