| ... | ... | @@ -1456,8 +1456,8 @@ pub fn io(t: *Threaded) Io { |
| 1456 | 1456 | .processReplacePath = processReplacePath, |
| 1457 | 1457 | .processSpawn = processSpawn, |
| 1458 | 1458 | .processSpawnPath = processSpawnPath, |
| 1459 | | .childWait = childWait, // TODO audit for cancelation and unreachable |
| 1460 | | .childKill = childKill, // TODO audit for cancelation and unreachable |
| 1459 | .childWait = childWait, |
| 1460 | .childKill = childKill, |
| 1461 | 1461 | |
| 1462 | 1462 | .progressParentFile = progressParentFile, |
| 1463 | 1463 | |
| ... | ... | @@ -11853,38 +11853,7 @@ fn processSetCurrentDir(userdata: ?*anyopaque, dir: Dir) process.SetCurrentDirEr |
| 11853 | 11853 | }; |
| 11854 | 11854 | } |
| 11855 | 11855 | |
| 11856 | | if (dir.handle == posix.AT.FDCWD) return; |
| 11857 | | |
| 11858 | | const syscall: Syscall = try .start(); |
| 11859 | | while (true) { |
| 11860 | | switch (posix.errno(posix.system.fchdir(dir.handle))) { |
| 11861 | | .SUCCESS => return syscall.finish(), |
| 11862 | | .INTR => { |
| 11863 | | try syscall.checkCancel(); |
| 11864 | | continue; |
| 11865 | | }, |
| 11866 | | .ACCES => { |
| 11867 | | syscall.finish(); |
| 11868 | | return error.AccessDenied; |
| 11869 | | }, |
| 11870 | | .BADF => |err| { |
| 11871 | | syscall.finish(); |
| 11872 | | return errnoBug(err); |
| 11873 | | }, |
| 11874 | | .NOTDIR => { |
| 11875 | | syscall.finish(); |
| 11876 | | return error.NotDir; |
| 11877 | | }, |
| 11878 | | .IO => { |
| 11879 | | syscall.finish(); |
| 11880 | | return error.FileSystem; |
| 11881 | | }, |
| 11882 | | else => |err| { |
| 11883 | | syscall.finish(); |
| 11884 | | return posix.unexpectedErrno(err); |
| 11885 | | }, |
| 11886 | | } |
| 11887 | | } |
| 11856 | return fchdir(dir.handle); |
| 11888 | 11857 | } |
| 11889 | 11858 | |
| 11890 | 11859 | pub const PosixAddress = extern union { |
| ... | ... | @@ -12960,57 +12929,64 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp |
| 12960 | 12929 | }; |
| 12961 | 12930 | |
| 12962 | 12931 | if (pid_result == 0) { |
| 12963 | | // We are the child. |
| 12932 | defer comptime unreachable; // We are the child. |
| 12964 | 12933 | if (Thread.current) |current_thread| current_thread.cancel_protection = .blocked; |
| 12934 | const ep1 = err_pipe[1]; |
| 12965 | 12935 | |
| 12966 | | setUpChildIo(options.stdin, stdin_pipe[0], posix.STDIN_FILENO, dev_null_fd) catch |err| forkBail(err_pipe[1], err); |
| 12967 | | setUpChildIo(options.stdout, stdout_pipe[1], posix.STDOUT_FILENO, dev_null_fd) catch |err| forkBail(err_pipe[1], err); |
| 12968 | | setUpChildIo(options.stderr, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) catch |err| forkBail(err_pipe[1], err); |
| 12936 | setUpChildIo(options.stdin, stdin_pipe[0], posix.STDIN_FILENO, dev_null_fd) catch |err| forkBail(ep1, err); |
| 12937 | setUpChildIo(options.stdout, stdout_pipe[1], posix.STDOUT_FILENO, dev_null_fd) catch |err| forkBail(ep1, err); |
| 12938 | setUpChildIo(options.stderr, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) catch |err| forkBail(ep1, err); |
| 12969 | 12939 | |
| 12970 | 12940 | if (options.cwd_dir) |cwd| { |
| 12971 | | posix.fchdir(cwd.handle) catch |err| forkBail(err_pipe[1], err); |
| 12941 | fchdir(cwd.handle) catch |err| forkBail(ep1, err); |
| 12972 | 12942 | } else if (options.cwd) |cwd| { |
| 12973 | | posix.chdir(cwd) catch |err| forkBail(err_pipe[1], err); |
| 12943 | chdir(cwd) catch |err| forkBail(ep1, err); |
| 12974 | 12944 | } |
| 12975 | 12945 | |
| 12976 | 12946 | // Must happen after fchdir above, the cwd file descriptor might be |
| 12977 | 12947 | // equal to prog_fileno and be clobbered by this dup2 call. |
| 12978 | | if (prog_pipe[1] != -1) posix.dup2(prog_pipe[1], prog_fileno) catch |err| forkBail(err_pipe[1], err); |
| 12948 | if (prog_pipe[1] != -1) dup2(prog_pipe[1], prog_fileno) catch |err| forkBail(ep1, err); |
| 12979 | 12949 | |
| 12980 | 12950 | if (options.gid) |gid| { |
| 12981 | | posix.setregid(gid, gid) catch |err| forkBail(err_pipe[1], err); |
| 12951 | switch (posix.errno(posix.system.setregid(gid, gid))) { |
| 12952 | .SUCCESS => {}, |
| 12953 | .AGAIN => forkBail(ep1, error.ResourceLimitReached), |
| 12954 | .INVAL => forkBail(ep1, error.InvalidUserId), |
| 12955 | .PERM => forkBail(ep1, error.PermissionDenied), |
| 12956 | else => forkBail(ep1, error.Unexpected), |
| 12957 | } |
| 12982 | 12958 | } |
| 12983 | 12959 | |
| 12984 | 12960 | if (options.uid) |uid| { |
| 12985 | 12961 | switch (posix.errno(posix.system.setreuid(uid, uid))) { |
| 12986 | 12962 | .SUCCESS => {}, |
| 12987 | | .AGAIN => forkBail(err_pipe[1], error.ResourceLimitReached), |
| 12988 | | .INVAL => forkBail(err_pipe[1], error.InvalidUserId), |
| 12989 | | .PERM => forkBail(err_pipe[1], error.PermissionDenied), |
| 12990 | | else => forkBail(err_pipe[1], error.Unexpected), |
| 12963 | .AGAIN => forkBail(ep1, error.ResourceLimitReached), |
| 12964 | .INVAL => forkBail(ep1, error.InvalidUserId), |
| 12965 | .PERM => forkBail(ep1, error.PermissionDenied), |
| 12966 | else => forkBail(ep1, error.Unexpected), |
| 12991 | 12967 | } |
| 12992 | 12968 | } |
| 12993 | 12969 | |
| 12994 | 12970 | if (options.pgid) |pid| { |
| 12995 | 12971 | switch (posix.errno(posix.system.setpgid(0, pid))) { |
| 12996 | 12972 | .SUCCESS => {}, |
| 12997 | | .ACCES => forkBail(err_pipe[1], error.ProcessAlreadyExec), |
| 12998 | | .INVAL => forkBail(err_pipe[1], error.InvalidProcessGroupId), |
| 12999 | | .PERM => forkBail(err_pipe[1], error.PermissionDenied), |
| 13000 | | else => forkBail(err_pipe[1], error.Unexpected), |
| 12973 | .ACCES => forkBail(ep1, error.ProcessAlreadyExec), |
| 12974 | .INVAL => forkBail(ep1, error.InvalidProcessGroupId), |
| 12975 | .PERM => forkBail(ep1, error.PermissionDenied), |
| 12976 | else => forkBail(ep1, error.Unexpected), |
| 13001 | 12977 | } |
| 13002 | 12978 | } |
| 13003 | 12979 | |
| 13004 | 12980 | if (options.start_suspended) { |
| 13005 | 12981 | switch (posix.errno(posix.system.kill(posix.system.getpid(), .STOP))) { |
| 13006 | 12982 | .SUCCESS => {}, |
| 13007 | | .PERM => forkBail(err_pipe[1], error.PermissionDenied), |
| 13008 | | else => forkBail(err_pipe[1], error.Unexpected), |
| 12983 | .PERM => forkBail(ep1, error.PermissionDenied), |
| 12984 | else => forkBail(ep1, error.Unexpected), |
| 13009 | 12985 | } |
| 13010 | 12986 | } |
| 13011 | 12987 | |
| 13012 | 12988 | const err = posixExecv(options.expand_arg0, argv_buf.ptr[0].?, argv_buf.ptr, envp, PATH); |
| 13013 | | forkBail(err_pipe[1], err); |
| 12989 | forkBail(ep1, err); |
| 13014 | 12990 | } |
| 13015 | 12991 | |
| 13016 | 12992 | const pid: posix.pid_t = @intCast(pid_result); // We are the parent. |
| ... | ... | @@ -13050,9 +13026,10 @@ fn getDevNullFd(t: *Threaded) !posix.fd_t { |
| 13050 | 13026 | defer t.mutex.unlock(); |
| 13051 | 13027 | if (t.null_file.fd != -1) return t.null_file.fd; |
| 13052 | 13028 | } |
| 13029 | const mode: u32 = 0; |
| 13053 | 13030 | const syscall: Syscall = try .start(); |
| 13054 | 13031 | while (true) { |
| 13055 | | const rc = open_sym("/dev/null", .{ .ACCMODE = .RDWR }, 0); |
| 13032 | const rc = open_sym("/dev/null", .{ .ACCMODE = .RDWR }, mode); |
| 13056 | 13033 | switch (posix.errno(rc)) { |
| 13057 | 13034 | .SUCCESS => { |
| 13058 | 13035 | syscall.finish(); |
| ... | ... | @@ -13089,10 +13066,15 @@ fn processSpawnPosix(userdata: ?*anyopaque, options: process.SpawnOptions) proce |
| 13089 | 13066 | defer posix.close(spawned.err_fd); |
| 13090 | 13067 | |
| 13091 | 13068 | // Wait for the child to report any errors in or before `execvpe`. |
| 13092 | | if (readIntFd(t, spawned.err_fd)) |child_err_int| { |
| 13069 | if (readIntFd(spawned.err_fd)) |child_err_int| { |
| 13093 | 13070 | const child_err: process.SpawnError = @errorCast(@errorFromInt(child_err_int)); |
| 13094 | 13071 | return child_err; |
| 13095 | 13072 | } else |read_err| switch (read_err) { |
| 13073 | error.Canceled => { |
| 13074 | // We don't want to wait for the error to be reported, but we do |
| 13075 | // need to return the child so that it can be cleaned up. |
| 13076 | recancelInner(); |
| 13077 | }, |
| 13096 | 13078 | error.EndOfStream => { |
| 13097 | 13079 | // Write end closed by CLOEXEC at the time of the `execvpe` call, |
| 13098 | 13080 | // indicating success. |
| ... | ... | @@ -13159,7 +13141,7 @@ fn childKillWindows(t: *Threaded, child: *process.Child, exit_code: windows.UINT |
| 13159 | 13141 | fn childWaitWindows(child: *process.Child) process.Child.WaitError!process.Child.Term { |
| 13160 | 13142 | const handle = child.id.?; |
| 13161 | 13143 | |
| 13162 | | var syscall: Syscall = try .start(); |
| 13144 | const syscall: Syscall = try .start(); |
| 13163 | 13145 | while (true) switch (windows.kernel32.WaitForSingleObjectEx(handle, windows.INFINITE, windows.FALSE)) { |
| 13164 | 13146 | windows.WAIT_OBJECT_0 => break syscall.finish(), |
| 13165 | 13147 | windows.WAIT_ABANDONED, windows.WAIT_TIMEOUT => { |
| ... | ... | @@ -13393,21 +13375,25 @@ fn writeIntFd(fd: posix.fd_t, value: ErrInt) !void { |
| 13393 | 13375 | } |
| 13394 | 13376 | } |
| 13395 | 13377 | |
| 13396 | | fn readIntFd(t: *Threaded, fd: posix.fd_t) !ErrInt { |
| 13397 | | _ = t; // TODO cancelation |
| 13378 | fn readIntFd(fd: posix.fd_t) !ErrInt { |
| 13398 | 13379 | var buffer: [8]u8 = undefined; |
| 13399 | 13380 | var i: usize = 0; |
| 13381 | const syscall: Syscall = try .start(); |
| 13400 | 13382 | while (true) { |
| 13401 | 13383 | const rc = posix.system.read(fd, buffer[i..].ptr, buffer.len - i); |
| 13402 | 13384 | switch (posix.errno(rc)) { |
| 13403 | 13385 | .SUCCESS => { |
| 13386 | syscall.finish(); |
| 13404 | 13387 | const n: usize = @intCast(rc); |
| 13405 | 13388 | if (n == 0) break; |
| 13406 | 13389 | i += n; |
| 13407 | 13390 | continue; |
| 13408 | 13391 | }, |
| 13409 | | .INTR => continue, |
| 13410 | | else => |err| return posix.unexpectedErrno(err), |
| 13392 | .INTR => { |
| 13393 | try syscall.checkCancel(); |
| 13394 | continue; |
| 13395 | }, |
| 13396 | else => |err| return syscall.unexpectedErrno(err), |
| 13411 | 13397 | } |
| 13412 | 13398 | } |
| 13413 | 13399 | if (buffer.len - i != 0) return error.EndOfStream; |
| ... | ... | @@ -13423,10 +13409,10 @@ fn destroyPipe(pipe: [2]posix.fd_t) void { |
| 13423 | 13409 | |
| 13424 | 13410 | fn setUpChildIo(stdio: process.SpawnOptions.StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) !void { |
| 13425 | 13411 | switch (stdio) { |
| 13426 | | .pipe => try posix.dup2(pipe_fd, std_fileno), |
| 13412 | .pipe => try dup2(pipe_fd, std_fileno), |
| 13427 | 13413 | .close => posix.close(std_fileno), |
| 13428 | 13414 | .inherit => {}, |
| 13429 | | .ignore => try posix.dup2(dev_null_fd, std_fileno), |
| 13415 | .ignore => try dup2(dev_null_fd, std_fileno), |
| 13430 | 13416 | .file => @panic("TODO implement setUpChildIo when file is used"), |
| 13431 | 13417 | } |
| 13432 | 13418 | } |
| ... | ... | @@ -15289,3 +15275,81 @@ pub fn pipe2(flags: posix.O) PipeError![2]posix.fd_t { |
| 15289 | 15275 | |
| 15290 | 15276 | return fds; |
| 15291 | 15277 | } |
| 15278 | |
| 15279 | pub const DupError = error{ |
| 15280 | ProcessFdQuotaExceeded, |
| 15281 | SystemResources, |
| 15282 | } || Io.UnexpectedError || Io.Cancelable; |
| 15283 | |
| 15284 | pub fn dup2(old_fd: posix.fd_t, new_fd: posix.fd_t) DupError!void { |
| 15285 | const syscall: Syscall = try .start(); |
| 15286 | while (true) switch (posix.errno(posix.system.dup2(old_fd, new_fd))) { |
| 15287 | .SUCCESS => return syscall.finish(), |
| 15288 | .BUSY, .INTR => { |
| 15289 | try syscall.checkCancel(); |
| 15290 | continue; |
| 15291 | }, |
| 15292 | .INVAL => |err| return syscall.errnoBug(err), // invalid parameters |
| 15293 | .BADF => |err| return syscall.errnoBug(err), // use after free |
| 15294 | .MFILE => return syscall.fail(error.ProcessFdQuotaExceeded), |
| 15295 | .NOMEM => return syscall.fail(error.SystemResources), |
| 15296 | else => |err| return syscall.unexpectedErrno(err), |
| 15297 | }; |
| 15298 | } |
| 15299 | |
| 15300 | pub const FchdirError = error{ |
| 15301 | AccessDenied, |
| 15302 | NotDir, |
| 15303 | FileSystem, |
| 15304 | } || Io.Cancelable || Io.UnexpectedError; |
| 15305 | |
| 15306 | pub fn fchdir(fd: posix.fd_t) FchdirError!void { |
| 15307 | if (fd == posix.AT.FDCWD) return; |
| 15308 | const syscall: Syscall = try .start(); |
| 15309 | while (true) switch (posix.errno(posix.system.fchdir(fd))) { |
| 15310 | .SUCCESS => return syscall.finish(), |
| 15311 | .INTR => { |
| 15312 | try syscall.checkCancel(); |
| 15313 | continue; |
| 15314 | }, |
| 15315 | .ACCES => return syscall.fail(error.AccessDenied), |
| 15316 | .NOTDIR => return syscall.fail(error.NotDir), |
| 15317 | .IO => return syscall.fail(error.FileSystem), |
| 15318 | .BADF => |err| return syscall.errnoBug(err), |
| 15319 | else => |err| return syscall.unexpectedErrno(err), |
| 15320 | }; |
| 15321 | } |
| 15322 | |
| 15323 | pub const ChdirError = error{ |
| 15324 | AccessDenied, |
| 15325 | FileSystem, |
| 15326 | SymLinkLoop, |
| 15327 | NameTooLong, |
| 15328 | FileNotFound, |
| 15329 | SystemResources, |
| 15330 | NotDir, |
| 15331 | BadPathName, |
| 15332 | } || Io.Cancelable || Io.UnexpectedError; |
| 15333 | |
| 15334 | pub fn chdir(dir_path: []const u8) ChdirError!void { |
| 15335 | var path_buffer: [posix.PATH_MAX]u8 = undefined; |
| 15336 | const dir_path_posix = try pathToPosix(dir_path, &path_buffer); |
| 15337 | const syscall: Syscall = try .start(); |
| 15338 | while (true) switch (posix.errno(posix.system.chdir(dir_path_posix))) { |
| 15339 | .SUCCESS => return syscall.finish(), |
| 15340 | .INTR => { |
| 15341 | try syscall.checkCancel(); |
| 15342 | continue; |
| 15343 | }, |
| 15344 | .ACCES => return syscall.fail(error.AccessDenied), |
| 15345 | .IO => return syscall.fail(error.FileSystem), |
| 15346 | .LOOP => return syscall.fail(error.SymLinkLoop), |
| 15347 | .NAMETOOLONG => return syscall.fail(error.NameTooLong), |
| 15348 | .NOENT => return syscall.fail(error.FileNotFound), |
| 15349 | .NOMEM => return syscall.fail(error.SystemResources), |
| 15350 | .NOTDIR => return syscall.fail(error.NotDir), |
| 15351 | .ILSEQ => return syscall.fail(error.BadPathName), |
| 15352 | .FAULT => |err| return syscall.errnoBug(err), |
| 15353 | else => |err| return syscall.unexpectedErrno(err), |
| 15354 | }; |
| 15355 | } |