diff options
author | Timo Teräs <timo.teras@iki.fi> | 2016-06-02 22:10:06 +0300 |
---|---|---|
committer | Timo Teräs <timo.teras@iki.fi> | 2016-11-15 22:25:14 +0200 |
commit | 070a51bf7c00f49bb82d26e889b88906a9b2fd0c (patch) | |
tree | fddd2462220284d9dd7abba8ec2c1c0d68a68159 /apps/app_externalivr.c | |
parent | 0cc14597b29203259b5e6ae4496f9f6d4f4e76f2 (diff) |
Implement internal abstraction for iostreams
fopencookie/funclose is a non-standard API and should not be used
in portable software. Additionally, the way FILE's fd is used in
non-blocking mode is undefined behaviour and cannot be relied on.
This introduces internal abstraction for io streams, that allows
implementing the desired virtualization of read/write operations
with necessary timeout handling.
ASTERISK-24515 #close
ASTERISK-24517 #close
Change-Id: Id916aef418b665ced6a7489aef74908b6e376e85
Diffstat (limited to 'apps/app_externalivr.c')
-rw-r--r-- | apps/app_externalivr.c | 119 |
1 files changed, 54 insertions, 65 deletions
diff --git a/apps/app_externalivr.c b/apps/app_externalivr.c index 273f96381..c2224b44b 100644 --- a/apps/app_externalivr.c +++ b/apps/app_externalivr.c @@ -150,10 +150,12 @@ struct gen_state { }; static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u, - int *eivr_events_fd, int *eivr_commands_fd, int *eivr_errors_fd, + struct ast_iostream *eivr_events, + struct ast_iostream *eivr_commands, + struct ast_iostream *eivr_errors, const struct ast_str *args, const struct ast_flags flags); -static void send_eivr_event(FILE *handle, const char event, const char *data, +static void send_eivr_event(struct ast_iostream *stream, const char event, const char *data, const struct ast_channel *chan) { struct ast_str *tmp = ast_str_create(12); @@ -162,9 +164,11 @@ static void send_eivr_event(FILE *handle, const char event, const char *data, if (data) { ast_str_append(&tmp, 0, ",%s", data); } + ast_str_append(&tmp, 0, "\n"); + ast_iostream_write(stream, ast_str_buffer(tmp), strlen(ast_str_buffer(tmp))); + ast_str_truncate(tmp, -1); - fprintf(handle, "%s\n", ast_str_buffer(tmp)); - ast_debug(1, "sent '%s'\n", ast_str_buffer(tmp)); + ast_debug(1, "sent '%s'", ast_str_buffer(tmp)); ast_free(tmp); } @@ -393,6 +397,8 @@ static int app_exec(struct ast_channel *chan, const char *data) int child_stdin[2] = { -1, -1 }; int child_stdout[2] = { -1, -1 }; int child_stderr[2] = { -1, -1 }; + struct ast_iostream *stream_stdin = NULL, *stream_stdout = NULL, + *stream_stderr = NULL; int res = -1; int pid; @@ -524,7 +530,7 @@ static int app_exec(struct ast_channel *chan, const char *data) goto exit; } - res = eivr_comm(chan, u, &ser->fd, &ser->fd, NULL, comma_delim_args, flags); + res = eivr_comm(chan, u, ser->stream, ser->stream, NULL, comma_delim_args, flags); } else { if (pipe(child_stdin)) { @@ -566,7 +572,12 @@ static int app_exec(struct ast_channel *chan, const char *data) child_stdout[1] = -1; close(child_stderr[1]); child_stderr[1] = -1; - res = eivr_comm(chan, u, &child_stdin[1], &child_stdout[0], &child_stderr[0], comma_delim_args, flags); + + stream_stdin = ast_iostream_from_fd(&child_stdin[1]); + stream_stdout = ast_iostream_from_fd(&child_stdout[0]); + stream_stderr = ast_iostream_from_fd(&child_stderr[0]); + + res = eivr_comm(chan, u, stream_stdin, stream_stdout, stream_stderr, comma_delim_args, flags); } } @@ -574,6 +585,15 @@ static int app_exec(struct ast_channel *chan, const char *data) if (u->gen_active) { ast_deactivate_generator(chan); } + if (stream_stdin) { + ast_iostream_close(stream_stdin); + } + if (stream_stdout) { + ast_iostream_close(stream_stdout); + } + if (stream_stderr) { + ast_iostream_close(stream_stderr); + } if (child_stdin[0] > -1) { close(child_stdin[0]); } @@ -602,46 +622,25 @@ static int app_exec(struct ast_channel *chan, 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 struct ast_str *args, const struct ast_flags flags) + struct ast_iostream *eivr_events, + struct ast_iostream *eivr_commands, + struct ast_iostream *eivr_errors, + const struct ast_str *args, const struct ast_flags flags) { + char input[1024]; struct playlist_entry *entry; struct ast_frame *f; int ms; int exception; int ready_fd; - int waitfds[2] = { *eivr_commands_fd, (eivr_errors_fd) ? *eivr_errors_fd : -1 }; + int waitfds[2]; + int r; struct ast_channel *rchan; int res = -1; - int test_available_fd = -1; int hangup_info_sent = 0; - - FILE *eivr_commands = NULL; - FILE *eivr_errors = NULL; - FILE *eivr_events = NULL; - if (!(eivr_events = fdopen(*eivr_events_fd, "w"))) { - ast_chan_log(LOG_ERROR, chan, "Could not open stream to send events\n"); - goto exit; - } - if (!(eivr_commands = fdopen(*eivr_commands_fd, "r"))) { - ast_chan_log(LOG_ERROR, 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_ERROR, chan, "Could not open stream to receive errors\n"); - goto exit; - } - } - - test_available_fd = open("/dev/null", O_RDONLY); - - setvbuf(eivr_events, NULL, _IONBF, 0); - setvbuf(eivr_commands, NULL, _IONBF, 0); - if (eivr_errors) { - setvbuf(eivr_errors, NULL, _IONBF, 0); - } + waitfds[0] = ast_iostream_get_fd(eivr_commands); + waitfds[1] = eivr_errors ? ast_iostream_get_fd(eivr_errors) : -1; while (1) { if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE)) { @@ -665,7 +664,7 @@ static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u, errno = 0; exception = 0; - rchan = ast_waitfor_nandfds(&chan, 1, waitfds, (eivr_errors_fd) ? 2 : 1, &exception, &ready_fd, &ms); + rchan = ast_waitfor_nandfds(&chan, 1, waitfds, (eivr_errors) ? 2 : 1, &exception, &ready_fd, &ms); if (ast_channel_state(chan) == AST_STATE_UP && !AST_LIST_EMPTY(&u->finishlist)) { AST_LIST_LOCK(&u->finishlist); @@ -713,15 +712,18 @@ static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u, break; } ast_frfree(f); - } else if (ready_fd == *eivr_commands_fd) { - char input[1024]; - - if (exception || (dup2(*eivr_commands_fd, test_available_fd) == -1) || feof(eivr_commands)) { + } else if (ready_fd == waitfds[0]) { + if (exception) { ast_chan_log(LOG_ERROR, chan, "Child process went away\n"); break; } - if (!fgets(input, sizeof(input), eivr_commands)) { + r = ast_iostream_gets(eivr_commands, input, sizeof(input)); + if (r <= 0) { + if (r == 0) { + ast_chan_log(LOG_ERROR, chan, "Child process went away\n"); + break; + } continue; } @@ -867,16 +869,19 @@ static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u, 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)) { + } else if (ready_fd == waitfds[1]) { + if (exception) { ast_chan_log(LOG_ERROR, chan, "Child process went away\n"); break; } - if (fgets(input, sizeof(input), eivr_errors)) { + + r = ast_iostream_gets(eivr_errors, input, sizeof(input)); + if (r > 0) { ast_chan_log(LOG_NOTICE, chan, "stderr: %s\n", ast_strip(input)); - } + } else if (r == 0) { + ast_chan_log(LOG_ERROR, chan, "Child process went away\n"); + break; + } } else if ((ready_fd < 0) && ms) { if (errno == 0 || errno == EINTR) continue; @@ -886,23 +891,7 @@ static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u, } } - exit: - if (test_available_fd > -1) { - close(test_available_fd); - } - if (eivr_events) { - fclose(eivr_events); - *eivr_events_fd = -1; - } - if (eivr_commands) { - fclose(eivr_commands); - *eivr_commands_fd = -1; - } - if (eivr_errors) { - fclose(eivr_errors); - *eivr_errors_fd = -1; - } - return res; + return res; } static int unload_module(void) |