1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
|
// SPDX-License-Identifier: MPL-2.0
// (c) Hare authors <https://harelang.org>
use errors;
use fs;
use io;
use os;
use rt;
use types::c;
use unix;
use path;
// Forks the current process, returning the [[process]] of the child (to the
// parent) and void (to the child), or an error.
export fn fork() (process | void | error) = {
match (rt::fork()) {
case let err: rt::errno =>
return errors::errno(err);
case let i: int =>
return i: process;
case void =>
return void;
};
};
// 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:
//
// const (read, write) = exec::pipe();
// exec::addfile(&cmd, os::stdout_file, write);
// let proc = exec::start(&cmd)!;
// io::close(write)!;
//
// let data = io::drain(read)!;
// io::close(read)!;
// exec::wait(&proc)!;
//
// To write to the standard input of a process:
//
// const (read, write) = exec::pipe();
// exec::addfile(&cmd, os::stdin_file, read);
// let proc = exec::start(&cmd)!;
// io::close(read)!;
//
// io::writeall(write, data)!;
// io::close(write)!;
// exec::wait(&proc)!;
export fn pipe() (io::file, io::file) = {
return unix::pipe()!;
};
fn open(path: str) (platform_cmd | error) = {
if (os::access(path, os::amode::X_OK)?) {
// Length was already checked by access()
return path::init(path)!;
};
return errors::noaccess;
};
fn platform_exec(cmd: *command) error = {
let need_devnull = false;
for (let file &.. cmd.files) {
const from = match (file.0) {
case let file: io::file =>
yield file;
case nullfd =>
need_devnull = true;
continue;
case closefd =>
continue;
};
file.0 = match (rt::fcntl(from, rt::F_DUPFD_CLOEXEC, 0)) {
case let fd: int =>
yield fd;
case let err: rt::errno =>
return errors::errno(err);
};
};
const devnull: io::file = if (need_devnull) {
yield os::open("/dev/null", fs::flag::RDWR)!;
} else -1;
for (let file .. cmd.files) {
const from = match (file.0) {
case let file: io::file =>
yield file;
case nullfd =>
yield devnull;
case closefd =>
io::close(file.1)?;
continue;
};
if (file.1 == from) {
let flags = match (rt::fcntl(from, rt::F_GETFD, 0)) {
case let flags: int =>
yield flags;
case let e: rt::errno =>
return errors::errno(e);
};
rt::fcntl(from, rt::F_SETFD, flags & ~rt::FD_CLOEXEC)!;
} else {
match (rt::dup2(from, file.1)) {
case int => void;
case let e: rt::errno =>
return errors::errno(e);
};
};
};
if (cmd.dir != "") {
os::chdir(cmd.dir)?;
};
let envp: nullable *[*]nullable *const c::char = null;
if (len(cmd.env) > 1) {
envp = cmd.env: *[*]nullable *const c::char;
};
return errors::errno(rt::execve(path::string(&cmd.platform),
cmd.argv: *[*]nullable *const u8,
envp: *[*]nullable *const u8));
};
fn platform_start(cmd: *command) (process | errors::error) = {
// TODO: Let the user configure clone more to their taste (e.g. SIGCHLD)
let pipe: [2]int = [0...];
match (rt::pipe2(&pipe, rt::O_CLOEXEC)) {
case let err: rt::errno =>
return errors::errno(err);
case void => void;
};
match (rt::fork()) {
case let err: rt::errno =>
return errors::errno(err);
case let pid: int =>
rt::close(pipe[1])!;
defer rt::close(pipe[0])!;
let errno: int = 0;
match (rt::read(pipe[0], &errno, size(int))) {
case let err: rt::errno =>
return errors::errno(err);
case let n: size =>
switch (n) {
case size(int) =>
return errors::errno(errno);
case 0 =>
return pid;
case =>
abort("Unexpected rt::read result");
};
};
case void =>
rt::close(pipe[0])!;
let err = platform_exec(cmd);
if (!(err is errors::opaque_)) {
rt::exit(1);
};
let err = err as errors::opaque_;
let err = &err.data: *rt::errno;
rt::write(pipe[1], err, size(int))!;
rt::exit(1);
};
};
|