summaryrefslogtreecommitdiffstats
path: root/stream.c
diff options
context:
space:
mode:
authorKaz Kylheku <kaz@kylheku.com>2016-05-07 07:15:51 -0700
committerKaz Kylheku <kaz@kylheku.com>2016-05-07 07:15:51 -0700
commitbad7fa72982a65f8e2b57ec0db7c2d2eb80b6737 (patch)
tree5415d9cbb234bd2c3cb0ba93a3a925c6e06f7524 /stream.c
parent038bed1fa17743cbbfe4d219b6798d88254eef2c (diff)
downloadtxr-bad7fa72982a65f8e2b57ec0db7c2d2eb80b6737.tar.gz
txr-bad7fa72982a65f8e2b57ec0db7c2d2eb80b6737.tar.bz2
txr-bad7fa72982a65f8e2b57ec0db7c2d2eb80b6737.zip
New: standard stream redirection for subprocesses.
When subprocesses are created using open-command, open-process, run or sh, any streams not bound by those functions are obtained from *stdin*, *stdout* or *stderr*, as appropriate. Thus manipulating these variables has the effect of redirecting not only local output within the program but over coprocesses as well. * stream.c (struct save_fds): New type. (FDS_IN, FDS_OUT, FDS_ERR): New macros. (fds_init, fds_subst, fds_swizzle, fds_restore): New static functions. (open_command, open_process, run): "Swizzle" and restore the standard file descriptors. * txr.1: Updated documentation of affected function.
Diffstat (limited to 'stream.c')
-rw-r--r--stream.c152
1 files changed, 141 insertions, 11 deletions
diff --git a/stream.c b/stream.c
index 39dbc18e..4c6e3584 100644
--- a/stream.c
+++ b/stream.c
@@ -3419,15 +3419,98 @@ val open_tail(val path, val mode_str, val seek_end_p)
return set_mode_props(m, stream);
}
+struct save_fds {
+ int in;
+ int out;
+ int err;
+};
+
+#define FDS_IN 1
+#define FDS_OUT 2
+#define FDS_ERR 4
+
+static void fds_init(struct save_fds *fds)
+{
+ fds->in = fds->out = fds->err = -1;
+}
+
+static int fds_subst(val stream, int fd_std)
+{
+ int fd_orig = c_num(stream_fd(stream));
+
+ if (fd_orig == fd_std)
+ return -1;
+
+ {
+ int fd_dup = dup(fd_std);
+
+ if (fd_dup != -1) {
+ dup2(fd_orig, fd_std);
+ return fd_dup;
+ }
+
+ uw_throwf(file_error_s, lit("failed to duplicate file descriptor: ~d/~s"),
+ num(errno), string_utf8(strerror(errno)), nao);
+ }
+}
+
+static void fds_swizzle(struct save_fds *fds, int flags)
+{
+ if ((flags & FDS_IN) != 0)
+ fds->in = fds_subst(std_input, STDIN_FILENO);
+
+ if ((flags & FDS_OUT) != 0)
+ fds->out = fds_subst(std_output, STDOUT_FILENO);
+
+ if ((flags & FDS_ERR) != 0)
+ fds->err = fds_subst(std_error, STDERR_FILENO);
+}
+
+static void fds_restore(struct save_fds *fds)
+{
+ if (fds->in != -1) {
+ dup2(fds->in, STDIN_FILENO);
+ close(fds->in);
+ }
+
+ if (fds->out != -1) {
+ dup2(fds->out, STDOUT_FILENO);
+ close(fds->out);
+ }
+
+ if (fds->err != -1) {
+ dup2(fds->err, STDERR_FILENO);
+ close(fds->err);
+ }
+}
+
+
val open_command(val path, val mode_str)
{
struct stdio_mode m, m_r = stdio_mode_init_r;
- FILE *f = w_popen(c_str(path), c_str(normalize_mode(&m, mode_str, m_r)));
+ val mode = normalize_mode(&m, mode_str, m_r);
+ int input = m.read != 0;
+ struct save_fds sfds;
+ FILE *f = 0;
+
+ fds_init(&sfds);
+
+ uw_simple_catch_begin;
+
+ fds_swizzle(&sfds, (input ? FDS_IN : FDS_OUT) | FDS_ERR);
+
+ f = w_popen(c_str(path), c_str(mode));
if (!f)
uw_throwf(file_error_s, lit("error opening pipe ~a: ~d/~s"),
path, num(errno), string_utf8(strerror(errno)), nao);
+ uw_unwind {
+ fds_restore(&sfds);
+ }
+
+ uw_catch_end;
+
return set_mode_props(m, make_pipe_stream(f, path));
}
@@ -3442,10 +3525,18 @@ val open_process(val name, val mode_str, val args)
char **argv = 0;
val iter;
int i, nargs;
+ struct save_fds sfds;
+ val ret = nil;
args = default_bool_arg(args);
nargs = c_num(length(args)) + 1;
+ fds_init(&sfds);
+
+ uw_simple_catch_begin;
+
+ fds_swizzle(&sfds, (input ? FDS_IN : FDS_OUT) | FDS_ERR);
+
if (pipe(fd) == -1) {
uw_throwf(file_error_s, lit("opening pipe ~a, pipe syscall failed: ~d/~s"),
name, num(errno), string_utf8(strerror(errno)), nao);
@@ -3518,8 +3609,16 @@ val open_process(val name, val mode_str, val args)
free(utf8mode);
/* TODO: catch potential OOM exception here and kill process. */
- return set_mode_props(m, make_pipevp_stream(f, name, pid));
+ ret = set_mode_props(m, make_pipevp_stream(f, name, pid));
}
+
+ uw_unwind {
+ fds_restore(&sfds);
+ }
+
+ uw_catch_end;
+
+ return ret;
}
#else
@@ -3623,6 +3722,8 @@ static val run(val name, val args)
char **argv = 0;
val iter;
int i, nargs;
+ struct save_fds sfds;
+ val ret = nil;
args = default_bool_arg(args);
nargs = c_num(length(args)) + 1;
@@ -3635,6 +3736,12 @@ static val run(val name, val args)
}
argv[i] = 0;
+ fds_init(&sfds);
+
+ uw_simple_catch_begin;
+
+ fds_swizzle(&sfds, FDS_IN | FDS_OUT | FDS_ERR);
+
pid = fork();
if (pid == -1) {
@@ -3649,22 +3756,32 @@ static val run(val name, val args)
execvp(argv[0], argv);
_exit(errno);
} else {
- int status;
+ int status, wres;
for (i = 0; i < nargs; i++)
free(argv[i]);
free(argv);
- while (waitpid(pid, &status, 0) == -1 && errno == EINTR)
+ while ((wres = waitpid(pid, &status, 0)) == -1 && errno == EINTR)
;
- if (status < 0)
- return nil;
+ if (wres != -1) {
#if HAVE_SYS_WAIT
- if (WIFEXITED(status)) {
- int exitstatus = WEXITSTATUS(status);
- return num(exitstatus);
- }
+ if (WIFEXITED(status)) {
+ int exitstatus = WEXITSTATUS(status);
+ ret = num(exitstatus);
+ goto out;
+ }
#endif
- return status == 0 ? zero : nil;
+ ret = (status == 0 ? zero : nil);
+ }
+ }
+
+out:
+ uw_unwind {
+ fds_restore(&sfds);
}
+
+ uw_catch_end;
+
+ return ret;
}
static val sh(val command)
@@ -3677,10 +3794,17 @@ static val run(val command, val args)
const wchar_t **wargv = 0;
val iter;
int i, nargs, status;
+ struct save_fds sfds;
args = default_bool_arg(args);
nargs = c_num(length(args)) + 1;
+ fds_init(&sfds);
+
+ uw_simple_catch_begin;
+
+ fds_swizzle(&sfds, FDS_IN | FDS_OUT | FDS_ERR);
+
wargv = coerce(const wchar_t **, chk_malloc((nargs + 2) * sizeof *wargv));
for (i = 0, iter = cons(command, args); iter; i++, iter = cdr(iter))
@@ -3693,6 +3817,12 @@ static val run(val command, val args)
gc_hint(args);
+ uw_unwind {
+ fds_restore(&sfds);
+ }
+
+ uw_catch_end;
+
return (status < 0) ? nil : num(status);
}