diff options
Diffstat (limited to 'lib/std/child_process.zig')
| -rw-r--r-- | lib/std/child_process.zig | 181 |
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); |
