Commit dd55b72949
Changed files (6)
lib/std/c/darwin.zig
@@ -78,15 +78,40 @@ pub const posix_spawn_file_actions_t = *opaque {};
pub extern "c" fn posix_spawnattr_init(attr: *posix_spawnattr_t) c_int;
pub extern "c" fn posix_spawnattr_destroy(attr: *posix_spawnattr_t) void;
pub extern "c" fn posix_spawnattr_setflags(attr: *posix_spawnattr_t, flags: c_short) c_int;
+pub extern "c" fn posix_spawnattr_getflags(attr: *const posix_spawnattr_t, flags: *c_short) c_int;
pub extern "c" fn posix_spawn_file_actions_init(actions: *posix_spawn_file_actions_t) c_int;
pub extern "c" fn posix_spawn_file_actions_destroy(actions: *posix_spawn_file_actions_t) void;
+pub extern "c" fn posix_spawn_file_actions_addclose(actions: *posix_spawn_file_actions_t, filedes: fd_t) c_int;
+pub extern "c" fn posix_spawn_file_actions_addopen(
+ actions: *posix_spawn_file_actions_t,
+ filedes: fd_t,
+ path: [*:0]const u8,
+ oflag: c_int,
+ mode: mode_t,
+) c_int;
+pub extern "c" fn posix_spawn_file_actions_adddup2(
+ actions: *posix_spawn_file_actions_t,
+ filedes: fd_t,
+ newfiledes: fd_t,
+) c_int;
+pub extern "c" fn posix_spawn_file_actions_addinherit_np(actions: *posix_spawn_file_actions_t, filedes: fd_t) c_int;
+pub extern "c" fn posix_spawn_file_actions_addchdir_np(actions: *posix_spawn_file_actions_t, path: [*:0]const u8) c_int;
+pub extern "c" fn posix_spawn_file_actions_addfchdir_np(actions: *posix_spawn_file_actions_t, filedes: fd_t) c_int;
+pub extern "c" fn posix_spawn(
+ pid: *pid_t,
+ path: [*:0]const u8,
+ actions: ?*const posix_spawn_file_actions_t,
+ attr: ?*const posix_spawnattr_t,
+ argv: [*:null]?[*:0]const u8,
+ env: [*:null]?[*:0]const u8,
+) c_int;
pub extern "c" fn posix_spawnp(
pid: *pid_t,
path: [*:0]const u8,
actions: ?*const posix_spawn_file_actions_t,
- attr: *const posix_spawnattr_t,
- argv: [*][*:0]const u8,
- env: [*][*:0]const u8,
+ attr: ?*const posix_spawnattr_t,
+ argv: [*:null]?[*:0]const u8,
+ env: [*:null]?[*:0]const u8,
) c_int;
pub extern "c" fn kevent64(
@@ -2563,16 +2588,16 @@ pub const CPUFAMILY = enum(u32) {
_,
};
-pub const POSIX_SPAWN_RESETIDS: c_int = 0x0001;
-pub const POSIX_SPAWN_SETPGROUP: c_int = 0x0002;
-pub const POSIX_SPAWN_SETSIGDEF: c_int = 0x0004;
-pub const POSIX_SPAWN_SETSIGMASK: c_int = 0x0008;
-pub const POSIX_SPAWN_SETEXEC: c_int = 0x0040;
-pub const POSIX_SPAWN_START_SUSPENDED: c_int = 0x0080;
-pub const _POSIX_SPAWN_DISABLE_ASLR: c_int = 0x0100;
-pub const POSIX_SPAWN_SETSID: c_int = 0x0400;
-pub const _POSIX_SPAWN_RESLIDE: c_int = 0x0800;
-pub const POSIX_SPAWN_CLOEXEC_DEFAULT: c_int = 0x4000;
+pub const POSIX_SPAWN_RESETIDS = 0x0001;
+pub const POSIX_SPAWN_SETPGROUP = 0x0002;
+pub const POSIX_SPAWN_SETSIGDEF = 0x0004;
+pub const POSIX_SPAWN_SETSIGMASK = 0x0008;
+pub const POSIX_SPAWN_SETEXEC = 0x0040;
+pub const POSIX_SPAWN_START_SUSPENDED = 0x0080;
+pub const _POSIX_SPAWN_DISABLE_ASLR = 0x0100;
+pub const POSIX_SPAWN_SETSID = 0x0400;
+pub const _POSIX_SPAWN_RESLIDE = 0x0800;
+pub const POSIX_SPAWN_CLOEXEC_DEFAULT = 0x4000;
pub const PT_TRACE_ME = 0;
pub const PT_CONTINUE = 7;
lib/std/os/posix_spawn.zig
@@ -0,0 +1,282 @@
+const std = @import("std");
+const builtin = @import("builtin");
+
+const os = @import("../os.zig");
+const system = os.system;
+const errno = system.getErrno;
+const fd_t = system.fd_t;
+const mode_t = system.mode_t;
+const pid_t = system.pid_t;
+const unexpectedErrno = os.unexpectedErrno;
+const UnexpectedError = os.UnexpectedError;
+const toPosixPath = os.toPosixPath;
+const WaitPidResult = os.WaitPidResult;
+
+pub usingnamespace posix_spawn;
+
+pub const Error = error{
+ SystemResources,
+ InvalidFileDescriptor,
+ NameTooLong,
+ TooBig,
+ PermissionDenied,
+ InputOutput,
+ FileSystem,
+ FileNotFound,
+ InvalidExe,
+ NotDir,
+ FileBusy,
+
+ /// Returned when the child fails to execute either in the pre-exec() initialization step, or
+ /// when exec(3) is invoked.
+ ChildExecFailed,
+} || UnexpectedError;
+
+const posix_spawn = if (builtin.target.isDarwin()) struct {
+ pub const Attr = struct {
+ attr: system.posix_spawnattr_t,
+
+ pub fn init() Error!Attr {
+ var attr: system.posix_spawnattr_t = undefined;
+ switch (errno(system.posix_spawnattr_init(&attr))) {
+ .SUCCESS => return Attr{ .attr = attr },
+ .NOMEM => return error.SystemResources,
+ .INVAL => unreachable,
+ else => |err| return unexpectedErrno(err),
+ }
+ }
+
+ pub fn deinit(self: *Attr) void {
+ system.posix_spawnattr_destroy(&self.attr);
+ self.* = undefined;
+ }
+
+ pub fn get(self: Attr) Error!u16 {
+ var flags: c_short = undefined;
+ switch (errno(system.posix_spawnattr_getflags(&self.attr, &flags))) {
+ .SUCCESS => return @bitCast(u16, flags),
+ .INVAL => unreachable,
+ else => |err| return unexpectedErrno(err),
+ }
+ }
+
+ pub fn set(self: *Attr, flags: u16) Error!void {
+ switch (errno(system.posix_spawnattr_setflags(&self.attr, @bitCast(c_short, flags)))) {
+ .SUCCESS => return,
+ .INVAL => unreachable,
+ else => |err| return unexpectedErrno(err),
+ }
+ }
+ };
+
+ pub const Actions = struct {
+ actions: system.posix_spawn_file_actions_t,
+
+ pub fn init() Error!Actions {
+ var actions: system.posix_spawn_file_actions_t = undefined;
+ switch (errno(system.posix_spawn_file_actions_init(&actions))) {
+ .SUCCESS => return Actions{ .actions = actions },
+ .NOMEM => return error.SystemResources,
+ .INVAL => unreachable,
+ else => |err| return unexpectedErrno(err),
+ }
+ }
+
+ pub fn deinit(self: *Actions) void {
+ system.posix_spawn_file_actions_destroy(&self.actions);
+ self.* = undefined;
+ }
+
+ pub fn open(self: *Actions, fd: fd_t, path: []const u8, flags: u32, mode: mode_t) Error!void {
+ const posix_path = try toPosixPath(path);
+ return self.openZ(fd, &posix_path, flags, mode);
+ }
+
+ pub fn openZ(self: *Actions, fd: fd_t, path: [*:0]const u8, flags: u32, mode: mode_t) Error!void {
+ switch (errno(system.posix_spawn_file_actions_addopen(&self.actions, fd, path, @bitCast(c_int, flags), mode))) {
+ .SUCCESS => return,
+ .BADF => return error.InvalidFileDescriptor,
+ .NOMEM => return error.SystemResources,
+ .NAMETOOLONG => return error.NameTooLong,
+ .INVAL => unreachable, // the value of file actions is invalid
+ else => |err| return unexpectedErrno(err),
+ }
+ }
+
+ pub fn close(self: *Actions, fd: fd_t) Error!void {
+ switch (errno(system.posix_spawn_file_actions_addclose(&self.actions, fd))) {
+ .SUCCESS => return,
+ .BADF => return error.InvalidFileDescriptor,
+ .NOMEM => return error.SystemResources,
+ .INVAL => unreachable, // the value of file actions is invalid
+ .NAMETOOLONG => unreachable,
+ else => |err| return unexpectedErrno(err),
+ }
+ }
+
+ pub fn dup2(self: *Actions, fd: fd_t, newfd: fd_t) Error!void {
+ switch (errno(system.posix_spawn_file_actions_adddup2(&self.actions, fd, newfd))) {
+ .SUCCESS => return,
+ .BADF => return error.InvalidFileDescriptor,
+ .NOMEM => return error.SystemResources,
+ .INVAL => unreachable, // the value of file actions is invalid
+ .NAMETOOLONG => unreachable,
+ else => |err| return unexpectedErrno(err),
+ }
+ }
+
+ pub fn inherit(self: *Actions, fd: fd_t) Error!void {
+ switch (errno(system.posix_spawn_file_actions_addinherit_np(&self.actions, fd))) {
+ .SUCCESS => return,
+ .BADF => return error.InvalidFileDescriptor,
+ .NOMEM => return error.SystemResources,
+ .INVAL => unreachable, // the value of file actions is invalid
+ .NAMETOOLONG => unreachable,
+ else => |err| return unexpectedErrno(err),
+ }
+ }
+
+ pub fn chdir(self: *Actions, path: []const u8) Error!void {
+ const posix_path = try toPosixPath(path);
+ return self.chdirZ(&posix_path);
+ }
+
+ pub fn chdirZ(self: *Actions, path: [*:0]const u8) Error!void {
+ switch (errno(system.posix_spawn_file_actions_addchdir_np(&self.actions, path))) {
+ .SUCCESS => return,
+ .NOMEM => return error.SystemResources,
+ .NAMETOOLONG => return error.NameTooLong,
+ .BADF => unreachable,
+ .INVAL => unreachable, // the value of file actions is invalid
+ else => |err| return unexpectedErrno(err),
+ }
+ }
+
+ pub fn fchdir(self: *Actions, fd: fd_t) Error!void {
+ switch (errno(system.posix_spawn_file_actions_addfchdir_np(&self.actions, fd))) {
+ .SUCCESS => return,
+ .BADF => return error.InvalidFileDescriptor,
+ .NOMEM => return error.SystemResources,
+ .INVAL => unreachable, // the value of file actions is invalid
+ .NAMETOOLONG => unreachable,
+ else => |err| return unexpectedErrno(err),
+ }
+ }
+ };
+
+ pub fn spawn(
+ path: []const u8,
+ actions: ?Actions,
+ attr: ?Attr,
+ argv: [*:null]?[*:0]const u8,
+ envp: [*:null]?[*:0]const u8,
+ ) Error!pid_t {
+ const posix_path = try toPosixPath(path);
+ return spawnZ(&posix_path, actions, attr, argv, envp);
+ }
+
+ pub fn spawnZ(
+ path: [*:0]const u8,
+ actions: ?Actions,
+ attr: ?Attr,
+ argv: [*:null]?[*:0]const u8,
+ envp: [*:null]?[*:0]const u8,
+ ) Error!pid_t {
+ var pid: pid_t = undefined;
+ switch (errno(system.posix_spawn(
+ &pid,
+ path,
+ if (actions) |a| &a.actions else null,
+ if (attr) |a| &a.attr else null,
+ argv,
+ envp,
+ ))) {
+ .SUCCESS => return pid,
+ .@"2BIG" => return error.TooBig,
+ .NOMEM => return error.SystemResources,
+ .BADF => return error.InvalidFileDescriptor,
+ .ACCES => return error.PermissionDenied,
+ .IO => return error.InputOutput,
+ .LOOP => return error.FileSystem,
+ .NAMETOOLONG => return error.NameTooLong,
+ .NOENT => return error.FileNotFound,
+ .NOEXEC => return error.InvalidExe,
+ .NOTDIR => return error.NotDir,
+ .TXTBSY => return error.FileBusy,
+ .BADARCH => return error.InvalidExe,
+ .BADEXEC => return error.InvalidExe,
+ .FAULT => unreachable,
+ .INVAL => unreachable,
+ else => |err| return unexpectedErrno(err),
+ }
+ }
+
+ pub fn spawnp(
+ file: []const u8,
+ actions: ?Actions,
+ attr: ?Attr,
+ argv: [*:null]?[*:0]const u8,
+ envp: [*:null]?[*:0]const u8,
+ ) Error!pid_t {
+ const posix_file = try toPosixPath(file);
+ return spawnpZ(&posix_file, actions, attr, argv, envp);
+ }
+
+ pub fn spawnpZ(
+ file: [*:0]const u8,
+ actions: ?Actions,
+ attr: ?Attr,
+ argv: [*:null]?[*:0]const u8,
+ envp: [*:null]?[*:0]const u8,
+ ) Error!pid_t {
+ var pid: pid_t = undefined;
+ switch (errno(system.posix_spawnp(
+ &pid,
+ file,
+ if (actions) |a| &a.actions else null,
+ if (attr) |a| &a.attr else null,
+ argv,
+ envp,
+ ))) {
+ .SUCCESS => return pid,
+ .@"2BIG" => return error.TooBig,
+ .NOMEM => return error.SystemResources,
+ .BADF => return error.InvalidFileDescriptor,
+ .ACCES => return error.PermissionDenied,
+ .IO => return error.InputOutput,
+ .LOOP => return error.FileSystem,
+ .NAMETOOLONG => return error.NameTooLong,
+ .NOENT => return error.FileNotFound,
+ .NOEXEC => return error.InvalidExe,
+ .NOTDIR => return error.NotDir,
+ .TXTBSY => return error.FileBusy,
+ .BADARCH => return error.InvalidExe,
+ .BADEXEC => return error.InvalidExe,
+ .FAULT => unreachable,
+ .INVAL => unreachable,
+ else => |err| return unexpectedErrno(err),
+ }
+ }
+
+ /// Use this version of the `waitpid` wrapper if you spawned your child process using `posix_spawn`
+ /// or `posix_spawnp` syscalls.
+ /// See also `std.os.waitpid` for an alternative if your child process was spawned via `fork` and
+ /// `execve` method.
+ pub fn waitpid(pid: pid_t, flags: u32) Error!WaitPidResult {
+ const Status = if (builtin.link_libc) c_int else u32;
+ var status: Status = undefined;
+ while (true) {
+ const rc = system.waitpid(pid, &status, if (builtin.link_libc) @intCast(c_int, flags) else flags);
+ switch (errno(rc)) {
+ .SUCCESS => return WaitPidResult{
+ .pid = @intCast(pid_t, rc),
+ .status = @bitCast(u32, status),
+ },
+ .INTR => continue,
+ .CHILD => return error.ChildExecFailed,
+ .INVAL => unreachable, // Invalid flags.
+ else => unreachable,
+ }
+ }
+ }
+} else struct {};
lib/std/build.zig
@@ -1321,6 +1321,7 @@ pub const Builder = struct {
error.FileNotFound => error.PkgConfigNotInstalled,
error.InvalidName => error.PkgConfigNotInstalled,
error.PkgConfigInvalidOutput => error.PkgConfigInvalidOutput,
+ error.ChildExecFailed => error.PkgConfigFailed,
else => return err,
};
self.pkg_config_pkg_list = result;
@@ -1963,6 +1964,7 @@ pub const LibExeObjStep = struct {
error.ExecNotSupported => return error.PkgConfigFailed,
error.ExitCodeFailure => return error.PkgConfigFailed,
error.FileNotFound => return error.PkgConfigNotInstalled,
+ error.ChildExecFailed => return error.PkgConfigFailed,
else => return err,
};
var it = mem.tokenize(u8, stdout, " \r\n\t");
lib/std/child_process.zig
@@ -53,10 +53,13 @@ pub const ChildProcess = struct {
/// Once that is done, `cwd` will be deprecated in favor of this field.
cwd_dir: ?fs.Dir = null,
- err_pipe: if (builtin.os.tag == .windows) void else [2]os.fd_t,
+ err_pipe: ?if (builtin.os.tag == .windows) void else [2]os.fd_t,
expand_arg0: Arg0Expand,
+ /// Darwin-only. Disable ASLR for the child process.
+ disable_aslr: bool = false,
+
pub const Arg0Expand = os.Arg0Expand;
pub const SpawnError = error{
@@ -72,7 +75,13 @@ pub const ChildProcess = struct {
/// Windows-only. `cwd` was provided, but the path did not exist when spawning the child process.
CurrentWorkingDirectoryUnlinked,
- } || os.ExecveError || os.SetIdError || os.ChangeCurDirError || windows.CreateProcessError || windows.WaitForSingleObjectError;
+ } ||
+ os.ExecveError ||
+ os.SetIdError ||
+ os.ChangeCurDirError ||
+ windows.CreateProcessError ||
+ windows.WaitForSingleObjectError ||
+ os.posix_spawn.Error;
pub const Term = union(enum) {
Exited: u8,
@@ -98,7 +107,7 @@ pub const ChildProcess = struct {
.pid = undefined,
.handle = undefined,
.thread_handle = undefined,
- .err_pipe = undefined,
+ .err_pipe = null,
.term = null,
.env_map = null,
.cwd = null,
@@ -128,6 +137,10 @@ pub const ChildProcess = struct {
@compileError("the target operating system cannot spawn processes");
}
+ if (comptime builtin.target.isDarwin()) {
+ return self.spawnMacos();
+ }
+
if (builtin.os.tag == .windows) {
return self.spawnWindows();
} else {
@@ -166,7 +179,7 @@ pub const ChildProcess = struct {
return term;
}
try os.kill(self.pid, os.SIG.TERM);
- self.waitUnwrapped();
+ try self.waitUnwrapped();
return self.term.?;
}
@@ -435,7 +448,7 @@ pub const ChildProcess = struct {
return term;
}
- self.waitUnwrapped();
+ try self.waitUnwrapped();
return self.term.?;
}
@@ -461,8 +474,12 @@ pub const ChildProcess = struct {
return result;
}
- fn waitUnwrapped(self: *ChildProcess) void {
- const status = os.waitpid(self.pid, 0).status;
+ fn waitUnwrapped(self: *ChildProcess) !void {
+ const res: os.WaitPidResult = if (comptime builtin.target.isDarwin())
+ try os.posix_spawn.waitpid(self.pid, 0)
+ else
+ os.waitpid(self.pid, 0);
+ const status = res.status;
self.cleanupStreams();
self.handleWaitResult(status);
}
@@ -487,37 +504,39 @@ pub const ChildProcess = struct {
}
fn cleanupAfterWait(self: *ChildProcess, status: u32) !Term {
- defer destroyPipe(self.err_pipe);
-
- if (builtin.os.tag == .linux) {
- var fd = [1]std.os.pollfd{std.os.pollfd{
- .fd = self.err_pipe[0],
- .events = std.os.POLL.IN,
- .revents = undefined,
- }};
-
- // Check if the eventfd buffer stores a non-zero value by polling
- // it, that's the error code returned by the child process.
- _ = std.os.poll(&fd, 0) catch unreachable;
-
- // According to eventfd(2) the descriptro is readable if the counter
- // has a value greater than 0
- if ((fd[0].revents & std.os.POLL.IN) != 0) {
- const err_int = try readIntFd(self.err_pipe[0]);
- return @errSetCast(SpawnError, @intToError(err_int));
- }
- } else {
- // Write maxInt(ErrInt) to the write end of the err_pipe. This is after
- // waitpid, so this write is guaranteed to be after the child
- // pid potentially wrote an error. This way we can do a blocking
- // read on the error pipe and either get maxInt(ErrInt) (no error) or
- // an error code.
- try writeIntFd(self.err_pipe[1], maxInt(ErrInt));
- const err_int = try readIntFd(self.err_pipe[0]);
- // Here we potentially return the fork child's error from the parent
- // pid.
- if (err_int != maxInt(ErrInt)) {
- return @errSetCast(SpawnError, @intToError(err_int));
+ if (self.err_pipe) |err_pipe| {
+ defer destroyPipe(err_pipe);
+
+ if (builtin.os.tag == .linux) {
+ var fd = [1]std.os.pollfd{std.os.pollfd{
+ .fd = err_pipe[0],
+ .events = std.os.POLL.IN,
+ .revents = undefined,
+ }};
+
+ // Check if the eventfd buffer stores a non-zero value by polling
+ // it, that's the error code returned by the child process.
+ _ = std.os.poll(&fd, 0) catch unreachable;
+
+ // According to eventfd(2) the descriptro is readable if the counter
+ // has a value greater than 0
+ if ((fd[0].revents & std.os.POLL.IN) != 0) {
+ const err_int = try readIntFd(err_pipe[0]);
+ return @errSetCast(SpawnError, @intToError(err_int));
+ }
+ } else {
+ // Write maxInt(ErrInt) to the write end of the err_pipe. This is after
+ // waitpid, so this write is guaranteed to be after the child
+ // pid potentially wrote an error. This way we can do a blocking
+ // read on the error pipe and either get maxInt(ErrInt) (no error) or
+ // an error code.
+ try writeIntFd(err_pipe[1], maxInt(ErrInt));
+ const err_int = try readIntFd(err_pipe[0]);
+ // Here we potentially return the fork child's error from the parent
+ // pid.
+ if (err_int != maxInt(ErrInt)) {
+ return @errSetCast(SpawnError, @intToError(err_int));
+ }
}
}
@@ -535,6 +554,114 @@ pub const ChildProcess = struct {
Term{ .Unknown = status };
}
+ fn spawnMacos(self: *ChildProcess) SpawnError!void {
+ const pipe_flags = if (io.is_async) os.O.NONBLOCK else 0;
+ const stdin_pipe = if (self.stdin_behavior == StdIo.Pipe) try os.pipe2(pipe_flags) else undefined;
+ errdefer if (self.stdin_behavior == StdIo.Pipe) destroyPipe(stdin_pipe);
+
+ const stdout_pipe = if (self.stdout_behavior == StdIo.Pipe) try os.pipe2(pipe_flags) else undefined;
+ errdefer if (self.stdout_behavior == StdIo.Pipe) destroyPipe(stdout_pipe);
+
+ const stderr_pipe = if (self.stderr_behavior == StdIo.Pipe) try os.pipe2(pipe_flags) else undefined;
+ errdefer if (self.stderr_behavior == StdIo.Pipe) destroyPipe(stderr_pipe);
+
+ const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore);
+ const dev_null_fd = if (any_ignore)
+ os.openZ("/dev/null", os.O.RDWR, 0) catch |err| switch (err) {
+ error.PathAlreadyExists => unreachable,
+ error.NoSpaceLeft => unreachable,
+ error.FileTooBig => unreachable,
+ error.DeviceBusy => unreachable,
+ error.FileLocksNotSupported => unreachable,
+ error.BadPathName => unreachable, // Windows-only
+ error.InvalidHandle => unreachable, // WASI-only
+ error.WouldBlock => unreachable,
+ else => |e| return e,
+ }
+ else
+ undefined;
+ defer if (any_ignore) os.close(dev_null_fd);
+
+ var attr = try os.posix_spawn.Attr.init();
+ defer attr.deinit();
+ var flags: u16 = os.darwin.POSIX_SPAWN_SETSIGDEF | os.darwin.POSIX_SPAWN_SETSIGMASK;
+ if (self.disable_aslr) {
+ flags |= os.darwin._POSIX_SPAWN_DISABLE_ASLR;
+ }
+ try attr.set(flags);
+
+ var actions = try os.posix_spawn.Actions.init();
+ defer actions.deinit();
+
+ try setUpChildIoPosixSpawn(self.stdin_behavior, &actions, stdin_pipe[0], os.STDIN_FILENO, dev_null_fd);
+ try setUpChildIoPosixSpawn(self.stdout_behavior, &actions, stdout_pipe[1], os.STDOUT_FILENO, dev_null_fd);
+ try setUpChildIoPosixSpawn(self.stderr_behavior, &actions, stderr_pipe[1], os.STDERR_FILENO, dev_null_fd);
+
+ if (self.cwd_dir) |cwd| {
+ try actions.fchdir(cwd.fd);
+ } else if (self.cwd) |cwd| {
+ try actions.chdir(cwd);
+ }
+
+ var arena_allocator = std.heap.ArenaAllocator.init(self.allocator);
+ defer arena_allocator.deinit();
+ const arena = arena_allocator.allocator();
+
+ const argv_buf = try arena.allocSentinel(?[*:0]u8, self.argv.len, null);
+ for (self.argv) |arg, i| argv_buf[i] = (try arena.dupeZ(u8, arg)).ptr;
+
+ const envp = if (self.env_map) |env_map| m: {
+ const envp_buf = try createNullDelimitedEnvMap(arena, env_map);
+ break :m envp_buf.ptr;
+ } else std.c.environ;
+
+ const pid = try os.posix_spawn.spawnp(self.argv[0], actions, attr, argv_buf, envp);
+
+ if (self.stdin_behavior == StdIo.Pipe) {
+ self.stdin = File{ .handle = stdin_pipe[1] };
+ } else {
+ self.stdin = null;
+ }
+ if (self.stdout_behavior == StdIo.Pipe) {
+ self.stdout = File{ .handle = stdout_pipe[0] };
+ } else {
+ self.stdout = null;
+ }
+ if (self.stderr_behavior == StdIo.Pipe) {
+ self.stderr = File{ .handle = stderr_pipe[0] };
+ } else {
+ self.stderr = null;
+ }
+
+ self.pid = pid;
+ self.term = null;
+
+ if (self.stdin_behavior == StdIo.Pipe) {
+ os.close(stdin_pipe[0]);
+ }
+ if (self.stdout_behavior == StdIo.Pipe) {
+ os.close(stdout_pipe[1]);
+ }
+ if (self.stderr_behavior == StdIo.Pipe) {
+ os.close(stderr_pipe[1]);
+ }
+ }
+
+ fn setUpChildIoPosixSpawn(
+ stdio: StdIo,
+ actions: *os.posix_spawn.Actions,
+ pipe_fd: i32,
+ std_fileno: i32,
+ dev_null_fd: i32,
+ ) !void {
+ switch (stdio) {
+ .Pipe => try actions.dup2(pipe_fd, std_fileno),
+ .Close => try actions.close(std_fileno),
+ .Inherit => {},
+ .Ignore => try actions.dup2(dev_null_fd, std_fileno),
+ }
+ }
+
fn spawnPosix(self: *ChildProcess) SpawnError!void {
const pipe_flags = if (io.is_async) os.O.NONBLOCK else 0;
const stdin_pipe = if (self.stdin_behavior == StdIo.Pipe) try os.pipe2(pipe_flags) else undefined;
lib/std/os.zig
@@ -41,6 +41,7 @@ pub const plan9 = @import("os/plan9.zig");
pub const uefi = @import("os/uefi.zig");
pub const wasi = @import("os/wasi.zig");
pub const windows = @import("os/windows.zig");
+pub const posix_spawn = @import("os/posix_spawn.zig");
comptime {
assert(@import("std") == std); // std lib tests require --zig-lib-dir
@@ -52,6 +53,7 @@ test {
_ = uefi;
_ = wasi;
_ = windows;
+ _ = posix_spawn;
_ = @import("os/test.zig");
}
@@ -4037,6 +4039,9 @@ pub const WaitPidResult = struct {
status: u32,
};
+/// Use this version of the `waitpid` wrapper if you spawned your child process using explicit
+/// `fork` and `execve` method. If you spawned your child process using `posix_spawn` method,
+/// use `std.os.posix_spawn.waitpid` instead.
pub fn waitpid(pid: pid_t, flags: u32) WaitPidResult {
const Status = if (builtin.link_libc) c_int else u32;
var status: Status = undefined;
CMakeLists.txt
@@ -467,6 +467,7 @@ set(ZIG_STAGE2_SOURCES
"${CMAKE_SOURCE_DIR}/lib/std/os/linux.zig"
"${CMAKE_SOURCE_DIR}/lib/std/os/linux/io_uring.zig"
"${CMAKE_SOURCE_DIR}/lib/std/os/linux/x86_64.zig"
+ "${CMAKE_SOURCE_DIR}/lib/std/os/posix_spawn.zig"
"${CMAKE_SOURCE_DIR}/lib/std/os/windows.zig"
"${CMAKE_SOURCE_DIR}/lib/std/os/windows/ntstatus.zig"
"${CMAKE_SOURCE_DIR}/lib/std/os/windows/win32error.zig"