| ... | ... | @@ -756,13 +756,13 @@ pub fn io(ev: *Evented) Io { |
| 756 | 756 | .processSetCurrentDir = processSetCurrentDir, |
| 757 | 757 | .processSetCurrentPath = processSetCurrentPath, |
| 758 | 758 | .processReplace = processReplace, |
| 759 | | .processReplacePath = processReplacePath, |
| 760 | 759 | .processSpawn = processSpawn, |
| 761 | | .processSpawnPath = processSpawnPath, |
| 762 | 760 | .childWait = childWait, |
| 763 | 761 | .childKill = childKill, |
| 764 | 762 | |
| 765 | 763 | .progressParentFile = progressParentFile, |
| 764 | .inheritParentDir = inheritParentDir, |
| 765 | .inheritParentFile = inheritParentFile, |
| 766 | 766 | |
| 767 | 767 | .now = now, |
| 768 | 768 | .clockResolution = clockResolution, |
| ... | ... | @@ -803,7 +803,7 @@ pub const InitOptions = struct { |
| 803 | 803 | argv0: Argv0 = .empty, |
| 804 | 804 | /// Affects the following operations: |
| 805 | 805 | /// * `fileIsTty` |
| 806 | | /// * `processSpawn`, `processSpawnPath`, `processReplace`, `processReplacePath` |
| 806 | /// * `processSpawn`, `processReplace` |
| 807 | 807 | environ: process.Environ = .empty, |
| 808 | 808 | }; |
| 809 | 809 | |
| ... | ... | @@ -4265,19 +4265,32 @@ fn processReplace(userdata: ?*anyopaque, options: process.ReplaceOptions) proces |
| 4265 | 4265 | |
| 4266 | 4266 | var sync: CancelRegion.Sync = try .init(ev); |
| 4267 | 4267 | defer sync.deinit(ev); |
| 4268 | | return execv(&sync, options.expand_arg0, argv_buf.ptr[0].?, argv_buf.ptr, env_block, PATH); |
| 4269 | | } |
| 4270 | 4268 | |
| 4271 | | fn processReplacePath( |
| 4272 | | userdata: ?*anyopaque, |
| 4273 | | dir: Dir, |
| 4274 | | options: process.ReplaceOptions, |
| 4275 | | ) process.ReplaceError { |
| 4276 | | const ev: *Evented = @ptrCast(@alignCast(userdata)); |
| 4277 | | _ = ev; |
| 4278 | | _ = dir; |
| 4279 | | _ = options; |
| 4280 | | @panic("TODO processReplacePath"); |
| 4269 | switch (options.cwd) { |
| 4270 | .inherit => {}, |
| 4271 | .dir => |cwd_dir| fchdir(&sync, cwd_dir.handle) catch |err| switch (err) { |
| 4272 | error.NoDevice, error.UnrecognizedVolume => return error.FileSystem, |
| 4273 | else => |e| return e, |
| 4274 | }, |
| 4275 | .path => |cwd_path| { |
| 4276 | var cwd_path_buffer: [PATH_MAX]u8 = undefined; |
| 4277 | const cwd_path_posix = try pathToPosix(cwd_path, &cwd_path_buffer); |
| 4278 | chdir(&sync, cwd_path_posix) catch |err| switch (err) { |
| 4279 | error.SymLinkLoop => return error.FileSystem, |
| 4280 | else => |e| return e, |
| 4281 | }; |
| 4282 | }, |
| 4283 | } |
| 4284 | for (options.inherit_dirs) |dir| try setFdFlags(&sync, dir.handle, 0); |
| 4285 | for (options.inherit_files) |file| try setFdFlags(&sync, file.handle, 0); |
| 4286 | if (options.start_suspended) { |
| 4287 | switch (linux.errno(linux.kill(linux.getpid(), .STOP))) { |
| 4288 | .SUCCESS => {}, |
| 4289 | .PERM => return error.PermissionDenied, |
| 4290 | else => return error.Unexpected, |
| 4291 | } |
| 4292 | } |
| 4293 | return processExec(&sync, options.exe, argv_buf.ptr, options.expand_arg0, env_block, PATH); |
| 4281 | 4294 | } |
| 4282 | 4295 | |
| 4283 | 4296 | fn processSpawn(userdata: ?*anyopaque, options: process.SpawnOptions) process.SpawnError!process.Child { |
| ... | ... | @@ -4314,18 +4327,6 @@ fn processSpawn(userdata: ?*anyopaque, options: process.SpawnOptions) process.Sp |
| 4314 | 4327 | return child_err; |
| 4315 | 4328 | } |
| 4316 | 4329 | |
| 4317 | | fn processSpawnPath( |
| 4318 | | userdata: ?*anyopaque, |
| 4319 | | dir: Dir, |
| 4320 | | options: process.SpawnOptions, |
| 4321 | | ) process.SpawnError!process.Child { |
| 4322 | | const ev: *Evented = @ptrCast(@alignCast(userdata)); |
| 4323 | | _ = ev; |
| 4324 | | _ = dir; |
| 4325 | | _ = options; |
| 4326 | | @panic("TODO processSpawnPath"); |
| 4327 | | } |
| 4328 | | |
| 4329 | 4330 | const prog_fileno = @max(linux.STDIN_FILENO, linux.STDOUT_FILENO, linux.STDERR_FILENO); |
| 4330 | 4331 | |
| 4331 | 4332 | const Spawned = struct { |
| ... | ... | @@ -4541,6 +4542,9 @@ fn setUpChild(sync: *CancelRegion.Sync, options: struct { |
| 4541 | 4542 | }, |
| 4542 | 4543 | } |
| 4543 | 4544 | |
| 4545 | for (options.spawn.inherit_dirs) |dir| try setFdFlags(sync, dir.handle, 0); |
| 4546 | for (options.spawn.inherit_files) |file| try setFdFlags(sync, file.handle, 0); |
| 4547 | |
| 4544 | 4548 | // Must happen after fchdir above, the cwd file descriptor might be |
| 4545 | 4549 | // equal to prog_fileno and be clobbered by this dup2 call. |
| 4546 | 4550 | if (options.prog_pipe != -1) try dup2(sync, options.prog_pipe, prog_fileno); |
| ... | ... | @@ -4583,11 +4587,11 @@ fn setUpChild(sync: *CancelRegion.Sync, options: struct { |
| 4583 | 4587 | } |
| 4584 | 4588 | } |
| 4585 | 4589 | |
| 4586 | | return execv( |
| 4590 | return processExec( |
| 4587 | 4591 | sync, |
| 4588 | | options.spawn.expand_arg0, |
| 4589 | | options.argv_buf.ptr[0].?, |
| 4592 | options.spawn.exe, |
| 4590 | 4593 | options.argv_buf.ptr, |
| 4594 | options.spawn.expand_arg0, |
| 4591 | 4595 | options.env_block, |
| 4592 | 4596 | options.PATH, |
| 4593 | 4597 | ); |
| ... | ... | @@ -4609,6 +4613,17 @@ fn setUpChildIo( |
| 4609 | 4613 | } |
| 4610 | 4614 | } |
| 4611 | 4615 | |
| 4616 | fn setFdFlags(sync: *CancelRegion.Sync, fd: fd_t, flags: usize) (Io.Cancelable || Io.UnexpectedError)!void { |
| 4617 | while (true) { |
| 4618 | try sync.cancel_region.await(.nothing); |
| 4619 | switch (linux.errno(linux.fcntl(fd, linux.F.SETFD, flags))) { |
| 4620 | .SUCCESS => return, |
| 4621 | .INTR => {}, |
| 4622 | else => |err| return unexpectedErrno(err), |
| 4623 | } |
| 4624 | } |
| 4625 | } |
| 4626 | |
| 4612 | 4627 | pub const DupError = error{ |
| 4613 | 4628 | ProcessFdQuotaExceeded, |
| 4614 | 4629 | SystemResources, |
| ... | ... | @@ -4628,63 +4643,77 @@ pub fn dup2(sync: *CancelRegion.Sync, old_fd: fd_t, new_fd: fd_t) DupError!void |
| 4628 | 4643 | } |
| 4629 | 4644 | } |
| 4630 | 4645 | |
| 4631 | | fn execv( |
| 4646 | fn processExec( |
| 4632 | 4647 | sync: *CancelRegion.Sync, |
| 4633 | | arg0_expand: process.ArgExpansion, |
| 4634 | | file: [*:0]const u8, |
| 4635 | | child_argv: [*:null]?[*:0]const u8, |
| 4648 | exe: process.ReplaceOptions.Exe, |
| 4649 | argv: [*:null]?[*:0]const u8, |
| 4650 | expand_arg0: process.ArgExpansion, |
| 4636 | 4651 | env_block: process.Environ.PosixBlock, |
| 4637 | 4652 | PATH: []const u8, |
| 4638 | 4653 | ) process.ReplaceError { |
| 4639 | | const file_slice = std.mem.sliceTo(file, 0); |
| 4640 | | if (std.mem.findScalar(u8, file_slice, '/') != null) |
| 4641 | | return execvPath(sync, file, child_argv, env_block); |
| 4642 | | |
| 4643 | | // Use of PATH_MAX here is valid as the path_buf will be passed |
| 4644 | | // directly to the operating system in posixExecvPath. |
| 4645 | | var path_buf: [PATH_MAX]u8 = undefined; |
| 4646 | | var it = std.mem.tokenizeScalar(u8, PATH, ':'); |
| 4647 | | var seen_eacces = false; |
| 4648 | | var err: process.ReplaceError = error.FileNotFound; |
| 4649 | | |
| 4650 | | // In case of expanding arg0 we must put it back if we return with an error. |
| 4651 | | const prev_arg0 = child_argv[0]; |
| 4652 | | defer switch (arg0_expand) { |
| 4653 | | .expand => child_argv[0] = prev_arg0, |
| 4654 | | .no_expand => {}, |
| 4655 | | }; |
| 4656 | | |
| 4657 | | while (it.next()) |search_path| { |
| 4658 | | const path_len = search_path.len + file_slice.len + 1; |
| 4659 | | if (path_buf.len < path_len + 1) return error.NameTooLong; |
| 4660 | | @memcpy(path_buf[0..search_path.len], search_path); |
| 4661 | | path_buf[search_path.len] = '/'; |
| 4662 | | @memcpy(path_buf[search_path.len + 1 ..][0..file_slice.len], file_slice); |
| 4663 | | path_buf[path_len] = 0; |
| 4664 | | const full_path = path_buf[0..path_len :0].ptr; |
| 4665 | | switch (arg0_expand) { |
| 4666 | | .expand => child_argv[0] = full_path, |
| 4667 | | .no_expand => {}, |
| 4668 | | } |
| 4669 | | err = execvPath(sync, full_path, child_argv, env_block); |
| 4670 | | switch (err) { |
| 4671 | | error.AccessDenied => seen_eacces = true, |
| 4672 | | error.FileNotFound, error.NotDir => {}, |
| 4673 | | else => |e| return e, |
| 4674 | | } |
| 4654 | const arg0_slice = std.mem.span(argv[0].?); |
| 4655 | exe: switch (exe) { |
| 4656 | .detect => continue :exe if (std.mem.findScalar(u8, arg0_slice, '/')) |_| .{ |
| 4657 | .path = .cwd(), |
| 4658 | } else .search, |
| 4659 | .search => { |
| 4660 | if (Dir.path.isAbsolute(arg0_slice)) continue :exe .{ .path = .cwd() }; |
| 4661 | var path_buf: [PATH_MAX]u8 = undefined; |
| 4662 | var it = std.mem.tokenizeScalar(u8, PATH, ':'); |
| 4663 | var err: error{ FileNotFound, NotDir } = error.FileNotFound; |
| 4664 | |
| 4665 | while (it.next()) |search_path| { |
| 4666 | const path_len = search_path.len + arg0_slice.len + 1; |
| 4667 | if (path_buf.len < path_len + 1) return error.NameTooLong; |
| 4668 | @memcpy(path_buf[0..search_path.len], search_path); |
| 4669 | path_buf[search_path.len] = Dir.path.sep; |
| 4670 | @memcpy(path_buf[search_path.len + 1 ..][0..arg0_slice.len], arg0_slice); |
| 4671 | path_buf[path_len] = 0; |
| 4672 | const full_path = path_buf[0..path_len :0].ptr; |
| 4673 | switch (expand_arg0) { |
| 4674 | .expand => argv[0] = full_path, |
| 4675 | .no_expand => {}, |
| 4676 | } |
| 4677 | switch (processExecveat(sync, linux.AT.FDCWD, full_path, argv, env_block)) { |
| 4678 | error.AccessDenied => {}, |
| 4679 | error.FileNotFound, error.NotDir => |e| err = e, |
| 4680 | else => |e| return e, |
| 4681 | } |
| 4682 | } |
| 4683 | return err; |
| 4684 | }, |
| 4685 | .path => |dir| return processExecveat(sync, dir.handle, arg0_slice, argv, env_block), |
| 4686 | .file => |file| return processExecveat(sync, file.handle, null, argv, env_block), |
| 4687 | .explicit => |explicit| { |
| 4688 | var path_buffer: [PATH_MAX]u8 = undefined; |
| 4689 | const path_posix = try pathToPosix(explicit.path, &path_buffer); |
| 4690 | return processExecveat(sync, explicit.dir.handle, path_posix, argv, env_block); |
| 4691 | }, |
| 4675 | 4692 | } |
| 4676 | | if (seen_eacces) return error.AccessDenied; |
| 4677 | | return err; |
| 4678 | 4693 | } |
| 4679 | | /// This function ignores PATH environment variable. |
| 4680 | | pub fn execvPath( |
| 4694 | |
| 4695 | fn processExecveat( |
| 4681 | 4696 | sync: *CancelRegion.Sync, |
| 4682 | | path: [*:0]const u8, |
| 4683 | | child_argv: [*:null]const ?[*:0]const u8, |
| 4697 | dir: fd_t, |
| 4698 | path: ?[*:0]const u8, |
| 4699 | argv: [*:null]const ?[*:0]const u8, |
| 4684 | 4700 | env_block: process.Environ.PosixBlock, |
| 4685 | 4701 | ) process.ReplaceError { |
| 4686 | 4702 | try sync.cancel_region.await(.nothing); |
| 4687 | | switch (linux.errno(linux.execve(path, child_argv, env_block.slice.ptr))) { |
| 4703 | const rc = if (path) |p| |
| 4704 | if (dir == linux.AT.FDCWD or Dir.path.isAbsoluteZ(p)) |
| 4705 | linux.execve(p, argv, env_block.slice.ptr) |
| 4706 | else |
| 4707 | linux.execveat(dir, p, argv, env_block.slice.ptr, .{ |
| 4708 | .EMPTY_PATH = false, |
| 4709 | .SYMLINK_NOFOLLOW = false, |
| 4710 | }) |
| 4711 | else |
| 4712 | linux.execveat(dir, "", argv, env_block.slice.ptr, .{ |
| 4713 | .EMPTY_PATH = true, |
| 4714 | .SYMLINK_NOFOLLOW = false, |
| 4715 | }); |
| 4716 | switch (linux.errno(rc)) { |
| 4688 | 4717 | .FAULT => |err| return errnoBug(err), // Bad pointer parameter. |
| 4689 | 4718 | .@"2BIG" => return error.SystemResources, |
| 4690 | 4719 | .MFILE => return error.ProcessFdQuotaExceeded, |
| ... | ... | @@ -4702,6 +4731,7 @@ pub fn execvPath( |
| 4702 | 4731 | .NOTDIR => return error.NotDir, |
| 4703 | 4732 | .TXTBSY => return error.FileBusy, |
| 4704 | 4733 | .LIBBAD => return error.InvalidExe, |
| 4734 | .NOSYS => return error.OperationUnsupported, |
| 4705 | 4735 | else => |err| return unexpectedErrno(err), |
| 4706 | 4736 | } |
| 4707 | 4737 | } |
| ... | ... | @@ -4850,6 +4880,28 @@ fn progressParentFile(userdata: ?*anyopaque) std.Progress.ParentFileError!File { |
| 4850 | 4880 | return ev.environ.zig_progress_file; |
| 4851 | 4881 | } |
| 4852 | 4882 | |
| 4883 | fn inheritParentHandle(ev: *Evented, handle: fd_t) Io.InheritParentHandleError!void { |
| 4884 | var sync: CancelRegion.Sync = try .init(ev); |
| 4885 | defer sync.deinit(ev); |
| 4886 | return setFdFlags(&sync, handle, linux.FD_CLOEXEC); |
| 4887 | } |
| 4888 | |
| 4889 | fn inheritParentDir(userdata: ?*anyopaque, handle: Dir.Handle) Io.InheritParentHandleError!Dir { |
| 4890 | const ev: *Evented = @ptrCast(@alignCast(userdata)); |
| 4891 | try inheritParentHandle(ev, handle); |
| 4892 | return .{ .handle = handle }; |
| 4893 | } |
| 4894 | |
| 4895 | fn inheritParentFile( |
| 4896 | userdata: ?*anyopaque, |
| 4897 | handle: File.Handle, |
| 4898 | flags: File.Flags, |
| 4899 | ) Io.InheritParentHandleError!File { |
| 4900 | const ev: *Evented = @ptrCast(@alignCast(userdata)); |
| 4901 | try inheritParentHandle(ev, handle); |
| 4902 | return .{ .handle = handle, .flags = flags }; |
| 4903 | } |
| 4904 | |
| 4853 | 4905 | fn scanEnviron(ev: *Evented) Io.Cancelable!void { |
| 4854 | 4906 | const ev_io = ev.io(); |
| 4855 | 4907 | try ev.environ_mutex.lock(ev_io); |