aboutsummaryrefslogtreecommitdiff
path: root/lib/std/child_process.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2024-03-18 22:39:59 -0700
committerAndrew Kelley <andrew@ziglang.org>2024-03-19 11:45:09 -0700
commitcd62005f19ff966d2c42de4daeb9a1e4b644bf76 (patch)
tree4bb316708afaf79c971808df792d8fe86274789b /lib/std/child_process.zig
parent7057bffc14602add697eb566b83934b7ad3fd81c (diff)
downloadzig-cd62005f19ff966d2c42de4daeb9a1e4b644bf76.tar.gz
zig-cd62005f19ff966d2c42de4daeb9a1e4b644bf76.zip
extract std.posix from std.os
closes #5019
Diffstat (limited to 'lib/std/child_process.zig')
-rw-r--r--lib/std/child_process.zig181
1 files changed, 91 insertions, 90 deletions
diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig
index 85fa3d294e..c9db06d40b 100644
--- a/lib/std/child_process.zig
+++ b/lib/std/child_process.zig
@@ -3,30 +3,31 @@ const builtin = @import("builtin");
const unicode = std.unicode;
const io = std.io;
const fs = std.fs;
-const os = std.os;
const process = std.process;
const File = std.fs.File;
-const windows = os.windows;
-const linux = os.linux;
+const windows = std.os.windows;
+const linux = std.os.linux;
+const posix = std.posix;
const mem = std.mem;
const math = std.math;
const debug = std.debug;
const EnvMap = process.EnvMap;
const maxInt = std.math.maxInt;
const assert = std.debug.assert;
+const native_os = builtin.os.tag;
pub const ChildProcess = struct {
- pub const Id = switch (builtin.os.tag) {
+ pub const Id = switch (native_os) {
.windows => windows.HANDLE,
.wasi => void,
- else => os.pid_t,
+ else => posix.pid_t,
};
/// Available after calling `spawn()`. This becomes `undefined` after calling `wait()`.
/// On Windows this is the hProcess.
/// On POSIX this is the pid.
id: Id,
- thread_handle: if (builtin.os.tag == .windows) windows.HANDLE else void,
+ thread_handle: if (native_os == .windows) windows.HANDLE else void,
allocator: mem.Allocator,
@@ -46,10 +47,10 @@ pub const ChildProcess = struct {
stderr_behavior: StdIo,
/// Set to change the user id when spawning the child process.
- uid: if (builtin.os.tag == .windows or builtin.os.tag == .wasi) void else ?os.uid_t,
+ uid: if (native_os == .windows or native_os == .wasi) void else ?posix.uid_t,
/// Set to change the group id when spawning the child process.
- gid: if (builtin.os.tag == .windows or builtin.os.tag == .wasi) void else ?os.gid_t,
+ gid: if (native_os == .windows or native_os == .wasi) void else ?posix.gid_t,
/// Set to change the current working directory when spawning the child process.
cwd: ?[]const u8,
@@ -58,7 +59,7 @@ 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 (native_os == .windows) void else [2]posix.fd_t,
expand_arg0: Arg0Expand,
@@ -87,7 +88,7 @@ pub const ChildProcess = struct {
/// Returns the peak resident set size of the child process, in bytes,
/// if available.
pub inline fn getMaxRss(rus: ResourceUsageStatistics) ?usize {
- switch (builtin.os.tag) {
+ switch (native_os) {
.linux => {
if (rus.rusage) |ru| {
return @as(usize, @intCast(ru.maxrss)) * 1024;
@@ -114,14 +115,14 @@ pub const ChildProcess = struct {
}
}
- const rusage_init = switch (builtin.os.tag) {
- .linux, .macos, .ios => @as(?std.os.rusage, null),
+ const rusage_init = switch (native_os) {
+ .linux, .macos, .ios => @as(?posix.rusage, null),
.windows => @as(?windows.VM_COUNTERS, null),
else => {},
};
};
- pub const Arg0Expand = os.Arg0Expand;
+ pub const Arg0Expand = posix.Arg0Expand;
pub const SpawnError = error{
OutOfMemory,
@@ -136,9 +137,9 @@ 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 ||
+ posix.ExecveError ||
+ posix.SetIdError ||
+ posix.ChangeCurDirError ||
windows.CreateProcessError ||
windows.GetProcessMemoryInfoError ||
windows.WaitForSingleObjectError;
@@ -168,8 +169,8 @@ pub const ChildProcess = struct {
.term = null,
.env_map = null,
.cwd = null,
- .uid = if (builtin.os.tag == .windows or builtin.os.tag == .wasi) {} else null,
- .gid = if (builtin.os.tag == .windows or builtin.os.tag == .wasi) {} else null,
+ .uid = if (native_os == .windows or native_os == .wasi) {} else null,
+ .gid = if (native_os == .windows or native_os == .wasi) {} else null,
.stdin = null,
.stdout = null,
.stderr = null,
@@ -193,7 +194,7 @@ pub const ChildProcess = struct {
@compileError("the target operating system cannot spawn processes");
}
- if (builtin.os.tag == .windows) {
+ if (native_os == .windows) {
return self.spawnWindows();
} else {
return self.spawnPosix();
@@ -207,7 +208,7 @@ pub const ChildProcess = struct {
/// Forcibly terminates child process and then cleans up all resources.
pub fn kill(self: *ChildProcess) !Term {
- if (builtin.os.tag == .windows) {
+ if (native_os == .windows) {
return self.killWindows(1);
} else {
return self.killPosix();
@@ -241,7 +242,7 @@ pub const ChildProcess = struct {
self.cleanupStreams();
return term;
}
- os.kill(self.id, os.SIG.TERM) catch |err| switch (err) {
+ posix.kill(self.id, posix.SIG.TERM) catch |err| switch (err) {
error.ProcessNotFound => return error.AlreadyTerminated,
else => return err,
};
@@ -251,7 +252,7 @@ pub const ChildProcess = struct {
/// Blocks until child process terminates and then cleans up all resources.
pub fn wait(self: *ChildProcess) !Term {
- const term = if (builtin.os.tag == .windows)
+ const term = if (native_os == .windows)
try self.waitWindows()
else
try self.waitPosix();
@@ -318,7 +319,7 @@ pub const ChildProcess = struct {
stderr.* = fifoToOwnedArrayList(poller.fifo(.stderr));
}
- pub const RunError = os.GetCwdError || os.ReadError || SpawnError || os.PollError || error{
+ pub const RunError = posix.GetCwdError || posix.ReadError || SpawnError || posix.PollError || error{
StdoutStreamTooLong,
StderrStreamTooLong,
};
@@ -396,19 +397,19 @@ pub const ChildProcess = struct {
self.resource_usage_statistics.rusage = try windows.GetProcessMemoryInfo(self.id);
}
- os.close(self.id);
- os.close(self.thread_handle);
+ posix.close(self.id);
+ posix.close(self.thread_handle);
self.cleanupStreams();
return result;
}
fn waitUnwrapped(self: *ChildProcess) !void {
- const res: os.WaitPidResult = res: {
+ const res: posix.WaitPidResult = res: {
if (self.request_resource_usage_statistics) {
- switch (builtin.os.tag) {
+ switch (native_os) {
.linux, .macos, .ios => {
- var ru: std.os.rusage = undefined;
- const res = os.wait4(self.id, 0, &ru);
+ var ru: posix.rusage = undefined;
+ const res = posix.wait4(self.id, 0, &ru);
self.resource_usage_statistics.rusage = ru;
break :res res;
},
@@ -416,7 +417,7 @@ pub const ChildProcess = struct {
}
}
- break :res os.waitpid(self.id, 0);
+ break :res posix.waitpid(self.id, 0);
};
const status = res.status;
self.cleanupStreams();
@@ -446,20 +447,20 @@ pub const ChildProcess = struct {
if (self.err_pipe) |err_pipe| {
defer destroyPipe(err_pipe);
- if (builtin.os.tag == .linux) {
- var fd = [1]std.os.pollfd{std.os.pollfd{
+ if (native_os == .linux) {
+ var fd = [1]posix.pollfd{posix.pollfd{
.fd = err_pipe[0],
- .events = std.os.POLL.IN,
+ .events = posix.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;
+ _ = posix.poll(&fd, 0) catch unreachable;
// According to eventfd(2) the descriptor is readable if the counter
// has a value greater than 0
- if ((fd[0].revents & std.os.POLL.IN) != 0) {
+ if ((fd[0].revents & posix.POLL.IN) != 0) {
const err_int = try readIntFd(err_pipe[0]);
return @as(SpawnError, @errorCast(@errorFromInt(err_int)));
}
@@ -483,36 +484,36 @@ pub const ChildProcess = struct {
}
fn statusToTerm(status: u32) Term {
- return if (os.W.IFEXITED(status))
- Term{ .Exited = os.W.EXITSTATUS(status) }
- else if (os.W.IFSIGNALED(status))
- Term{ .Signal = os.W.TERMSIG(status) }
- else if (os.W.IFSTOPPED(status))
- Term{ .Stopped = os.W.STOPSIG(status) }
+ return if (posix.W.IFEXITED(status))
+ Term{ .Exited = posix.W.EXITSTATUS(status) }
+ else if (posix.W.IFSIGNALED(status))
+ Term{ .Signal = posix.W.TERMSIG(status) }
+ else if (posix.W.IFSTOPPED(status))
+ Term{ .Stopped = posix.W.STOPSIG(status) }
else
Term{ .Unknown = status };
}
fn spawnPosix(self: *ChildProcess) SpawnError!void {
- const pipe_flags: os.O = .{};
- const stdin_pipe = if (self.stdin_behavior == StdIo.Pipe) try os.pipe2(pipe_flags) else undefined;
+ const pipe_flags: posix.O = .{};
+ const stdin_pipe = if (self.stdin_behavior == StdIo.Pipe) try posix.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;
+ const stdout_pipe = if (self.stdout_behavior == StdIo.Pipe) try posix.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;
+ const stderr_pipe = if (self.stderr_behavior == StdIo.Pipe) try posix.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", .{ .ACCMODE = .RDWR }, 0) catch |err| switch (err) {
+ posix.openZ("/dev/null", .{ .ACCMODE = .RDWR }, 0) catch |err| switch (err) {
error.PathAlreadyExists => unreachable,
error.NoSpaceLeft => unreachable,
error.FileTooBig => unreachable,
@@ -526,7 +527,7 @@ pub const ChildProcess = struct {
else
undefined;
defer {
- if (any_ignore) os.close(dev_null_fd);
+ if (any_ignore) posix.close(dev_null_fd);
}
var arena_allocator = std.heap.ArenaAllocator.init(self.allocator);
@@ -554,7 +555,7 @@ pub const ChildProcess = struct {
} else if (builtin.output_mode == .Exe) {
// Then we have Zig start code and this works.
// TODO type-safety for null-termination of `os.environ`.
- break :m @as([*:null]const ?[*:0]const u8, @ptrCast(os.environ.ptr));
+ break :m @as([*:null]const ?[*:0]const u8, @ptrCast(std.os.environ.ptr));
} else {
// TODO come up with a solution for this.
@compileError("missing std lib enhancement: ChildProcess implementation has no way to collect the environment variables to forward to the child process");
@@ -564,60 +565,60 @@ pub const ChildProcess = struct {
// This pipe is used to communicate errors between the time of fork
// and execve from the child process to the parent process.
const err_pipe = blk: {
- if (builtin.os.tag == .linux) {
- const fd = try os.eventfd(0, linux.EFD.CLOEXEC);
+ if (native_os == .linux) {
+ const fd = try posix.eventfd(0, linux.EFD.CLOEXEC);
// There's no distinction between the readable and the writeable
// end with eventfd
- break :blk [2]os.fd_t{ fd, fd };
+ break :blk [2]posix.fd_t{ fd, fd };
} else {
- break :blk try os.pipe2(.{ .CLOEXEC = true });
+ break :blk try posix.pipe2(.{ .CLOEXEC = true });
}
};
errdefer destroyPipe(err_pipe);
- const pid_result = try os.fork();
+ const pid_result = try posix.fork();
if (pid_result == 0) {
// we are the child
- setUpChildIo(self.stdin_behavior, stdin_pipe[0], os.STDIN_FILENO, dev_null_fd) catch |err| forkChildErrReport(err_pipe[1], err);
- setUpChildIo(self.stdout_behavior, stdout_pipe[1], os.STDOUT_FILENO, dev_null_fd) catch |err| forkChildErrReport(err_pipe[1], err);
- setUpChildIo(self.stderr_behavior, stderr_pipe[1], os.STDERR_FILENO, dev_null_fd) catch |err| forkChildErrReport(err_pipe[1], err);
+ setUpChildIo(self.stdin_behavior, stdin_pipe[0], posix.STDIN_FILENO, dev_null_fd) catch |err| forkChildErrReport(err_pipe[1], err);
+ setUpChildIo(self.stdout_behavior, stdout_pipe[1], posix.STDOUT_FILENO, dev_null_fd) catch |err| forkChildErrReport(err_pipe[1], err);
+ setUpChildIo(self.stderr_behavior, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) catch |err| forkChildErrReport(err_pipe[1], err);
if (self.stdin_behavior == .Pipe) {
- os.close(stdin_pipe[0]);
- os.close(stdin_pipe[1]);
+ posix.close(stdin_pipe[0]);
+ posix.close(stdin_pipe[1]);
}
if (self.stdout_behavior == .Pipe) {
- os.close(stdout_pipe[0]);
- os.close(stdout_pipe[1]);
+ posix.close(stdout_pipe[0]);
+ posix.close(stdout_pipe[1]);
}
if (self.stderr_behavior == .Pipe) {
- os.close(stderr_pipe[0]);
- os.close(stderr_pipe[1]);
+ posix.close(stderr_pipe[0]);
+ posix.close(stderr_pipe[1]);
}
if (self.cwd_dir) |cwd| {
- os.fchdir(cwd.fd) catch |err| forkChildErrReport(err_pipe[1], err);
+ posix.fchdir(cwd.fd) catch |err| forkChildErrReport(err_pipe[1], err);
} else if (self.cwd) |cwd| {
- os.chdir(cwd) catch |err| forkChildErrReport(err_pipe[1], err);
+ posix.chdir(cwd) catch |err| forkChildErrReport(err_pipe[1], err);
}
if (self.gid) |gid| {
- os.setregid(gid, gid) catch |err| forkChildErrReport(err_pipe[1], err);
+ posix.setregid(gid, gid) catch |err| forkChildErrReport(err_pipe[1], err);
}
if (self.uid) |uid| {
- os.setreuid(uid, uid) catch |err| forkChildErrReport(err_pipe[1], err);
+ posix.setreuid(uid, uid) catch |err| forkChildErrReport(err_pipe[1], err);
}
const err = switch (self.expand_arg0) {
- .expand => os.execvpeZ_expandArg0(.expand, argv_buf.ptr[0].?, argv_buf.ptr, envp),
- .no_expand => os.execvpeZ_expandArg0(.no_expand, argv_buf.ptr[0].?, argv_buf.ptr, envp),
+ .expand => posix.execvpeZ_expandArg0(.expand, argv_buf.ptr[0].?, argv_buf.ptr, envp),
+ .no_expand => posix.execvpeZ_expandArg0(.no_expand, argv_buf.ptr[0].?, argv_buf.ptr, envp),
};
forkChildErrReport(err_pipe[1], err);
}
// we are the parent
- const pid = @as(i32, @intCast(pid_result));
+ const pid: i32 = @intCast(pid_result);
if (self.stdin_behavior == StdIo.Pipe) {
self.stdin = File{ .handle = stdin_pipe[1] };
} else {
@@ -639,13 +640,13 @@ pub const ChildProcess = struct {
self.term = null;
if (self.stdin_behavior == StdIo.Pipe) {
- os.close(stdin_pipe[0]);
+ posix.close(stdin_pipe[0]);
}
if (self.stdout_behavior == StdIo.Pipe) {
- os.close(stdout_pipe[1]);
+ posix.close(stdout_pipe[1]);
}
if (self.stderr_behavior == StdIo.Pipe) {
- os.close(stderr_pipe[1]);
+ posix.close(stderr_pipe[1]);
}
}
@@ -679,7 +680,7 @@ pub const ChildProcess = struct {
else
undefined;
defer {
- if (any_ignore) os.close(nul_handle);
+ if (any_ignore) posix.close(nul_handle);
}
var g_hChildStd_IN_Rd: ?windows.HANDLE = null;
@@ -821,8 +822,8 @@ pub const ChildProcess = struct {
defer self.allocator.free(cmd_line_w);
run: {
- const PATH: [:0]const u16 = std.os.getenvW(unicode.utf8ToUtf16LeStringLiteral("PATH")) orelse &[_:0]u16{};
- const PATHEXT: [:0]const u16 = std.os.getenvW(unicode.utf8ToUtf16LeStringLiteral("PATHEXT")) orelse &[_:0]u16{};
+ const PATH: [:0]const u16 = std.process.getenvW(unicode.utf8ToUtf16LeStringLiteral("PATH")) orelse &[_:0]u16{};
+ const PATHEXT: [:0]const u16 = std.process.getenvW(unicode.utf8ToUtf16LeStringLiteral("PATHEXT")) orelse &[_:0]u16{};
var app_buf = std.ArrayListUnmanaged(u16){};
defer app_buf.deinit(self.allocator);
@@ -905,22 +906,22 @@ pub const ChildProcess = struct {
self.term = null;
if (self.stdin_behavior == StdIo.Pipe) {
- os.close(g_hChildStd_IN_Rd.?);
+ posix.close(g_hChildStd_IN_Rd.?);
}
if (self.stderr_behavior == StdIo.Pipe) {
- os.close(g_hChildStd_ERR_Wr.?);
+ posix.close(g_hChildStd_ERR_Wr.?);
}
if (self.stdout_behavior == StdIo.Pipe) {
- os.close(g_hChildStd_OUT_Wr.?);
+ posix.close(g_hChildStd_OUT_Wr.?);
}
}
fn setUpChildIo(stdio: StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) !void {
switch (stdio) {
- .Pipe => try os.dup2(pipe_fd, std_fileno),
- .Close => os.close(std_fileno),
+ .Pipe => try posix.dup2(pipe_fd, std_fileno),
+ .Close => posix.close(std_fileno),
.Inherit => {},
- .Ignore => try os.dup2(dev_null_fd, std_fileno),
+ .Ignore => try posix.dup2(dev_null_fd, std_fileno),
}
}
};
@@ -987,7 +988,7 @@ fn windowsCreateProcessPathExt(
// This 2048 is arbitrary, we just want it to be large enough to get multiple FILE_DIRECTORY_INFORMATION entries
// returned per NtQueryDirectoryFile call.
- var file_information_buf: [2048]u8 align(@alignOf(os.windows.FILE_DIRECTORY_INFORMATION)) = undefined;
+ var file_information_buf: [2048]u8 align(@alignOf(windows.FILE_DIRECTORY_INFORMATION)) = undefined;
const file_info_maximum_single_entry_size = @sizeOf(windows.FILE_DIRECTORY_INFORMATION) + (windows.NAME_MAX * 2);
if (file_information_buf.len < file_info_maximum_single_entry_size) {
@compileError("file_information_buf must be large enough to contain at least one maximum size FILE_DIRECTORY_INFORMATION entry");
@@ -1391,8 +1392,8 @@ fn testArgvToCommandLineWindows(argv: []const []const u8, expected_cmd_line: []c
}
fn windowsDestroyPipe(rd: ?windows.HANDLE, wr: ?windows.HANDLE) void {
- if (rd) |h| os.close(h);
- if (wr) |h| os.close(h);
+ if (rd) |h| posix.close(h);
+ if (wr) |h| posix.close(h);
}
fn windowsMakePipeIn(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const windows.SECURITY_ATTRIBUTES) !void {
@@ -1443,7 +1444,7 @@ fn windowsMakeAsyncPipe(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *cons
else => |err| return windows.unexpectedError(err),
}
}
- errdefer os.close(read_handle);
+ errdefer posix.close(read_handle);
var sattr_copy = sattr.*;
const write_handle = windows.kernel32.CreateFileW(
@@ -1460,7 +1461,7 @@ fn windowsMakeAsyncPipe(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *cons
else => |err| return windows.unexpectedError(err),
}
}
- errdefer os.close(write_handle);
+ errdefer posix.close(write_handle);
try windows.SetHandleInformation(read_handle, windows.HANDLE_FLAG_INHERIT, 0);
@@ -1468,9 +1469,9 @@ fn windowsMakeAsyncPipe(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *cons
wr.* = write_handle;
}
-fn destroyPipe(pipe: [2]os.fd_t) void {
- os.close(pipe[0]);
- if (pipe[0] != pipe[1]) os.close(pipe[1]);
+fn destroyPipe(pipe: [2]posix.fd_t) void {
+ posix.close(pipe[0]);
+ if (pipe[0] != pipe[1]) posix.close(pipe[1]);
}
// Child of fork calls this to report an error to the fork parent.
@@ -1485,7 +1486,7 @@ fn forkChildErrReport(fd: i32, err: ChildProcess.SpawnError) noreturn {
// The _exit(2) function does nothing but make the exit syscall, unlike exit(3)
std.c._exit(1);
}
- os.exit(1);
+ posix.exit(1);
}
const ErrInt = std.meta.Int(.unsigned, @sizeOf(anyerror) * 8);