master
  1const errno = std.posix.errno;
  2const unexpectedErrno = std.posix.unexpectedErrno;
  3
  4pub const Error = error{
  5    SystemResources,
  6    InvalidFileDescriptor,
  7    NameTooLong,
  8    TooBig,
  9    AccessDenied,
 10    PermissionDenied,
 11    InputOutput,
 12    FileSystem,
 13    FileNotFound,
 14    InvalidExe,
 15    NotDir,
 16    FileBusy,
 17    /// Returned when the child fails to execute either in the pre-exec() initialization step, or
 18    /// when exec(3) is invoked.
 19    ChildExecFailed,
 20} || std.posix.UnexpectedError;
 21
 22pub const Attr = struct {
 23    attr: std.c.posix_spawnattr_t,
 24
 25    pub fn init() Error!Attr {
 26        var attr: std.c.posix_spawnattr_t = undefined;
 27        switch (errno(std.c.posix_spawnattr_init(&attr))) {
 28            .SUCCESS => return Attr{ .attr = attr },
 29            .NOMEM => return error.SystemResources,
 30            .INVAL => unreachable,
 31            else => |err| return unexpectedErrno(err),
 32        }
 33    }
 34
 35    pub fn deinit(self: *Attr) void {
 36        defer self.* = undefined;
 37        switch (errno(std.c.posix_spawnattr_destroy(&self.attr))) {
 38            .SUCCESS => return,
 39            .INVAL => unreachable, // Invalid parameters.
 40            else => unreachable,
 41        }
 42    }
 43
 44    pub fn get(self: Attr) Error!std.c.POSIX_SPAWN {
 45        var flags: std.c.POSIX_SPAWN = undefined;
 46        switch (errno(std.c.posix_spawnattr_getflags(&self.attr, &flags))) {
 47            .SUCCESS => return flags,
 48            .INVAL => unreachable,
 49            else => |err| return unexpectedErrno(err),
 50        }
 51    }
 52
 53    pub fn set(self: *Attr, flags: std.c.POSIX_SPAWN) Error!void {
 54        switch (errno(std.c.posix_spawnattr_setflags(&self.attr, flags))) {
 55            .SUCCESS => return,
 56            .INVAL => unreachable,
 57            else => |err| return unexpectedErrno(err),
 58        }
 59    }
 60};
 61
 62pub const Actions = struct {
 63    actions: std.c.posix_spawn_file_actions_t,
 64
 65    pub fn init() Error!Actions {
 66        var actions: std.c.posix_spawn_file_actions_t = undefined;
 67        switch (errno(std.c.posix_spawn_file_actions_init(&actions))) {
 68            .SUCCESS => return Actions{ .actions = actions },
 69            .NOMEM => return error.SystemResources,
 70            .INVAL => unreachable,
 71            else => |err| return unexpectedErrno(err),
 72        }
 73    }
 74
 75    pub fn deinit(self: *Actions) void {
 76        defer self.* = undefined;
 77        switch (errno(std.c.posix_spawn_file_actions_destroy(&self.actions))) {
 78            .SUCCESS => return,
 79            .INVAL => unreachable, // Invalid parameters.
 80            else => unreachable,
 81        }
 82    }
 83
 84    pub fn open(self: *Actions, fd: std.c.fd_t, path: []const u8, flags: u32, mode: std.c.mode_t) Error!void {
 85        const posix_path = try std.posix.toPosixPath(path);
 86        return self.openZ(fd, &posix_path, flags, mode);
 87    }
 88
 89    pub fn openZ(self: *Actions, fd: std.c.fd_t, path: [*:0]const u8, flags: u32, mode: std.c.mode_t) Error!void {
 90        switch (errno(std.c.posix_spawn_file_actions_addopen(&self.actions, fd, path, @as(c_int, @bitCast(flags)), mode))) {
 91            .SUCCESS => return,
 92            .BADF => return error.InvalidFileDescriptor,
 93            .NOMEM => return error.SystemResources,
 94            .NAMETOOLONG => return error.NameTooLong,
 95            .INVAL => unreachable, // the value of file actions is invalid
 96            else => |err| return unexpectedErrno(err),
 97        }
 98    }
 99
100    pub fn close(self: *Actions, fd: std.c.fd_t) Error!void {
101        switch (errno(std.c.posix_spawn_file_actions_addclose(&self.actions, fd))) {
102            .SUCCESS => return,
103            .BADF => return error.InvalidFileDescriptor,
104            .NOMEM => return error.SystemResources,
105            .INVAL => unreachable, // the value of file actions is invalid
106            .NAMETOOLONG => unreachable,
107            else => |err| return unexpectedErrno(err),
108        }
109    }
110
111    pub fn dup2(self: *Actions, fd: std.c.fd_t, newfd: std.c.fd_t) Error!void {
112        switch (errno(std.c.posix_spawn_file_actions_adddup2(&self.actions, fd, newfd))) {
113            .SUCCESS => return,
114            .BADF => return error.InvalidFileDescriptor,
115            .NOMEM => return error.SystemResources,
116            .INVAL => unreachable, // the value of file actions is invalid
117            .NAMETOOLONG => unreachable,
118            else => |err| return unexpectedErrno(err),
119        }
120    }
121
122    pub fn inherit(self: *Actions, fd: std.c.fd_t) Error!void {
123        switch (errno(std.c.posix_spawn_file_actions_addinherit_np(&self.actions, fd))) {
124            .SUCCESS => return,
125            .BADF => return error.InvalidFileDescriptor,
126            .NOMEM => return error.SystemResources,
127            .INVAL => unreachable, // the value of file actions is invalid
128            .NAMETOOLONG => unreachable,
129            else => |err| return unexpectedErrno(err),
130        }
131    }
132
133    pub fn chdir(self: *Actions, path: []const u8) Error!void {
134        const posix_path = try std.posix.toPosixPath(path);
135        return self.chdirZ(&posix_path);
136    }
137
138    pub fn chdirZ(self: *Actions, path: [*:0]const u8) Error!void {
139        switch (errno(std.c.posix_spawn_file_actions_addchdir_np(&self.actions, path))) {
140            .SUCCESS => return,
141            .NOMEM => return error.SystemResources,
142            .NAMETOOLONG => return error.NameTooLong,
143            .BADF => unreachable,
144            .INVAL => unreachable, // the value of file actions is invalid
145            else => |err| return unexpectedErrno(err),
146        }
147    }
148
149    pub fn fchdir(self: *Actions, fd: std.c.fd_t) Error!void {
150        switch (errno(std.c.posix_spawn_file_actions_addfchdir_np(&self.actions, fd))) {
151            .SUCCESS => return,
152            .BADF => return error.InvalidFileDescriptor,
153            .NOMEM => return error.SystemResources,
154            .INVAL => unreachable, // the value of file actions is invalid
155            .NAMETOOLONG => unreachable,
156            else => |err| return unexpectedErrno(err),
157        }
158    }
159};
160
161pub fn spawn(
162    path: []const u8,
163    actions: ?Actions,
164    attr: ?Attr,
165    argv: [*:null]const ?[*:0]const u8,
166    envp: [*:null]const ?[*:0]const u8,
167) Error!std.c.pid_t {
168    const posix_path = try std.posix.toPosixPath(path);
169    return spawnZ(&posix_path, actions, attr, argv, envp);
170}
171
172pub fn spawnZ(
173    path: [*:0]const u8,
174    actions: ?Actions,
175    attr: ?Attr,
176    argv: [*:null]const ?[*:0]const u8,
177    envp: [*:null]const ?[*:0]const u8,
178) Error!std.c.pid_t {
179    var pid: std.c.pid_t = undefined;
180    switch (errno(std.c.posix_spawn(
181        &pid,
182        path,
183        if (actions) |a| &a.actions else null,
184        if (attr) |a| &a.attr else null,
185        argv,
186        envp,
187    ))) {
188        .SUCCESS => return pid,
189        .@"2BIG" => return error.TooBig,
190        .NOMEM => return error.SystemResources,
191        .BADF => return error.InvalidFileDescriptor,
192        .ACCES => return error.AccessDenied,
193        .IO => return error.InputOutput,
194        .LOOP => return error.FileSystem,
195        .NAMETOOLONG => return error.NameTooLong,
196        .NOENT => return error.FileNotFound,
197        .NOEXEC => return error.InvalidExe,
198        .NOTDIR => return error.NotDir,
199        .TXTBSY => return error.FileBusy,
200        .BADARCH => return error.InvalidExe,
201        .BADEXEC => return error.InvalidExe,
202        .FAULT => unreachable,
203        .INVAL => unreachable,
204        else => |err| return unexpectedErrno(err),
205    }
206}
207
208pub fn waitpid(pid: std.c.pid_t, flags: u32) Error!std.posix.WaitPidResult {
209    var status: c_int = undefined;
210    while (true) {
211        const rc = waitpid(pid, &status, @as(c_int, @intCast(flags)));
212        switch (errno(rc)) {
213            .SUCCESS => return std.posix.WaitPidResult{
214                .pid = @as(std.c.pid_t, @intCast(rc)),
215                .status = @as(u32, @bitCast(status)),
216            },
217            .INTR => continue,
218            .CHILD => return error.ChildExecFailed,
219            .INVAL => unreachable, // Invalid flags.
220            else => unreachable,
221        }
222    }
223}
224
225const std = @import("std");