const std = @import("std"); const builtin = @import("builtin"); const os = @import("../os.zig"); const system = os.system; const errno = system.getErrno; const fd_t = system.fd_t; const mode_t = system.mode_t; const pid_t = system.pid_t; const unexpectedErrno = os.unexpectedErrno; const UnexpectedError = os.UnexpectedError; const toPosixPath = os.toPosixPath; const WaitPidResult = os.WaitPidResult; pub usingnamespace posix_spawn; pub const Error = error{ SystemResources, InvalidFileDescriptor, NameTooLong, TooBig, 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, } || UnexpectedError; const posix_spawn = if (builtin.target.isDarwin()) struct { pub const Attr = struct { attr: system.posix_spawnattr_t, pub fn init() Error!Attr { var attr: system.posix_spawnattr_t = undefined; switch (errno(system.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 { system.posix_spawnattr_destroy(&self.attr); self.* = undefined; } pub fn get(self: Attr) Error!u16 { var flags: c_short = undefined; switch (errno(system.posix_spawnattr_getflags(&self.attr, &flags))) { .SUCCESS => return @bitCast(u16, flags), .INVAL => unreachable, else => |err| return unexpectedErrno(err), } } pub fn set(self: *Attr, flags: u16) Error!void { switch (errno(system.posix_spawnattr_setflags(&self.attr, @bitCast(c_short, flags)))) { .SUCCESS => return, .INVAL => unreachable, else => |err| return unexpectedErrno(err), } } }; pub const Actions = struct { actions: system.posix_spawn_file_actions_t, pub fn init() Error!Actions { var actions: system.posix_spawn_file_actions_t = undefined; switch (errno(system.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 { system.posix_spawn_file_actions_destroy(&self.actions); self.* = undefined; } pub fn open(self: *Actions, fd: fd_t, path: []const u8, flags: u32, mode: mode_t) Error!void { const posix_path = try toPosixPath(path); return self.openZ(fd, &posix_path, flags, mode); } pub fn openZ(self: *Actions, fd: fd_t, path: [*:0]const u8, flags: u32, mode: mode_t) Error!void { switch (errno(system.posix_spawn_file_actions_addopen(&self.actions, fd, path, @bitCast(c_int, 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: fd_t) Error!void { switch (errno(system.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: fd_t, newfd: fd_t) Error!void { switch (errno(system.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: fd_t) Error!void { switch (errno(system.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 toPosixPath(path); return self.chdirZ(&posix_path); } pub fn chdirZ(self: *Actions, path: [*:0]const u8) Error!void { switch (errno(system.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: fd_t) Error!void { switch (errno(system.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]?[*:0]const u8, envp: [*:null]?[*:0]const u8, ) Error!pid_t { const posix_path = try toPosixPath(path); return spawnZ(&posix_path, actions, attr, argv, envp); } pub fn spawnZ( path: [*:0]const u8, actions: ?Actions, attr: ?Attr, argv: [*:null]?[*:0]const u8, envp: [*:null]?[*:0]const u8, ) Error!pid_t { var pid: pid_t = undefined; switch (errno(system.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.PermissionDenied, .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 spawnp( file: []const u8, actions: ?Actions, attr: ?Attr, argv: [*:null]?[*:0]const u8, envp: [*:null]?[*:0]const u8, ) Error!pid_t { const posix_file = try toPosixPath(file); return spawnpZ(&posix_file, actions, attr, argv, envp); } pub fn spawnpZ( file: [*:0]const u8, actions: ?Actions, attr: ?Attr, argv: [*:null]?[*:0]const u8, envp: [*:null]?[*:0]const u8, ) Error!pid_t { var pid: pid_t = undefined; switch (errno(system.posix_spawnp( &pid, file, 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.PermissionDenied, .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), } } /// Use this version of the `waitpid` wrapper if you spawned your child process using `posix_spawn` /// or `posix_spawnp` syscalls. /// See also `std.os.waitpid` for an alternative if your child process was spawned via `fork` and /// `execve` method. pub fn waitpid(pid: pid_t, flags: u32) Error!WaitPidResult { const Status = if (builtin.link_libc) c_int else u32; var status: Status = undefined; while (true) { const rc = system.waitpid(pid, &status, if (builtin.link_libc) @intCast(c_int, flags) else flags); switch (errno(rc)) { .SUCCESS => return WaitPidResult{ .pid = @intCast(pid_t, rc), .status = @bitCast(u32, status), }, .INTR => continue, .CHILD => return error.ChildExecFailed, .INVAL => unreachable, // Invalid flags. else => unreachable, } } } } else struct {};