authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-03-09 19:51:58-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-03-10 15:41:07-05:00
log3169f0529b22fca2a387a841a62fec29b3d2096a
tree11680a5f5e95f6b5cd32436f37ebb5ec68541bfc
parent904f414e7eab7bc0f7ea00f616831bfc3c1f18a4

eliminate posix_spawn from the standard library

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,7 +303,6 @@ set(ZIG_STAGE2_SOURCES
303 "${CMAKE_SOURCE_DIR}/lib/std/os/linux.zig"303 "${CMAKE_SOURCE_DIR}/lib/std/os/linux.zig"
304 "${CMAKE_SOURCE_DIR}/lib/std/os/linux/io_uring.zig"304 "${CMAKE_SOURCE_DIR}/lib/std/os/linux/io_uring.zig"
305 "${CMAKE_SOURCE_DIR}/lib/std/os/linux/x86_64.zig"305 "${CMAKE_SOURCE_DIR}/lib/std/os/linux/x86_64.zig"
306 "${CMAKE_SOURCE_DIR}/lib/std/os/posix_spawn.zig"
307 "${CMAKE_SOURCE_DIR}/lib/std/os/windows.zig"306 "${CMAKE_SOURCE_DIR}/lib/std/os/windows.zig"
308 "${CMAKE_SOURCE_DIR}/lib/std/os/windows/ntstatus.zig"307 "${CMAKE_SOURCE_DIR}/lib/std/os/windows/ntstatus.zig"
309 "${CMAKE_SOURCE_DIR}/lib/std/os/windows/win32error.zig"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,7 +742,6 @@ pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u
742 error.ExecNotSupported => return error.PkgConfigFailed,742 error.ExecNotSupported => return error.PkgConfigFailed,
743 error.ExitCodeFailure => return error.PkgConfigFailed,743 error.ExitCodeFailure => return error.PkgConfigFailed,
744 error.FileNotFound => return error.PkgConfigNotInstalled,744 error.FileNotFound => return error.PkgConfigNotInstalled,
745 error.ChildExecFailed => return error.PkgConfigFailed,
746 else => return err,745 else => return err,
747 };746 };
748747
...@@ -2042,7 +2041,6 @@ fn getPkgConfigList(self: *std.Build) ![]const PkgConfigPkg {...@@ -2042,7 +2041,6 @@ fn getPkgConfigList(self: *std.Build) ![]const PkgConfigPkg {
2042 error.FileNotFound => error.PkgConfigNotInstalled,2041 error.FileNotFound => error.PkgConfigNotInstalled,
2043 error.InvalidName => error.PkgConfigNotInstalled,2042 error.InvalidName => error.PkgConfigNotInstalled,
2044 error.PkgConfigInvalidOutput => error.PkgConfigInvalidOutput,2043 error.PkgConfigInvalidOutput => error.PkgConfigInvalidOutput,
2045 error.ChildExecFailed => error.PkgConfigFailed,
2046 else => return err,2044 else => return err,
2047 };2045 };
2048 self.pkg_config_pkg_list = result;2046 self.pkg_config_pkg_list = result;
lib/std/child_process.zig+2-125
...@@ -90,8 +90,7 @@ pub const ChildProcess = struct {...@@ -90,8 +90,7 @@ pub const ChildProcess = struct {
90 os.SetIdError ||90 os.SetIdError ||
91 os.ChangeCurDirError ||91 os.ChangeCurDirError ||
92 windows.CreateProcessError ||92 windows.CreateProcessError ||
93 windows.WaitForSingleObjectError ||93 windows.WaitForSingleObjectError;
94 os.posix_spawn.Error;
9594
96 pub const Term = union(enum) {95 pub const Term = union(enum) {
97 Exited: u8,96 Exited: u8,
...@@ -143,10 +142,6 @@ pub const ChildProcess = struct {...@@ -143,10 +142,6 @@ pub const ChildProcess = struct {
143 @compileError("the target operating system cannot spawn processes");142 @compileError("the target operating system cannot spawn processes");
144 }143 }
145144
146 if (comptime builtin.target.isDarwin()) {
147 return self.spawnMacos();
148 }
149
150 if (builtin.os.tag == .windows) {145 if (builtin.os.tag == .windows) {
151 return self.spawnWindows();146 return self.spawnWindows();
152 } else {147 } else {
...@@ -337,10 +332,7 @@ pub const ChildProcess = struct {...@@ -337,10 +332,7 @@ pub const ChildProcess = struct {
337 }332 }
338333
339 fn waitUnwrapped(self: *ChildProcess) !void {334 fn waitUnwrapped(self: *ChildProcess) !void {
340 const res: os.WaitPidResult = if (comptime builtin.target.isDarwin())335 const res: os.WaitPidResult = os.waitpid(self.id, 0);
341 try os.posix_spawn.waitpid(self.id, 0)
342 else
343 os.waitpid(self.id, 0);
344 const status = res.status;336 const status = res.status;
345 self.cleanupStreams();337 self.cleanupStreams();
346 self.handleWaitResult(status);338 self.handleWaitResult(status);
...@@ -416,121 +408,6 @@ pub const ChildProcess = struct {...@@ -416,121 +408,6 @@ pub const ChildProcess = struct {
416 Term{ .Unknown = status };408 Term{ .Unknown = status };
417 }409 }
418410
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 fn spawnPosix(self: *ChildProcess) SpawnError!void {411 fn spawnPosix(self: *ChildProcess) SpawnError!void {
535 const pipe_flags = if (io.is_async) os.O.NONBLOCK else 0;412 const pipe_flags = if (io.is_async) os.O.NONBLOCK else 0;
536 const stdin_pipe = if (self.stdin_behavior == StdIo.Pipe) try os.pipe2(pipe_flags) else undefined;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,7 +41,6 @@ pub const plan9 = @import("os/plan9.zig");
41pub const uefi = @import("os/uefi.zig");41pub const uefi = @import("os/uefi.zig");
42pub const wasi = @import("os/wasi.zig");42pub const wasi = @import("os/wasi.zig");
43pub const windows = @import("os/windows.zig");43pub const windows = @import("os/windows.zig");
44pub const posix_spawn = @import("os/posix_spawn.zig");
45pub const ptrace = @import("os/ptrace.zig");44pub const ptrace = @import("os/ptrace.zig");
4645
47comptime {46comptime {
...@@ -56,7 +55,6 @@ test {...@@ -56,7 +55,6 @@ test {
56 }55 }
57 _ = wasi;56 _ = wasi;
58 _ = windows;57 _ = windows;
59 _ = posix_spawn;
6058
61 _ = @import("os/test.zig");59 _ = @import("os/test.zig");
62}60}
...@@ -3998,8 +3996,7 @@ pub const WaitPidResult = struct {...@@ -3998,8 +3996,7 @@ pub const WaitPidResult = struct {
3998};3996};
39993997
4000/// Use this version of the `waitpid` wrapper if you spawned your child process using explicit3998/// 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,3999/// `fork` and `execve` method.
4002/// use `std.os.posix_spawn.waitpid` instead.
4003pub fn waitpid(pid: pid_t, flags: u32) WaitPidResult {4000pub fn waitpid(pid: pid_t, flags: u32) WaitPidResult {
4004 const Status = if (builtin.link_libc) c_int else u32;4001 const Status = if (builtin.link_libc) c_int else u32;
4005 var status: Status = undefined;4002 var status: Status = undefined;
lib/std/os/posix_spawn.zig deleted-290
...@@ -1,290 +0,0 @@
1const std = @import("std");
2const builtin = @import("builtin");
3
4const os = @import("../os.zig");
5const system = os.system;
6const errno = system.getErrno;
7const fd_t = system.fd_t;
8const mode_t = system.mode_t;
9const pid_t = system.pid_t;
10const unexpectedErrno = os.unexpectedErrno;
11const UnexpectedError = os.UnexpectedError;
12const toPosixPath = os.toPosixPath;
13const WaitPidResult = os.WaitPidResult;
14
15pub usingnamespace posix_spawn;
16
17pub 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
35const 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 {};