const errno = std.posix.errno; const unexpectedErrno = std.posix.unexpectedErrno; pub const Error = error{ SystemResources, InvalidFileDescriptor, NameTooLong, TooBig, AccessDenied, PermissionDenied, InputOutput, FileSystem, FileNotFound, InvalidExe, NotDir, FileBusy, /// Returned when the child fails to execute either in the pre-exec() initialization step, or /// when exec(3) is invoked. ChildExecFailed, } || std.posix.UnexpectedError; pub const Attr = struct { attr: std.c.posix_spawnattr_t, pub fn init() Error!Attr { var attr: std.c.posix_spawnattr_t = undefined; switch (errno(std.c.posix_spawnattr_init(&attr))) { .SUCCESS => return Attr{ .attr = attr }, .NOMEM => return error.SystemResources, .INVAL => unreachable, else => |err| return unexpectedErrno(err), } } pub fn deinit(self: *Attr) void { defer self.* = undefined; switch (errno(std.c.posix_spawnattr_destroy(&self.attr))) { .SUCCESS => return, .INVAL => unreachable, // Invalid parameters. else => unreachable, } } pub fn get(self: Attr) Error!std.c.POSIX_SPAWN { var flags: std.c.POSIX_SPAWN = undefined; switch (errno(std.c.posix_spawnattr_getflags(&self.attr, &flags))) { .SUCCESS => return flags, .INVAL => unreachable, else => |err| return unexpectedErrno(err), } } pub fn set(self: *Attr, flags: std.c.POSIX_SPAWN) Error!void { switch (errno(std.c.posix_spawnattr_setflags(&self.attr, flags))) { .SUCCESS => return, .INVAL => unreachable, else => |err| return unexpectedErrno(err), } } }; pub const Actions = struct { actions: std.c.posix_spawn_file_actions_t, pub fn init() Error!Actions { var actions: std.c.posix_spawn_file_actions_t = undefined; switch (errno(std.c.posix_spawn_file_actions_init(&actions))) { .SUCCESS => return Actions{ .actions = actions }, .NOMEM => return error.SystemResources, .INVAL => unreachable, else => |err| return unexpectedErrno(err), } } pub fn deinit(self: *Actions) void { defer self.* = undefined; switch (errno(std.c.posix_spawn_file_actions_destroy(&self.actions))) { .SUCCESS => return, .INVAL => unreachable, // Invalid parameters. else => unreachable, } } pub fn open(self: *Actions, fd: std.c.fd_t, path: []const u8, flags: u32, mode: std.c.mode_t) Error!void { const posix_path = try std.posix.toPosixPath(path); return self.openZ(fd, &posix_path, flags, mode); } pub fn openZ(self: *Actions, fd: std.c.fd_t, path: [*:0]const u8, flags: u32, mode: std.c.mode_t) Error!void { switch (errno(std.c.posix_spawn_file_actions_addopen(&self.actions, fd, path, @as(c_int, @bitCast(flags)), mode))) { .SUCCESS => return, .BADF => return error.InvalidFileDescriptor, .NOMEM => return error.SystemResources, .NAMETOOLONG => return error.NameTooLong, .INVAL => unreachable, // the value of file actions is invalid else => |err| return unexpectedErrno(err), } } pub fn close(self: *Actions, fd: std.c.fd_t) Error!void { switch (errno(std.c.posix_spawn_file_actions_addclose(&self.actions, fd))) { .SUCCESS => return, .BADF => return error.InvalidFileDescriptor, .NOMEM => return error.SystemResources, .INVAL => unreachable, // the value of file actions is invalid .NAMETOOLONG => unreachable, else => |err| return unexpectedErrno(err), } } pub fn dup2(self: *Actions, fd: std.c.fd_t, newfd: std.c.fd_t) Error!void { switch (errno(std.c.posix_spawn_file_actions_adddup2(&self.actions, fd, newfd))) { .SUCCESS => return, .BADF => return error.InvalidFileDescriptor, .NOMEM => return error.SystemResources, .INVAL => unreachable, // the value of file actions is invalid .NAMETOOLONG => unreachable, else => |err| return unexpectedErrno(err), } } pub fn inherit(self: *Actions, fd: std.c.fd_t) Error!void { switch (errno(std.c.posix_spawn_file_actions_addinherit_np(&self.actions, fd))) { .SUCCESS => return, .BADF => return error.InvalidFileDescriptor, .NOMEM => return error.SystemResources, .INVAL => unreachable, // the value of file actions is invalid .NAMETOOLONG => unreachable, else => |err| return unexpectedErrno(err), } } pub fn chdir(self: *Actions, path: []const u8) Error!void { const posix_path = try std.posix.toPosixPath(path); return self.chdirZ(&posix_path); } pub fn chdirZ(self: *Actions, path: [*:0]const u8) Error!void { switch (errno(std.c.posix_spawn_file_actions_addchdir_np(&self.actions, path))) { .SUCCESS => return, .NOMEM => return error.SystemResources, .NAMETOOLONG => return error.NameTooLong, .BADF => unreachable, .INVAL => unreachable, // the value of file actions is invalid else => |err| return unexpectedErrno(err), } } pub fn fchdir(self: *Actions, fd: std.c.fd_t) Error!void { switch (errno(std.c.posix_spawn_file_actions_addfchdir_np(&self.actions, fd))) { .SUCCESS => return, .BADF => return error.InvalidFileDescriptor, .NOMEM => return error.SystemResources, .INVAL => unreachable, // the value of file actions is invalid .NAMETOOLONG => unreachable, else => |err| return unexpectedErrno(err), } } }; pub fn spawn( path: []const u8, actions: ?Actions, attr: ?Attr, argv: [*:null]const ?[*:0]const u8, envp: [*:null]const ?[*:0]const u8, ) Error!std.c.pid_t { const posix_path = try std.posix.toPosixPath(path); return spawnZ(&posix_path, actions, attr, argv, envp); } pub fn spawnZ( path: [*:0]const u8, actions: ?Actions, attr: ?Attr, argv: [*:null]const ?[*:0]const u8, envp: [*:null]const ?[*:0]const u8, ) Error!std.c.pid_t { var pid: std.c.pid_t = undefined; switch (errno(std.c.posix_spawn( &pid, path, if (actions) |a| &a.actions else null, if (attr) |a| &a.attr else null, argv, envp, ))) { .SUCCESS => return pid, .@"2BIG" => return error.TooBig, .NOMEM => return error.SystemResources, .BADF => return error.InvalidFileDescriptor, .ACCES => return error.AccessDenied, .IO => return error.InputOutput, .LOOP => return error.FileSystem, .NAMETOOLONG => return error.NameTooLong, .NOENT => return error.FileNotFound, .NOEXEC => return error.InvalidExe, .NOTDIR => return error.NotDir, .TXTBSY => return error.FileBusy, .BADARCH => return error.InvalidExe, .BADEXEC => return error.InvalidExe, .FAULT => unreachable, .INVAL => unreachable, else => |err| return unexpectedErrno(err), } } pub fn waitpid(pid: std.c.pid_t, flags: u32) Error!std.posix.WaitPidResult { var status: c_int = undefined; while (true) { const rc = waitpid(pid, &status, @as(c_int, @intCast(flags))); switch (errno(rc)) { .SUCCESS => return std.posix.WaitPidResult{ .pid = @as(std.c.pid_t, @intCast(rc)), .status = @as(u32, @bitCast(status)), }, .INTR => continue, .CHILD => return error.ChildExecFailed, .INVAL => unreachable, // Invalid flags. else => unreachable, } } } const std = @import("std");