commit 78f8abee333122e841d620b409358b3f36239f55
parent 7e657ecd8c171350c5b440c9cf8c9a56116328e3
Author: Drew DeVault <sir@cmpwn.com>
Date: Mon, 22 Nov 2021 10:06:33 +0100
os::exec: add exec::pipe
This provides a more portable way to create pipes than using unix::pipe
directly.
Signed-off-by: Drew DeVault <sir@cmpwn.com>
Diffstat:
3 files changed, 33 insertions(+), 7 deletions(-)
diff --git a/cmd/hare/release.ha b/cmd/hare/release.ha
@@ -9,7 +9,6 @@ use path;
use strconv;
use strings;
use temp;
-use unix;
type increment = enum {
MAJOR,
@@ -283,14 +282,14 @@ fn signtag(name: str, tag: str, key: str) (void | release_error) = {
const note = exec::cmd("git", "notes", "add", "-F", "-", tag)?;
exec::setenv(¬e, "GIT_NOTES_REF", "refs/notes/signatures/tar.gz");
- // Squelch "Signing data on standard input"
+ // Squelch "Signing data on standard input" message
// TODO: It might be better to capture this and print it to stderr
// ourselves if ssh-keygen exits nonzero, so that the error details are
// available to the user for diagnosis.
exec::addfile(&ssh, exec::nullfd, os::stderr);
- const pipe1 = unix::pipe()?;
- const pipe2 = unix::pipe()?;
+ const pipe1 = exec::pipe();
+ const pipe2 = exec::pipe();
exec::addfile(&archive, pipe1.1, os::stdout_file);
exec::addfile(&ssh, pipe1.0, os::stdin_file);
exec::addfile(&ssh, pipe2.1, os::stdout_file);
@@ -316,7 +315,7 @@ fn git_runcmd(args: str...) (void | release_error) = {
};
fn git_readcmd(args: str...) (str | release_error) = {
- const pipe = unix::pipe()?;
+ const pipe = exec::pipe();
defer io::close(pipe.0);
const cmd = exec::cmd("git", args...)?;
exec::addfile(&cmd, pipe.1, os::stdout_file);
diff --git a/os/exec/cmd.ha b/os/exec/cmd.ha
@@ -130,6 +130,9 @@ export fn setenv(cmd: *command, key: str, value: str) void = {
// Pass [[os::exec::closefd]] in the 'to' argument to close a file descriptor
// which was not opened with the CLOEXEC flag. Note that Hare opens all files
// with CLOEXEC by default, so this is not usually necessary.
+//
+// To write to a process's stdin, capture its stdout, or pipe two programs
+// together, see the [[pipe]] function.
export fn addfile(
cmd: *command,
from: (io::file | nullfd | closefd),
diff --git a/os/exec/exec+linux.ha b/os/exec/exec+linux.ha
@@ -1,8 +1,9 @@
use errors;
+use io;
+use os;
use rt;
use strings;
-use os;
-use io;
+use unix;
export type platform_cmd = io::file;
@@ -17,6 +18,29 @@ export fn fork() (int | void | error) = {
};
};
+// Creates an anonymous pipe for use with [[addfile]]. Any data written to the
+// second file may be read from the first file. The caller should close one or
+// both of the file descriptors after they have transferred them to another
+// process, and after they have finished using them themselves, if applicable.
+//
+// This function will abort the process if the system is unable to allocate the
+// resources for a pipe. If you need to handle this error gracefully, you may
+// call [[unix::pipe]] yourself, but this may reduce the portability of your
+// software.
+//
+// To capture the standard output of a process:
+//
+// let pipe = exec::pipe();
+// exec::addfile(&cmd, pipe.1, os::stdout_file);
+// exec::start(&cmd);
+// io::close(pipe.1);
+//
+// let data = io::drain(pipe.0)!;
+// io::close(pipe.0);
+export fn pipe() (io::file, io::file) = {
+ return unix::pipe()!;
+};
+
fn open(path: str) (platform_cmd | error) = {
match (rt::access(path, rt::X_OK)) {
case err: rt::errno =>