| ... | @@ -1456,8 +1456,8 @@ pub fn io(t: *Threaded) Io { | ... | @@ -1456,8 +1456,8 @@ pub fn io(t: *Threaded) Io { |
| 1456 | .processReplacePath = processReplacePath, | 1456 | .processReplacePath = processReplacePath, |
| 1457 | .processSpawn = processSpawn, | 1457 | .processSpawn = processSpawn, |
| 1458 | .processSpawnPath = processSpawnPath, | 1458 | .processSpawnPath = processSpawnPath, |
| 1459 | .childWait = childWait, // TODO audit for cancelation and unreachable | 1459 | .childWait = childWait, |
| 1460 | .childKill = childKill, // TODO audit for cancelation and unreachable | 1460 | .childKill = childKill, |
| 1461 | | 1461 | |
| 1462 | .progressParentFile = progressParentFile, | 1462 | .progressParentFile = progressParentFile, |
| 1463 | | 1463 | |
| ... | @@ -11853,38 +11853,7 @@ fn processSetCurrentDir(userdata: ?*anyopaque, dir: Dir) process.SetCurrentDirEr | ... | @@ -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; | 11856 | return fchdir(dir.handle); |
| 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 | } | | |
| 11888 | } | 11857 | } |
| 11889 | | 11858 | |
| 11890 | pub const PosixAddress = extern union { | 11859 | pub const PosixAddress = extern union { |
| ... | @@ -12960,57 +12929,64 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp | ... | @@ -12960,57 +12929,64 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp |
| 12960 | }; | 12929 | }; |
| 12961 | | 12930 | |
| 12962 | if (pid_result == 0) { | 12931 | if (pid_result == 0) { |
| 12963 | // We are the child. | 12932 | defer comptime unreachable; // We are the child. |
| 12964 | if (Thread.current) |current_thread| current_thread.cancel_protection = .blocked; | 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); | 12936 | setUpChildIo(options.stdin, stdin_pipe[0], posix.STDIN_FILENO, dev_null_fd) catch |err| forkBail(ep1, err); |
| 12967 | setUpChildIo(options.stdout, stdout_pipe[1], posix.STDOUT_FILENO, dev_null_fd) catch |err| forkBail(err_pipe[1], err); | 12937 | setUpChildIo(options.stdout, stdout_pipe[1], posix.STDOUT_FILENO, dev_null_fd) catch |err| forkBail(ep1, err); |
| 12968 | setUpChildIo(options.stderr, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) catch |err| forkBail(err_pipe[1], err); | 12938 | setUpChildIo(options.stderr, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) catch |err| forkBail(ep1, err); |
| 12969 | | 12939 | |
| 12970 | if (options.cwd_dir) |cwd| { | 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 | } else if (options.cwd) |cwd| { | 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 | // Must happen after fchdir above, the cwd file descriptor might be | 12946 | // Must happen after fchdir above, the cwd file descriptor might be |
| 12977 | // equal to prog_fileno and be clobbered by this dup2 call. | 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 | if (options.gid) |gid| { | 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 | if (options.uid) |uid| { | 12960 | if (options.uid) |uid| { |
| 12985 | switch (posix.errno(posix.system.setreuid(uid, uid))) { | 12961 | switch (posix.errno(posix.system.setreuid(uid, uid))) { |
| 12986 | .SUCCESS => {}, | 12962 | .SUCCESS => {}, |
| 12987 | .AGAIN => forkBail(err_pipe[1], error.ResourceLimitReached), | 12963 | .AGAIN => forkBail(ep1, error.ResourceLimitReached), |
| 12988 | .INVAL => forkBail(err_pipe[1], error.InvalidUserId), | 12964 | .INVAL => forkBail(ep1, error.InvalidUserId), |
| 12989 | .PERM => forkBail(err_pipe[1], error.PermissionDenied), | 12965 | .PERM => forkBail(ep1, error.PermissionDenied), |
| 12990 | else => forkBail(err_pipe[1], error.Unexpected), | 12966 | else => forkBail(ep1, error.Unexpected), |
| 12991 | } | 12967 | } |
| 12992 | } | 12968 | } |
| 12993 | | 12969 | |
| 12994 | if (options.pgid) |pid| { | 12970 | if (options.pgid) |pid| { |
| 12995 | switch (posix.errno(posix.system.setpgid(0, pid))) { | 12971 | switch (posix.errno(posix.system.setpgid(0, pid))) { |
| 12996 | .SUCCESS => {}, | 12972 | .SUCCESS => {}, |
| 12997 | .ACCES => forkBail(err_pipe[1], error.ProcessAlreadyExec), | 12973 | .ACCES => forkBail(ep1, error.ProcessAlreadyExec), |
| 12998 | .INVAL => forkBail(err_pipe[1], error.InvalidProcessGroupId), | 12974 | .INVAL => forkBail(ep1, error.InvalidProcessGroupId), |
| 12999 | .PERM => forkBail(err_pipe[1], error.PermissionDenied), | 12975 | .PERM => forkBail(ep1, error.PermissionDenied), |
| 13000 | else => forkBail(err_pipe[1], error.Unexpected), | 12976 | else => forkBail(ep1, error.Unexpected), |
| 13001 | } | 12977 | } |
| 13002 | } | 12978 | } |
| 13003 | | 12979 | |
| 13004 | if (options.start_suspended) { | 12980 | if (options.start_suspended) { |
| 13005 | switch (posix.errno(posix.system.kill(posix.system.getpid(), .STOP))) { | 12981 | switch (posix.errno(posix.system.kill(posix.system.getpid(), .STOP))) { |
| 13006 | .SUCCESS => {}, | 12982 | .SUCCESS => {}, |
| 13007 | .PERM => forkBail(err_pipe[1], error.PermissionDenied), | 12983 | .PERM => forkBail(ep1, error.PermissionDenied), |
| 13008 | else => forkBail(err_pipe[1], error.Unexpected), | 12984 | else => forkBail(ep1, error.Unexpected), |
| 13009 | } | 12985 | } |
| 13010 | } | 12986 | } |
| 13011 | | 12987 | |
| 13012 | const err = posixExecv(options.expand_arg0, argv_buf.ptr[0].?, argv_buf.ptr, envp, PATH); | 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 | const pid: posix.pid_t = @intCast(pid_result); // We are the parent. | 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,9 +13026,10 @@ fn getDevNullFd(t: *Threaded) !posix.fd_t { |
| 13050 | defer t.mutex.unlock(); | 13026 | defer t.mutex.unlock(); |
| 13051 | if (t.null_file.fd != -1) return t.null_file.fd; | 13027 | if (t.null_file.fd != -1) return t.null_file.fd; |
| 13052 | } | 13028 | } |
| | 13029 | const mode: u32 = 0; |
| 13053 | const syscall: Syscall = try .start(); | 13030 | const syscall: Syscall = try .start(); |
| 13054 | while (true) { | 13031 | while (true) { |
| 13055 | const rc = open_sym("/dev/null", .{ .ACCMODE = .RDWR }, 0); | 13032 | const rc = open_sym("/dev/null", .{ .ACCMODE = .RDWR }, mode); |
| 13056 | switch (posix.errno(rc)) { | 13033 | switch (posix.errno(rc)) { |
| 13057 | .SUCCESS => { | 13034 | .SUCCESS => { |
| 13058 | syscall.finish(); | 13035 | syscall.finish(); |
| ... | @@ -13089,10 +13066,15 @@ fn processSpawnPosix(userdata: ?*anyopaque, options: process.SpawnOptions) proce | ... | @@ -13089,10 +13066,15 @@ fn processSpawnPosix(userdata: ?*anyopaque, options: process.SpawnOptions) proce |
| 13089 | defer posix.close(spawned.err_fd); | 13066 | defer posix.close(spawned.err_fd); |
| 13090 | | 13067 | |
| 13091 | // Wait for the child to report any errors in or before `execvpe`. | 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 | const child_err: process.SpawnError = @errorCast(@errorFromInt(child_err_int)); | 13070 | const child_err: process.SpawnError = @errorCast(@errorFromInt(child_err_int)); |
| 13094 | return child_err; | 13071 | return child_err; |
| 13095 | } else |read_err| switch (read_err) { | 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 | error.EndOfStream => { | 13078 | error.EndOfStream => { |
| 13097 | // Write end closed by CLOEXEC at the time of the `execvpe` call, | 13079 | // Write end closed by CLOEXEC at the time of the `execvpe` call, |
| 13098 | // indicating success. | 13080 | // indicating success. |
| ... | @@ -13159,7 +13141,7 @@ fn childKillWindows(t: *Threaded, child: *process.Child, exit_code: windows.UINT | ... | @@ -13159,7 +13141,7 @@ fn childKillWindows(t: *Threaded, child: *process.Child, exit_code: windows.UINT |
| 13159 | fn childWaitWindows(child: *process.Child) process.Child.WaitError!process.Child.Term { | 13141 | fn childWaitWindows(child: *process.Child) process.Child.WaitError!process.Child.Term { |
| 13160 | const handle = child.id.?; | 13142 | const handle = child.id.?; |
| 13161 | | 13143 | |
| 13162 | var syscall: Syscall = try .start(); | 13144 | const syscall: Syscall = try .start(); |
| 13163 | while (true) switch (windows.kernel32.WaitForSingleObjectEx(handle, windows.INFINITE, windows.FALSE)) { | 13145 | while (true) switch (windows.kernel32.WaitForSingleObjectEx(handle, windows.INFINITE, windows.FALSE)) { |
| 13164 | windows.WAIT_OBJECT_0 => break syscall.finish(), | 13146 | windows.WAIT_OBJECT_0 => break syscall.finish(), |
| 13165 | windows.WAIT_ABANDONED, windows.WAIT_TIMEOUT => { | 13147 | windows.WAIT_ABANDONED, windows.WAIT_TIMEOUT => { |
| ... | @@ -13393,21 +13375,25 @@ fn writeIntFd(fd: posix.fd_t, value: ErrInt) !void { | ... | @@ -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 { | 13378 | fn readIntFd(fd: posix.fd_t) !ErrInt { |
| 13397 | _ = t; // TODO cancelation | | |
| 13398 | var buffer: [8]u8 = undefined; | 13379 | var buffer: [8]u8 = undefined; |
| 13399 | var i: usize = 0; | 13380 | var i: usize = 0; |
| | 13381 | const syscall: Syscall = try .start(); |
| 13400 | while (true) { | 13382 | while (true) { |
| 13401 | const rc = posix.system.read(fd, buffer[i..].ptr, buffer.len - i); | 13383 | const rc = posix.system.read(fd, buffer[i..].ptr, buffer.len - i); |
| 13402 | switch (posix.errno(rc)) { | 13384 | switch (posix.errno(rc)) { |
| 13403 | .SUCCESS => { | 13385 | .SUCCESS => { |
| | 13386 | syscall.finish(); |
| 13404 | const n: usize = @intCast(rc); | 13387 | const n: usize = @intCast(rc); |
| 13405 | if (n == 0) break; | 13388 | if (n == 0) break; |
| 13406 | i += n; | 13389 | i += n; |
| 13407 | continue; | 13390 | continue; |
| 13408 | }, | 13391 | }, |
| 13409 | .INTR => continue, | 13392 | .INTR => { |
| 13410 | else => |err| return posix.unexpectedErrno(err), | 13393 | try syscall.checkCancel(); |
| | 13394 | continue; |
| | 13395 | }, |
| | 13396 | else => |err| return syscall.unexpectedErrno(err), |
| 13411 | } | 13397 | } |
| 13412 | } | 13398 | } |
| 13413 | if (buffer.len - i != 0) return error.EndOfStream; | 13399 | if (buffer.len - i != 0) return error.EndOfStream; |
| ... | @@ -13423,10 +13409,10 @@ fn destroyPipe(pipe: [2]posix.fd_t) void { | ... | @@ -13423,10 +13409,10 @@ fn destroyPipe(pipe: [2]posix.fd_t) void { |
| 13423 | | 13409 | |
| 13424 | fn setUpChildIo(stdio: process.SpawnOptions.StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) !void { | 13410 | fn setUpChildIo(stdio: process.SpawnOptions.StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) !void { |
| 13425 | switch (stdio) { | 13411 | switch (stdio) { |
| 13426 | .pipe => try posix.dup2(pipe_fd, std_fileno), | 13412 | .pipe => try dup2(pipe_fd, std_fileno), |
| 13427 | .close => posix.close(std_fileno), | 13413 | .close => posix.close(std_fileno), |
| 13428 | .inherit => {}, | 13414 | .inherit => {}, |
| 13429 | .ignore => try posix.dup2(dev_null_fd, std_fileno), | 13415 | .ignore => try dup2(dev_null_fd, std_fileno), |
| 13430 | .file => @panic("TODO implement setUpChildIo when file is used"), | 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,3 +15275,81 @@ pub fn pipe2(flags: posix.O) PipeError![2]posix.fd_t { |
| 15289 | | 15275 | |
| 15290 | return fds; | 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 | } |