| author | |
| committer | |
| log | 3169f0529b22fca2a387a841a62fec29b3d2096a |
| tree | 11680a5f5e95f6b5cd32436f37ebb5ec68541bfc |
| parent | 904f414e7eab7bc0f7ea00f616831bfc3c1f18a4 |
Today I found out that posix_spawn is trash. It's actually implemented
on top of fork/exec inside of libc (or libSystem in the case of macOS).
So, anything posix_spawn can do, we can do better. In particular, what
we can do better is handle spawning of child processes that are
potentially foreign binaries. If you try to spawn a wasm binary, for
example, posix spawn does the following:
* Goes ahead and creates a child process.
* The child process writes "foo.wasm: foo.wasm: cannot execute binary file"
to stderr (yes, it prints the filename twice).
* The child process then exits with code 126.
This behavior is indistinguishable from the binary being successfully
spawned, and then printing to stderr, and exiting with a failure -
something that is an extremely common occurrence.
Meanwhile, using the lower level fork/exec will simply return ENOEXEC
code from the execve syscall (which is mapped to zig error.InvalidExe).
The posix_spawn behavior means the zig build runner can't tell the
difference between a failure to run a foreign binary, and a binary that
did run, but failed in some other fashion. This is unacceptable, because
attempting to excecve is the proper way to support things like Rosetta.5 files changed, 3 insertions(+), 422 deletions(-)
CMakeLists.txt-1| ... | ... | @@ -303,7 +303,6 @@ set(ZIG_STAGE2_SOURCES |
| 303 | 303 | "${CMAKE_SOURCE_DIR}/lib/std/os/linux.zig" |
| 304 | 304 | "${CMAKE_SOURCE_DIR}/lib/std/os/linux/io_uring.zig" |
| 305 | 305 | "${CMAKE_SOURCE_DIR}/lib/std/os/linux/x86_64.zig" |
| 306 | "${CMAKE_SOURCE_DIR}/lib/std/os/posix_spawn.zig" | |
| 307 | 306 | "${CMAKE_SOURCE_DIR}/lib/std/os/windows.zig" |
| 308 | 307 | "${CMAKE_SOURCE_DIR}/lib/std/os/windows/ntstatus.zig" |
| 309 | 308 | "${CMAKE_SOURCE_DIR}/lib/std/os/windows/win32error.zig" |
lib/std/Build/CompileStep.zig-2| ... | ... | @@ -742,7 +742,6 @@ pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u |
| 742 | 742 | error.ExecNotSupported => return error.PkgConfigFailed, |
| 743 | 743 | error.ExitCodeFailure => return error.PkgConfigFailed, |
| 744 | 744 | error.FileNotFound => return error.PkgConfigNotInstalled, |
| 745 | error.ChildExecFailed => return error.PkgConfigFailed, | |
| 746 | 745 | else => return err, |
| 747 | 746 | }; |
| 748 | 747 | |
| ... | ... | @@ -2042,7 +2041,6 @@ fn getPkgConfigList(self: *std.Build) ![]const PkgConfigPkg { |
| 2042 | 2041 | error.FileNotFound => error.PkgConfigNotInstalled, |
| 2043 | 2042 | error.InvalidName => error.PkgConfigNotInstalled, |
| 2044 | 2043 | error.PkgConfigInvalidOutput => error.PkgConfigInvalidOutput, |
| 2045 | error.ChildExecFailed => error.PkgConfigFailed, | |
| 2046 | 2044 | else => return err, |
| 2047 | 2045 | }; |
| 2048 | 2046 | self.pkg_config_pkg_list = result; |
lib/std/child_process.zig+2-125| ... | ... | @@ -90,8 +90,7 @@ pub const ChildProcess = struct { |
| 90 | 90 | os.SetIdError || |
| 91 | 91 | os.ChangeCurDirError || |
| 92 | 92 | windows.CreateProcessError || |
| 93 | windows.WaitForSingleObjectError || | |
| 94 | os.posix_spawn.Error; | |
| 93 | windows.WaitForSingleObjectError; | |
| 95 | 94 | |
| 96 | 95 | pub const Term = union(enum) { |
| 97 | 96 | Exited: u8, |
| ... | ... | @@ -143,10 +142,6 @@ pub const ChildProcess = struct { |
| 143 | 142 | @compileError("the target operating system cannot spawn processes"); |
| 144 | 143 | } |
| 145 | 144 | |
| 146 | if (comptime builtin.target.isDarwin()) { | |
| 147 | return self.spawnMacos(); | |
| 148 | } | |
| 149 | ||
| 150 | 145 | if (builtin.os.tag == .windows) { |
| 151 | 146 | return self.spawnWindows(); |
| 152 | 147 | } else { |
| ... | ... | @@ -337,10 +332,7 @@ pub const ChildProcess = struct { |
| 337 | 332 | } |
| 338 | 333 | |
| 339 | 334 | fn waitUnwrapped(self: *ChildProcess) !void { |
| 340 | const res: os.WaitPidResult = if (comptime builtin.target.isDarwin()) | |
| 341 | try os.posix_spawn.waitpid(self.id, 0) | |
| 342 | else | |
| 343 | os.waitpid(self.id, 0); | |
| 335 | const res: os.WaitPidResult = os.waitpid(self.id, 0); | |
| 344 | 336 | const status = res.status; |
| 345 | 337 | self.cleanupStreams(); |
| 346 | 338 | self.handleWaitResult(status); |
| ... | ... | @@ -416,121 +408,6 @@ pub const ChildProcess = struct { |
| 416 | 408 | Term{ .Unknown = status }; |
| 417 | 409 | } |
| 418 | 410 | |
| 419 | fn spawnMacos(self: *ChildProcess) SpawnError!void { | |
| 420 | const pipe_flags = if (io.is_async) os.O.NONBLOCK else 0; | |
| 421 | const stdin_pipe = if (self.stdin_behavior == StdIo.Pipe) try os.pipe2(pipe_flags) else undefined; | |
| 422 | errdefer if (self.stdin_behavior == StdIo.Pipe) destroyPipe(stdin_pipe); | |
| 423 | ||
| 424 | const stdout_pipe = if (self.stdout_behavior == StdIo.Pipe) try os.pipe2(pipe_flags) else undefined; | |
| 425 | errdefer if (self.stdout_behavior == StdIo.Pipe) destroyPipe(stdout_pipe); | |
| 426 | ||
| 427 | const stderr_pipe = if (self.stderr_behavior == StdIo.Pipe) try os.pipe2(pipe_flags) else undefined; | |
| 428 | errdefer if (self.stderr_behavior == StdIo.Pipe) destroyPipe(stderr_pipe); | |
| 429 | ||
| 430 | const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore); | |
| 431 | const dev_null_fd = if (any_ignore) | |
| 432 | os.openZ("/dev/null", os.O.RDWR, 0) catch |err| switch (err) { | |
| 433 | error.PathAlreadyExists => unreachable, | |
| 434 | error.NoSpaceLeft => unreachable, | |
| 435 | error.FileTooBig => unreachable, | |
| 436 | error.DeviceBusy => unreachable, | |
| 437 | error.FileLocksNotSupported => unreachable, | |
| 438 | error.BadPathName => unreachable, // Windows-only | |
| 439 | error.InvalidHandle => unreachable, // WASI-only | |
| 440 | error.WouldBlock => unreachable, | |
| 441 | else => |e| return e, | |
| 442 | } | |
| 443 | else | |
| 444 | undefined; | |
| 445 | defer if (any_ignore) os.close(dev_null_fd); | |
| 446 | ||
| 447 | var attr = try os.posix_spawn.Attr.init(); | |
| 448 | defer attr.deinit(); | |
| 449 | var flags: u16 = os.darwin.POSIX_SPAWN_SETSIGDEF | os.darwin.POSIX_SPAWN_SETSIGMASK; | |
| 450 | if (self.disable_aslr) { | |
| 451 | flags |= os.darwin._POSIX_SPAWN_DISABLE_ASLR; | |
| 452 | } | |
| 453 | if (self.start_suspended) { | |
| 454 | flags |= os.darwin.POSIX_SPAWN_START_SUSPENDED; | |
| 455 | } | |
| 456 | try attr.set(flags); | |
| 457 | ||
| 458 | var actions = try os.posix_spawn.Actions.init(); | |
| 459 | defer actions.deinit(); | |
| 460 | ||
| 461 | try setUpChildIoPosixSpawn(self.stdin_behavior, &actions, stdin_pipe, os.STDIN_FILENO, dev_null_fd); | |
| 462 | try setUpChildIoPosixSpawn(self.stdout_behavior, &actions, stdout_pipe, os.STDOUT_FILENO, dev_null_fd); | |
| 463 | try setUpChildIoPosixSpawn(self.stderr_behavior, &actions, stderr_pipe, os.STDERR_FILENO, dev_null_fd); | |
| 464 | ||
| 465 | if (self.cwd_dir) |cwd| { | |
| 466 | try actions.fchdir(cwd.fd); | |
| 467 | } else if (self.cwd) |cwd| { | |
| 468 | try actions.chdir(cwd); | |
| 469 | } | |
| 470 | ||
| 471 | var arena_allocator = std.heap.ArenaAllocator.init(self.allocator); | |
| 472 | defer arena_allocator.deinit(); | |
| 473 | const arena = arena_allocator.allocator(); | |
| 474 | ||
| 475 | const argv_buf = try arena.allocSentinel(?[*:0]u8, self.argv.len, null); | |
| 476 | for (self.argv, 0..) |arg, i| argv_buf[i] = (try arena.dupeZ(u8, arg)).ptr; | |
| 477 | ||
| 478 | const envp = if (self.env_map) |env_map| m: { | |
| 479 | const envp_buf = try createNullDelimitedEnvMap(arena, env_map); | |
| 480 | break :m envp_buf.ptr; | |
| 481 | } else std.c.environ; | |
| 482 | ||
| 483 | const pid = try os.posix_spawn.spawnp(self.argv[0], actions, attr, argv_buf, envp); | |
| 484 | ||
| 485 | if (self.stdin_behavior == StdIo.Pipe) { | |
| 486 | self.stdin = File{ .handle = stdin_pipe[1] }; | |
| 487 | } else { | |
| 488 | self.stdin = null; | |
| 489 | } | |
| 490 | if (self.stdout_behavior == StdIo.Pipe) { | |
| 491 | self.stdout = File{ .handle = stdout_pipe[0] }; | |
| 492 | } else { | |
| 493 | self.stdout = null; | |
| 494 | } | |
| 495 | if (self.stderr_behavior == StdIo.Pipe) { | |
| 496 | self.stderr = File{ .handle = stderr_pipe[0] }; | |
| 497 | } else { | |
| 498 | self.stderr = null; | |
| 499 | } | |
| 500 | ||
| 501 | self.id = pid; | |
| 502 | self.term = null; | |
| 503 | ||
| 504 | if (self.stdin_behavior == StdIo.Pipe) { | |
| 505 | os.close(stdin_pipe[0]); | |
| 506 | } | |
| 507 | if (self.stdout_behavior == StdIo.Pipe) { | |
| 508 | os.close(stdout_pipe[1]); | |
| 509 | } | |
| 510 | if (self.stderr_behavior == StdIo.Pipe) { | |
| 511 | os.close(stderr_pipe[1]); | |
| 512 | } | |
| 513 | } | |
| 514 | ||
| 515 | fn setUpChildIoPosixSpawn( | |
| 516 | stdio: StdIo, | |
| 517 | actions: *os.posix_spawn.Actions, | |
| 518 | pipe_fd: [2]i32, | |
| 519 | std_fileno: i32, | |
| 520 | dev_null_fd: i32, | |
| 521 | ) !void { | |
| 522 | switch (stdio) { | |
| 523 | .Pipe => { | |
| 524 | const idx: usize = if (std_fileno == 0) 0 else 1; | |
| 525 | try actions.dup2(pipe_fd[idx], std_fileno); | |
| 526 | try actions.close(pipe_fd[1 - idx]); | |
| 527 | }, | |
| 528 | .Close => try actions.close(std_fileno), | |
| 529 | .Inherit => {}, | |
| 530 | .Ignore => try actions.dup2(dev_null_fd, std_fileno), | |
| 531 | } | |
| 532 | } | |
| 533 | ||
| 534 | 411 | fn spawnPosix(self: *ChildProcess) SpawnError!void { |
| 535 | 412 | const pipe_flags = if (io.is_async) os.O.NONBLOCK else 0; |
| 536 | 413 | const stdin_pipe = if (self.stdin_behavior == StdIo.Pipe) try os.pipe2(pipe_flags) else undefined; |
lib/std/os.zig+1-4| ... | ... | @@ -41,7 +41,6 @@ pub const plan9 = @import("os/plan9.zig"); |
| 41 | 41 | pub const uefi = @import("os/uefi.zig"); |
| 42 | 42 | pub const wasi = @import("os/wasi.zig"); |
| 43 | 43 | pub const windows = @import("os/windows.zig"); |
| 44 | pub const posix_spawn = @import("os/posix_spawn.zig"); | |
| 45 | 44 | pub const ptrace = @import("os/ptrace.zig"); |
| 46 | 45 | |
| 47 | 46 | comptime { |
| ... | ... | @@ -56,7 +55,6 @@ test { |
| 56 | 55 | } |
| 57 | 56 | _ = wasi; |
| 58 | 57 | _ = windows; |
| 59 | _ = posix_spawn; | |
| 60 | 58 | |
| 61 | 59 | _ = @import("os/test.zig"); |
| 62 | 60 | } |
| ... | ... | @@ -3998,8 +3996,7 @@ pub const WaitPidResult = struct { |
| 3998 | 3996 | }; |
| 3999 | 3997 | |
| 4000 | 3998 | /// Use this version of the `waitpid` wrapper if you spawned your child process using explicit |
| 4001 | /// `fork` and `execve` method. If you spawned your child process using `posix_spawn` method, | |
| 4002 | /// use `std.os.posix_spawn.waitpid` instead. | |
| 3999 | /// `fork` and `execve` method. | |
| 4003 | 4000 | pub fn waitpid(pid: pid_t, flags: u32) WaitPidResult { |
| 4004 | 4001 | const Status = if (builtin.link_libc) c_int else u32; |
| 4005 | 4002 | var status: Status = undefined; |
lib/std/os/posix_spawn.zig deleted-290| ... | ... | @@ -1,290 +0,0 @@ |
| 1 | const std = @import("std"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | ||
| 4 | const os = @import("../os.zig"); | |
| 5 | const system = os.system; | |
| 6 | const errno = system.getErrno; | |
| 7 | const fd_t = system.fd_t; | |
| 8 | const mode_t = system.mode_t; | |
| 9 | const pid_t = system.pid_t; | |
| 10 | const unexpectedErrno = os.unexpectedErrno; | |
| 11 | const UnexpectedError = os.UnexpectedError; | |
| 12 | const toPosixPath = os.toPosixPath; | |
| 13 | const WaitPidResult = os.WaitPidResult; | |
| 14 | ||
| 15 | pub usingnamespace posix_spawn; | |
| 16 | ||
| 17 | pub const Error = error{ | |
| 18 | SystemResources, | |
| 19 | InvalidFileDescriptor, | |
| 20 | NameTooLong, | |
| 21 | TooBig, | |
| 22 | PermissionDenied, | |
| 23 | InputOutput, | |
| 24 | FileSystem, | |
| 25 | FileNotFound, | |
| 26 | InvalidExe, | |
| 27 | NotDir, | |
| 28 | FileBusy, | |
| 29 | ||
| 30 | /// Returned when the child fails to execute either in the pre-exec() initialization step, or | |
| 31 | /// when exec(3) is invoked. | |
| 32 | ChildExecFailed, | |
| 33 | } || UnexpectedError; | |
| 34 | ||
| 35 | const posix_spawn = if (builtin.target.isDarwin()) struct { | |
| 36 | pub const Attr = struct { | |
| 37 | attr: system.posix_spawnattr_t, | |
| 38 | ||
| 39 | pub fn init() Error!Attr { | |
| 40 | var attr: system.posix_spawnattr_t = undefined; | |
| 41 | switch (errno(system.posix_spawnattr_init(&attr))) { | |
| 42 | .SUCCESS => return Attr{ .attr = attr }, | |
| 43 | .NOMEM => return error.SystemResources, | |
| 44 | .INVAL => unreachable, | |
| 45 | else => |err| return unexpectedErrno(err), | |
| 46 | } | |
| 47 | } | |
| 48 | ||
| 49 | pub fn deinit(self: *Attr) void { | |
| 50 | defer self.* = undefined; | |
| 51 | switch (errno(system.posix_spawnattr_destroy(&self.attr))) { | |
| 52 | .SUCCESS => return, | |
| 53 | .INVAL => unreachable, // Invalid parameters. | |
| 54 | else => unreachable, | |
| 55 | } | |
| 56 | } | |
| 57 | ||
| 58 | pub fn get(self: Attr) Error!u16 { | |
| 59 | var flags: c_short = undefined; | |
| 60 | switch (errno(system.posix_spawnattr_getflags(&self.attr, &flags))) { | |
| 61 | .SUCCESS => return @bitCast(u16, flags), | |
| 62 | .INVAL => unreachable, | |
| 63 | else => |err| return unexpectedErrno(err), | |
| 64 | } | |
| 65 | } | |
| 66 | ||
| 67 | pub fn set(self: *Attr, flags: u16) Error!void { | |
| 68 | switch (errno(system.posix_spawnattr_setflags(&self.attr, @bitCast(c_short, flags)))) { | |
| 69 | .SUCCESS => return, | |
| 70 | .INVAL => unreachable, | |
| 71 | else => |err| return unexpectedErrno(err), | |
| 72 | } | |
| 73 | } | |
| 74 | }; | |
| 75 | ||
| 76 | pub const Actions = struct { | |
| 77 | actions: system.posix_spawn_file_actions_t, | |
| 78 | ||
| 79 | pub fn init() Error!Actions { | |
| 80 | var actions: system.posix_spawn_file_actions_t = undefined; | |
| 81 | switch (errno(system.posix_spawn_file_actions_init(&actions))) { | |
| 82 | .SUCCESS => return Actions{ .actions = actions }, | |
| 83 | .NOMEM => return error.SystemResources, | |
| 84 | .INVAL => unreachable, | |
| 85 | else => |err| return unexpectedErrno(err), | |
| 86 | } | |
| 87 | } | |
| 88 | ||
| 89 | pub fn deinit(self: *Actions) void { | |
| 90 | defer self.* = undefined; | |
| 91 | switch (errno(system.posix_spawn_file_actions_destroy(&self.actions))) { | |
| 92 | .SUCCESS => return, | |
| 93 | .INVAL => unreachable, // Invalid parameters. | |
| 94 | else => unreachable, | |
| 95 | } | |
| 96 | } | |
| 97 | ||
| 98 | pub fn open(self: *Actions, fd: fd_t, path: []const u8, flags: u32, mode: mode_t) Error!void { | |
| 99 | const posix_path = try toPosixPath(path); | |
| 100 | return self.openZ(fd, &posix_path, flags, mode); | |
| 101 | } | |
| 102 | ||
| 103 | pub fn openZ(self: *Actions, fd: fd_t, path: [*:0]const u8, flags: u32, mode: mode_t) Error!void { | |
| 104 | switch (errno(system.posix_spawn_file_actions_addopen(&self.actions, fd, path, @bitCast(c_int, flags), mode))) { | |
| 105 | .SUCCESS => return, | |
| 106 | .BADF => return error.InvalidFileDescriptor, | |
| 107 | .NOMEM => return error.SystemResources, | |
| 108 | .NAMETOOLONG => return error.NameTooLong, | |
| 109 | .INVAL => unreachable, // the value of file actions is invalid | |
| 110 | else => |err| return unexpectedErrno(err), | |
| 111 | } | |
| 112 | } | |
| 113 | ||
| 114 | pub fn close(self: *Actions, fd: fd_t) Error!void { | |
| 115 | switch (errno(system.posix_spawn_file_actions_addclose(&self.actions, fd))) { | |
| 116 | .SUCCESS => return, | |
| 117 | .BADF => return error.InvalidFileDescriptor, | |
| 118 | .NOMEM => return error.SystemResources, | |
| 119 | .INVAL => unreachable, // the value of file actions is invalid | |
| 120 | .NAMETOOLONG => unreachable, | |
| 121 | else => |err| return unexpectedErrno(err), | |
| 122 | } | |
| 123 | } | |
| 124 | ||
| 125 | pub fn dup2(self: *Actions, fd: fd_t, newfd: fd_t) Error!void { | |
| 126 | switch (errno(system.posix_spawn_file_actions_adddup2(&self.actions, fd, newfd))) { | |
| 127 | .SUCCESS => return, | |
| 128 | .BADF => return error.InvalidFileDescriptor, | |
| 129 | .NOMEM => return error.SystemResources, | |
| 130 | .INVAL => unreachable, // the value of file actions is invalid | |
| 131 | .NAMETOOLONG => unreachable, | |
| 132 | else => |err| return unexpectedErrno(err), | |
| 133 | } | |
| 134 | } | |
| 135 | ||
| 136 | pub fn inherit(self: *Actions, fd: fd_t) Error!void { | |
| 137 | switch (errno(system.posix_spawn_file_actions_addinherit_np(&self.actions, fd))) { | |
| 138 | .SUCCESS => return, | |
| 139 | .BADF => return error.InvalidFileDescriptor, | |
| 140 | .NOMEM => return error.SystemResources, | |
| 141 | .INVAL => unreachable, // the value of file actions is invalid | |
| 142 | .NAMETOOLONG => unreachable, | |
| 143 | else => |err| return unexpectedErrno(err), | |
| 144 | } | |
| 145 | } | |
| 146 | ||
| 147 | pub fn chdir(self: *Actions, path: []const u8) Error!void { | |
| 148 | const posix_path = try toPosixPath(path); | |
| 149 | return self.chdirZ(&posix_path); | |
| 150 | } | |
| 151 | ||
| 152 | pub fn chdirZ(self: *Actions, path: [*:0]const u8) Error!void { | |
| 153 | switch (errno(system.posix_spawn_file_actions_addchdir_np(&self.actions, path))) { | |
| 154 | .SUCCESS => return, | |
| 155 | .NOMEM => return error.SystemResources, | |
| 156 | .NAMETOOLONG => return error.NameTooLong, | |
| 157 | .BADF => unreachable, | |
| 158 | .INVAL => unreachable, // the value of file actions is invalid | |
| 159 | else => |err| return unexpectedErrno(err), | |
| 160 | } | |
| 161 | } | |
| 162 | ||
| 163 | pub fn fchdir(self: *Actions, fd: fd_t) Error!void { | |
| 164 | switch (errno(system.posix_spawn_file_actions_addfchdir_np(&self.actions, fd))) { | |
| 165 | .SUCCESS => return, | |
| 166 | .BADF => return error.InvalidFileDescriptor, | |
| 167 | .NOMEM => return error.SystemResources, | |
| 168 | .INVAL => unreachable, // the value of file actions is invalid | |
| 169 | .NAMETOOLONG => unreachable, | |
| 170 | else => |err| return unexpectedErrno(err), | |
| 171 | } | |
| 172 | } | |
| 173 | }; | |
| 174 | ||
| 175 | pub fn spawn( | |
| 176 | path: []const u8, | |
| 177 | actions: ?Actions, | |
| 178 | attr: ?Attr, | |
| 179 | argv: [*:null]?[*:0]const u8, | |
| 180 | envp: [*:null]?[*:0]const u8, | |
| 181 | ) Error!pid_t { | |
| 182 | const posix_path = try toPosixPath(path); | |
| 183 | return spawnZ(&posix_path, actions, attr, argv, envp); | |
| 184 | } | |
| 185 | ||
| 186 | pub fn spawnZ( | |
| 187 | path: [*:0]const u8, | |
| 188 | actions: ?Actions, | |
| 189 | attr: ?Attr, | |
| 190 | argv: [*:null]?[*:0]const u8, | |
| 191 | envp: [*:null]?[*:0]const u8, | |
| 192 | ) Error!pid_t { | |
| 193 | var pid: pid_t = undefined; | |
| 194 | switch (errno(system.posix_spawn( | |
| 195 | &pid, | |
| 196 | path, | |
| 197 | if (actions) |a| &a.actions else null, | |
| 198 | if (attr) |a| &a.attr else null, | |
| 199 | argv, | |
| 200 | envp, | |
| 201 | ))) { | |
| 202 | .SUCCESS => return pid, | |
| 203 | .@"2BIG" => return error.TooBig, | |
| 204 | .NOMEM => return error.SystemResources, | |
| 205 | .BADF => return error.InvalidFileDescriptor, | |
| 206 | .ACCES => return error.PermissionDenied, | |
| 207 | .IO => return error.InputOutput, | |
| 208 | .LOOP => return error.FileSystem, | |
| 209 | .NAMETOOLONG => return error.NameTooLong, | |
| 210 | .NOENT => return error.FileNotFound, | |
| 211 | .NOEXEC => return error.InvalidExe, | |
| 212 | .NOTDIR => return error.NotDir, | |
| 213 | .TXTBSY => return error.FileBusy, | |
| 214 | .BADARCH => return error.InvalidExe, | |
| 215 | .BADEXEC => return error.InvalidExe, | |
| 216 | .FAULT => unreachable, | |
| 217 | .INVAL => unreachable, | |
| 218 | else => |err| return unexpectedErrno(err), | |
| 219 | } | |
| 220 | } | |
| 221 | ||
| 222 | pub fn spawnp( | |
| 223 | file: []const u8, | |
| 224 | actions: ?Actions, | |
| 225 | attr: ?Attr, | |
| 226 | argv: [*:null]?[*:0]const u8, | |
| 227 | envp: [*:null]?[*:0]const u8, | |
| 228 | ) Error!pid_t { | |
| 229 | const posix_file = try toPosixPath(file); | |
| 230 | return spawnpZ(&posix_file, actions, attr, argv, envp); | |
| 231 | } | |
| 232 | ||
| 233 | pub fn spawnpZ( | |
| 234 | file: [*:0]const u8, | |
| 235 | actions: ?Actions, | |
| 236 | attr: ?Attr, | |
| 237 | argv: [*:null]?[*:0]const u8, | |
| 238 | envp: [*:null]?[*:0]const u8, | |
| 239 | ) Error!pid_t { | |
| 240 | var pid: pid_t = undefined; | |
| 241 | switch (errno(system.posix_spawnp( | |
| 242 | &pid, | |
| 243 | file, | |
| 244 | if (actions) |a| &a.actions else null, | |
| 245 | if (attr) |a| &a.attr else null, | |
| 246 | argv, | |
| 247 | envp, | |
| 248 | ))) { | |
| 249 | .SUCCESS => return pid, | |
| 250 | .@"2BIG" => return error.TooBig, | |
| 251 | .NOMEM => return error.SystemResources, | |
| 252 | .BADF => return error.InvalidFileDescriptor, | |
| 253 | .ACCES => return error.PermissionDenied, | |
| 254 | .IO => return error.InputOutput, | |
| 255 | .LOOP => return error.FileSystem, | |
| 256 | .NAMETOOLONG => return error.NameTooLong, | |
| 257 | .NOENT => return error.FileNotFound, | |
| 258 | .NOEXEC => return error.InvalidExe, | |
| 259 | .NOTDIR => return error.NotDir, | |
| 260 | .TXTBSY => return error.FileBusy, | |
| 261 | .BADARCH => return error.InvalidExe, | |
| 262 | .BADEXEC => return error.InvalidExe, | |
| 263 | .FAULT => unreachable, | |
| 264 | .INVAL => unreachable, | |
| 265 | else => |err| return unexpectedErrno(err), | |
| 266 | } | |
| 267 | } | |
| 268 | ||
| 269 | /// Use this version of the `waitpid` wrapper if you spawned your child process using `posix_spawn` | |
| 270 | /// or `posix_spawnp` syscalls. | |
| 271 | /// See also `std.os.waitpid` for an alternative if your child process was spawned via `fork` and | |
| 272 | /// `execve` method. | |
| 273 | pub fn waitpid(pid: pid_t, flags: u32) Error!WaitPidResult { | |
| 274 | const Status = if (builtin.link_libc) c_int else u32; | |
| 275 | var status: Status = undefined; | |
| 276 | while (true) { | |
| 277 | const rc = system.waitpid(pid, &status, if (builtin.link_libc) @intCast(c_int, flags) else flags); | |
| 278 | switch (errno(rc)) { | |
| 279 | .SUCCESS => return WaitPidResult{ | |
| 280 | .pid = @intCast(pid_t, rc), | |
| 281 | .status = @bitCast(u32, status), | |
| 282 | }, | |
| 283 | .INTR => continue, | |
| 284 | .CHILD => return error.ChildExecFailed, | |
| 285 | .INVAL => unreachable, // Invalid flags. | |
| 286 | else => unreachable, | |
| 287 | } | |
| 288 | } | |
| 289 | } | |
| 290 | } else struct {}; |