commit 9acf605c7d172a4017cf5022b6a5d7f9232e2896
parent 04655ea7214c48b7e57ac03d96b44edc1ea192b7
Author: Eyal Sawady <ecs@d2evs.net>
Date: Wed, 3 Nov 2021 16:55:48 +0000
os::exec: implement FD manipulation
Signed-off-by: Eyal Sawady <ecs@d2evs.net>
Diffstat:
7 files changed, 72 insertions(+), 2 deletions(-)
diff --git a/os/exec/cmd.ha b/os/exec/cmd.ha
@@ -1,5 +1,6 @@
use ascii;
use errors;
+use io;
use os;
use strings;
@@ -37,6 +38,7 @@ export fn cmd(name: str, args: str...) (command | error) = {
},
argv = alloc([], len(args) + 1z),
env = alloc([], len(env)),
+ files = [],
...
};
append(cmd.argv, name, args...);
@@ -114,6 +116,27 @@ export fn setenv(cmd: *command, key: str, value: str) void = {
append(cmd.env, strings::concat(fullkey, value));
};
+// Maps an [[io::file]] into the command. All mappings are performed
+// atomically, such that the following:
+//
+// exec::setfd(&cmd, os::stdout_file, os::stderr);
+// exec::setfd(&cmd, os::stderr, os::stdout_file);
+//
+// will swap stdout and stderr, rather than duplicating stdout onto stderr.
+// If the same [[io::file]] is mapped to multiple times, only the last mapping
+// will take effect.
+export fn addfile(cmd: *command, old: io::file, new: io::file) void = {
+ // TODO: Can we make old be an io::handle?
+ append(cmd.files, (old, new));
+};
+
+// Mapp [[os::stdin]], [[os::stdout]], and [[os::stderr]] into the command.
+export fn addstd(cmd: *command) void = {
+ addfile(cmd, os::stdin_file, os::stdin_file);
+ addfile(cmd, os::stdout_file, os::stdout_file);
+ addfile(cmd, os::stderr, os::stderr);
+};
+
fn lookup(name: str) (platform_cmd | void) = {
const path = match (os::getenv("PATH")) {
case void =>
diff --git a/os/exec/exec+freebsd.ha b/os/exec/exec+freebsd.ha
@@ -1,9 +1,10 @@
use errors;
+use io;
use rt;
use strings;
use os;
-export type platform_cmd = int;
+export type platform_cmd = io::file;
// Forks the current process, returning the pid of the child (to the parent) and
// void (to the child), or an error.
@@ -55,6 +56,23 @@ fn platform_exec(cmd: *command) error = {
envp = env: *[*]nullable *const char;
};
+ for (let i = 0z; i < len(cmd.files); i += 1) {
+ cmd.files[i].0 = match (rt::fcntl(cmd.files[i].0, rt::F_DUPFD_CLOEXEC, 0)) {
+ case fd: int =>
+ yield fd;
+ case err: rt::errno =>
+ return errors::errno(err);
+ };
+ };
+
+ for (let i = 0z; i < len(cmd.files); i += 1) {
+ match (rt::dup2(cmd.files[i].0, cmd.files[i].1)) {
+ case int => void;
+ case e: rt::errno =>
+ return errors::errno(e);
+ };
+ };
+
return errors::errno(rt::fexecve(cmd.platform,
argv: *[*]nullable *const char, envp));
};
diff --git a/os/exec/exec+linux.ha b/os/exec/exec+linux.ha
@@ -2,8 +2,9 @@ use errors;
use rt;
use strings;
use os;
+use io;
-export type platform_cmd = int;
+export type platform_cmd = io::file;
// Forks the current process, returning the pid of the child (to the parent) and
// void (to the child), or an error.
@@ -56,6 +57,23 @@ fn platform_exec(cmd: *command) error = {
envp = env: *[*]nullable *const char;
};
+ for (let i = 0z; i < len(cmd.files); i += 1) {
+ cmd.files[i].0 = match (rt::fcntl(cmd.files[i].0, rt::F_DUPFD_CLOEXEC, 0)) {
+ case fd: int =>
+ yield fd;
+ case err: rt::errno =>
+ return errors::errno(err);
+ };
+ };
+
+ for (let i = 0z; i < len(cmd.files); i += 1) {
+ match (rt::dup2(cmd.files[i].0, cmd.files[i].1)) {
+ case int => void;
+ case e: rt::errno =>
+ return errors::errno(e);
+ };
+ };
+
return errors::errno(rt::execveat(cmd.platform, strings::c_empty,
argv: *[*]nullable *const char, envp, rt::AT_EMPTY_PATH));
};
diff --git a/os/exec/types.ha b/os/exec/types.ha
@@ -1,10 +1,12 @@
use errors;
+use io;
// An executable command.
export type command = struct {
platform: platform_cmd,
argv: []str,
env: []str,
+ files: [](io::file, io::file),
};
// Returned when path resolution fails to find a command by its name.
diff --git a/rt/+freebsd/syscalls.ha b/rt/+freebsd/syscalls.ha
@@ -470,3 +470,7 @@ export fn sysctlbyname(name: str, oldp: nullable *void, oldlenp: nullable *size,
newp: uintptr: u64, newlen: u64))?;
return;
};
+
+export fn dup2(oldfd: int, newfd: int) (int | errno) = {
+ return wrap_return(syscall2(SYS_dup2, oldfd: u64, newfd: u64))?: int;
+};
diff --git a/rt/+linux/syscalls.ha b/rt/+linux/syscalls.ha
@@ -159,6 +159,10 @@ export fn dup(fd: int) (int | errno) = {
return wrap_return(syscall1(SYS_dup, fd: u64))?: int;
};
+export fn dup2(oldfd: int, newfd: int) (int | errno) = {
+ return wrap_return(syscall2(SYS_dup2, oldfd: u64, newfd: u64))?: int;
+};
+
export fn close(fd: int) (void | errno) = {
wrap_return(syscall1(SYS_close, fd: u64))?;
return;
diff --git a/rt/+linux/types.ha b/rt/+linux/types.ha
@@ -215,6 +215,7 @@ export def W_OK: int = 2;
export def X_OK: int = 1;
export def F_DUPFD: int = 0;
+export def F_DUPFD_CLOEXEC: int = 1030;
export def F_GETFD: int = 1;
export def F_SETFD: int = 2;
export def F_GETFL: int = 3;