| ... | @@ -756,13 +756,13 @@ pub fn io(ev: *Evented) Io { | ... | @@ -756,13 +756,13 @@ pub fn io(ev: *Evented) Io { |
| 756 | .processSetCurrentDir = processSetCurrentDir, | 756 | .processSetCurrentDir = processSetCurrentDir, |
| 757 | .processSetCurrentPath = processSetCurrentPath, | 757 | .processSetCurrentPath = processSetCurrentPath, |
| 758 | .processReplace = processReplace, | 758 | .processReplace = processReplace, |
| 759 | .processReplacePath = processReplacePath, | | |
| 760 | .processSpawn = processSpawn, | 759 | .processSpawn = processSpawn, |
| 761 | .processSpawnPath = processSpawnPath, | | |
| 762 | .childWait = childWait, | 760 | .childWait = childWait, |
| 763 | .childKill = childKill, | 761 | .childKill = childKill, |
| 764 | | 762 | |
| 765 | .progressParentFile = progressParentFile, | 763 | .progressParentFile = progressParentFile, |
| | 764 | .inheritParentDir = inheritParentDir, |
| | 765 | .inheritParentFile = inheritParentFile, |
| 766 | | 766 | |
| 767 | .now = now, | 767 | .now = now, |
| 768 | .clockResolution = clockResolution, | 768 | .clockResolution = clockResolution, |
| ... | @@ -804,7 +804,7 @@ pub const InitOptions = struct { | ... | @@ -804,7 +804,7 @@ pub const InitOptions = struct { |
| 804 | argv0: Argv0 = .empty, | 804 | argv0: Argv0 = .empty, |
| 805 | /// Affects the following operations: | 805 | /// Affects the following operations: |
| 806 | /// * `fileIsTty` | 806 | /// * `fileIsTty` |
| 807 | /// * `processSpawn`, `processSpawnPath`, `processReplace`, `processReplacePath` | 807 | /// * `processSpawn`, `processReplace` |
| 808 | environ: process.Environ = .empty, | 808 | environ: process.Environ = .empty, |
| 809 | }; | 809 | }; |
| 810 | | 810 | |
| ... | @@ -1230,7 +1230,8 @@ fn idle(ev: *Evented, thread: *Thread) void { | ... | @@ -1230,7 +1230,8 @@ fn idle(ev: *Evented, thread: *Thread) void { |
| 1230 | batch_userdata[0] = next; | 1230 | batch_userdata[0] = next; |
| 1231 | } | 1231 | } |
| 1232 | break :ready_fiber switch (@as(u2, @truncate(next))) { | 1232 | break :ready_fiber switch (@as(u2, @truncate(next))) { |
| 1233 | 0b00, 0b01 => @ptrFromInt(next & ~@as(usize, 0b11)), | 1233 | 0b00 => @ptrFromInt(next), |
| | 1234 | 0b01 => null, // the timeout completion already woke this fiber |
| 1234 | 0b10, 0b11 => null, | 1235 | 0b10, 0b11 => null, |
| 1235 | }; | 1236 | }; |
| 1236 | }, | 1237 | }, |
| ... | @@ -2401,6 +2402,10 @@ fn batchDrainSubmitted( | ... | @@ -2401,6 +2402,10 @@ fn batchDrainSubmitted( |
| 2401 | return error.ConcurrencyUnavailable | 2402 | return error.ConcurrencyUnavailable |
| 2402 | else | 2403 | else |
| 2403 | .{ .device_io_control = try ev.deviceIoControl(try maybe_sync.enterSync(ev), o) }, | 2404 | .{ .device_io_control = try ev.deviceIoControl(try maybe_sync.enterSync(ev), o) }, |
| | 2405 | .net_accept => |o| { |
| | 2406 | _ = o; |
| | 2407 | @panic("TODO implement batchDrainSubmitted for net_receive"); |
| | 2408 | }, |
| 2404 | .net_receive => |o| { | 2409 | .net_receive => |o| { |
| 2405 | _ = o; | 2410 | _ = o; |
| 2406 | @panic("TODO implement batchDrainSubmitted for net_receive"); | 2411 | @panic("TODO implement batchDrainSubmitted for net_receive"); |
| ... | @@ -2479,7 +2484,10 @@ fn batchDrainReady(batch: *Io.Batch) Io.Timeout.Error!void { | ... | @@ -2479,7 +2484,10 @@ fn batchDrainReady(batch: *Io.Batch) Io.Timeout.Error!void { |
| 2479 | if (@as(?Io.Operation.Result, result: switch (pending.tag) { | 2484 | if (@as(?Io.Operation.Result, result: switch (pending.tag) { |
| 2480 | .file_read_streaming => .{ | 2485 | .file_read_streaming => .{ |
| 2481 | .file_read_streaming = switch (completion.errno()) { | 2486 | .file_read_streaming = switch (completion.errno()) { |
| 2482 | .SUCCESS => @as(u32, @bitCast(completion.result)), | 2487 | .SUCCESS => if (completion.result == 0) |
| | 2488 | error.EndOfStream |
| | 2489 | else |
| | 2490 | @as(u32, @bitCast(completion.result)), |
| 2483 | .INTR => 0, | 2491 | .INTR => 0, |
| 2484 | .CANCELED => break :result null, | 2492 | .CANCELED => break :result null, |
| 2485 | .INVAL => |err| errnoBug(err), | 2493 | .INVAL => |err| errnoBug(err), |
| ... | @@ -2517,6 +2525,7 @@ fn batchDrainReady(batch: *Io.Batch) Io.Timeout.Error!void { | ... | @@ -2517,6 +2525,7 @@ fn batchDrainReady(batch: *Io.Batch) Io.Timeout.Error!void { |
| 2517 | }, | 2525 | }, |
| 2518 | }, | 2526 | }, |
| 2519 | .device_io_control => unreachable, | 2527 | .device_io_control => unreachable, |
| | 2528 | .net_accept => @panic("TODO"), |
| 2520 | .net_receive => @panic("TODO"), | 2529 | .net_receive => @panic("TODO"), |
| 2521 | .net_send => @panic("TODO"), | 2530 | .net_send => @panic("TODO"), |
| 2522 | .net_read => @panic("TODO"), | 2531 | .net_read => @panic("TODO"), |
| ... | @@ -4260,19 +4269,32 @@ fn processReplace(userdata: ?*anyopaque, options: process.ReplaceOptions) proces | ... | @@ -4260,19 +4269,32 @@ fn processReplace(userdata: ?*anyopaque, options: process.ReplaceOptions) proces |
| 4260 | | 4269 | |
| 4261 | var sync: CancelRegion.Sync = try .init(ev); | 4270 | var sync: CancelRegion.Sync = try .init(ev); |
| 4262 | defer sync.deinit(ev); | 4271 | defer sync.deinit(ev); |
| 4263 | return execv(&sync, options.expand_arg0, argv_buf.ptr[0].?, argv_buf.ptr, env_block, PATH); | | |
| 4264 | } | | |
| 4265 | | 4272 | |
| 4266 | fn processReplacePath( | 4273 | switch (options.cwd) { |
| 4267 | userdata: ?*anyopaque, | 4274 | .inherit => {}, |
| 4268 | dir: Dir, | 4275 | .dir => |cwd_dir| fchdir(&sync, cwd_dir.handle) catch |err| switch (err) { |
| 4269 | options: process.ReplaceOptions, | 4276 | error.NoDevice, error.UnrecognizedVolume => return error.FileSystem, |
| 4270 | ) process.ReplaceError { | 4277 | else => |e| return e, |
| 4271 | const ev: *Evented = @ptrCast(@alignCast(userdata)); | 4278 | }, |
| 4272 | _ = ev; | 4279 | .path => |cwd_path| { |
| 4273 | _ = dir; | 4280 | var cwd_path_buffer: [PATH_MAX]u8 = undefined; |
| 4274 | _ = options; | 4281 | const cwd_path_posix = try pathToPosix(cwd_path, &cwd_path_buffer); |
| 4275 | @panic("TODO processReplacePath"); | 4282 | chdir(&sync, cwd_path_posix) catch |err| switch (err) { |
| | 4283 | error.SymLinkLoop => return error.FileSystem, |
| | 4284 | else => |e| return e, |
| | 4285 | }; |
| | 4286 | }, |
| | 4287 | } |
| | 4288 | for (options.inherit_dirs) |dir| try setFdFlags(&sync, dir.handle, 0); |
| | 4289 | for (options.inherit_files) |file| try setFdFlags(&sync, file.handle, 0); |
| | 4290 | if (options.start_suspended) { |
| | 4291 | switch (linux.errno(linux.kill(linux.getpid(), .STOP))) { |
| | 4292 | .SUCCESS => {}, |
| | 4293 | .PERM => return error.PermissionDenied, |
| | 4294 | else => return error.Unexpected, |
| | 4295 | } |
| | 4296 | } |
| | 4297 | return processExec(&sync, options.exe, argv_buf.ptr, options.expand_arg0, env_block, PATH); |
| 4276 | } | 4298 | } |
| 4277 | | 4299 | |
| 4278 | fn processSpawn(userdata: ?*anyopaque, options: process.SpawnOptions) process.SpawnError!process.Child { | 4300 | fn processSpawn(userdata: ?*anyopaque, options: process.SpawnOptions) process.SpawnError!process.Child { |
| ... | @@ -4309,18 +4331,6 @@ fn processSpawn(userdata: ?*anyopaque, options: process.SpawnOptions) process.Sp | ... | @@ -4309,18 +4331,6 @@ fn processSpawn(userdata: ?*anyopaque, options: process.SpawnOptions) process.Sp |
| 4309 | return child_err; | 4331 | return child_err; |
| 4310 | } | 4332 | } |
| 4311 | | 4333 | |
| 4312 | fn processSpawnPath( | | |
| 4313 | userdata: ?*anyopaque, | | |
| 4314 | dir: Dir, | | |
| 4315 | options: process.SpawnOptions, | | |
| 4316 | ) process.SpawnError!process.Child { | | |
| 4317 | const ev: *Evented = @ptrCast(@alignCast(userdata)); | | |
| 4318 | _ = ev; | | |
| 4319 | _ = dir; | | |
| 4320 | _ = options; | | |
| 4321 | @panic("TODO processSpawnPath"); | | |
| 4322 | } | | |
| 4323 | | | |
| 4324 | const prog_fileno = @max(linux.STDIN_FILENO, linux.STDOUT_FILENO, linux.STDERR_FILENO); | 4334 | const prog_fileno = @max(linux.STDIN_FILENO, linux.STDOUT_FILENO, linux.STDERR_FILENO); |
| 4325 | | 4335 | |
| 4326 | const Spawned = struct { | 4336 | const Spawned = struct { |
| ... | @@ -4504,6 +4514,20 @@ fn setUpChild(sync: *CancelRegion.Sync, options: struct { | ... | @@ -4504,6 +4514,20 @@ fn setUpChild(sync: *CancelRegion.Sync, options: struct { |
| 4504 | PATH: []const u8, | 4514 | PATH: []const u8, |
| 4505 | spawn: process.SpawnOptions, | 4515 | spawn: process.SpawnOptions, |
| 4506 | }) ForkBailError { | 4516 | }) ForkBailError { |
| | 4517 | // Must happen before clobbering file descriptors below. |
| | 4518 | switch (options.spawn.cwd) { |
| | 4519 | .inherit => {}, |
| | 4520 | .dir => |cwd_dir| try fchdir(sync, cwd_dir.handle), |
| | 4521 | .path => |cwd_path| { |
| | 4522 | var cwd_path_buffer: [PATH_MAX]u8 = undefined; |
| | 4523 | const cwd_path_posix = try pathToPosix(cwd_path, &cwd_path_buffer); |
| | 4524 | try chdir(sync, cwd_path_posix); |
| | 4525 | }, |
| | 4526 | } |
| | 4527 | |
| | 4528 | for (options.spawn.inherit_dirs) |dir| try setFdFlags(sync, dir.handle, 0); |
| | 4529 | for (options.spawn.inherit_files) |file| try setFdFlags(sync, file.handle, 0); |
| | 4530 | |
| 4507 | try setUpChildIo( | 4531 | try setUpChildIo( |
| 4508 | sync, | 4532 | sync, |
| 4509 | options.spawn.stdin, | 4533 | options.spawn.stdin, |
| ... | @@ -4526,20 +4550,15 @@ fn setUpChild(sync: *CancelRegion.Sync, options: struct { | ... | @@ -4526,20 +4550,15 @@ fn setUpChild(sync: *CancelRegion.Sync, options: struct { |
| 4526 | options.dev_null_fd, | 4550 | options.dev_null_fd, |
| 4527 | ); | 4551 | ); |
| 4528 | | 4552 | |
| 4529 | switch (options.spawn.cwd) { | 4553 | // Must happen after fchdir above because the cwd file descriptor might be |
| 4530 | .inherit => {}, | 4554 | // equal to prog_fileno and be clobbered by this operation. |
| 4531 | .dir => |cwd_dir| try fchdir(sync, cwd_dir.handle), | 4555 | if (options.prog_pipe != -1) { |
| 4532 | .path => |cwd_path| { | 4556 | if (options.prog_pipe == prog_fileno) |
| 4533 | var cwd_path_buffer: [PATH_MAX]u8 = undefined; | 4557 | try setFdFlags(sync, options.prog_pipe, 0) |
| 4534 | const cwd_path_posix = try pathToPosix(cwd_path, &cwd_path_buffer); | 4558 | else |
| 4535 | try chdir(sync, cwd_path_posix); | 4559 | try dup2(sync, options.prog_pipe, prog_fileno); |
| 4536 | }, | | |
| 4537 | } | 4560 | } |
| 4538 | | 4561 | |
| 4539 | // Must happen after fchdir above, the cwd file descriptor might be | | |
| 4540 | // equal to prog_fileno and be clobbered by this dup2 call. | | |
| 4541 | if (options.prog_pipe != -1) try dup2(sync, options.prog_pipe, prog_fileno); | | |
| 4542 | | | |
| 4543 | if (options.spawn.gid) |gid| { | 4562 | if (options.spawn.gid) |gid| { |
| 4544 | switch (linux.errno(linux.setregid(gid, gid))) { | 4563 | switch (linux.errno(linux.setregid(gid, gid))) { |
| 4545 | .SUCCESS => {}, | 4564 | .SUCCESS => {}, |
| ... | @@ -4571,18 +4590,18 @@ fn setUpChild(sync: *CancelRegion.Sync, options: struct { | ... | @@ -4571,18 +4590,18 @@ fn setUpChild(sync: *CancelRegion.Sync, options: struct { |
| 4571 | } | 4590 | } |
| 4572 | | 4591 | |
| 4573 | if (options.spawn.start_suspended) { | 4592 | if (options.spawn.start_suspended) { |
| 4574 | switch (linux.errno(linux.kill(0, .STOP))) { | 4593 | switch (linux.errno(linux.kill(linux.getpid(), .STOP))) { |
| 4575 | .SUCCESS => {}, | 4594 | .SUCCESS => {}, |
| 4576 | .PERM => return error.PermissionDenied, | 4595 | .PERM => return error.PermissionDenied, |
| 4577 | else => return error.Unexpected, | 4596 | else => return error.Unexpected, |
| 4578 | } | 4597 | } |
| 4579 | } | 4598 | } |
| 4580 | | 4599 | |
| 4581 | return execv( | 4600 | return processExec( |
| 4582 | sync, | 4601 | sync, |
| 4583 | options.spawn.expand_arg0, | 4602 | options.spawn.exe, |
| 4584 | options.argv_buf.ptr[0].?, | | |
| 4585 | options.argv_buf.ptr, | 4603 | options.argv_buf.ptr, |
| | 4604 | options.spawn.expand_arg0, |
| 4586 | options.env_block, | 4605 | options.env_block, |
| 4587 | options.PATH, | 4606 | options.PATH, |
| 4588 | ); | 4607 | ); |
| ... | @@ -4604,6 +4623,17 @@ fn setUpChildIo( | ... | @@ -4604,6 +4623,17 @@ fn setUpChildIo( |
| 4604 | } | 4623 | } |
| 4605 | } | 4624 | } |
| 4606 | | 4625 | |
| | 4626 | fn setFdFlags(sync: *CancelRegion.Sync, fd: fd_t, flags: usize) (Io.Cancelable || Io.UnexpectedError)!void { |
| | 4627 | while (true) { |
| | 4628 | try sync.cancel_region.await(.nothing); |
| | 4629 | switch (linux.errno(linux.fcntl(fd, linux.F.SETFD, flags))) { |
| | 4630 | .SUCCESS => return, |
| | 4631 | .INTR => {}, |
| | 4632 | else => |err| return unexpectedErrno(err), |
| | 4633 | } |
| | 4634 | } |
| | 4635 | } |
| | 4636 | |
| 4607 | pub const DupError = error{ | 4637 | pub const DupError = error{ |
| 4608 | ProcessFdQuotaExceeded, | 4638 | ProcessFdQuotaExceeded, |
| 4609 | SystemResources, | 4639 | SystemResources, |
| ... | @@ -4623,63 +4653,77 @@ pub fn dup2(sync: *CancelRegion.Sync, old_fd: fd_t, new_fd: fd_t) DupError!void | ... | @@ -4623,63 +4653,77 @@ pub fn dup2(sync: *CancelRegion.Sync, old_fd: fd_t, new_fd: fd_t) DupError!void |
| 4623 | } | 4653 | } |
| 4624 | } | 4654 | } |
| 4625 | | 4655 | |
| 4626 | fn execv( | 4656 | fn processExec( |
| 4627 | sync: *CancelRegion.Sync, | 4657 | sync: *CancelRegion.Sync, |
| 4628 | arg0_expand: process.ArgExpansion, | 4658 | exe: process.ReplaceOptions.Exe, |
| 4629 | file: [*:0]const u8, | 4659 | argv: [*:null]?[*:0]const u8, |
| 4630 | child_argv: [*:null]?[*:0]const u8, | 4660 | expand_arg0: process.ArgExpansion, |
| 4631 | env_block: process.Environ.PosixBlock, | 4661 | env_block: process.Environ.PosixBlock, |
| 4632 | PATH: []const u8, | 4662 | PATH: []const u8, |
| 4633 | ) process.ReplaceError { | 4663 | ) process.ReplaceError { |
| 4634 | const file_slice = std.mem.sliceTo(file, 0); | 4664 | const arg0_slice = std.mem.span(argv[0].?); |
| 4635 | if (std.mem.findScalar(u8, file_slice, '/') != null) | 4665 | exe: switch (exe) { |
| 4636 | return execvPath(sync, file, child_argv, env_block); | 4666 | .detect => continue :exe if (std.mem.findScalar(u8, arg0_slice, '/')) |_| .{ |
| 4637 | | 4667 | .path = .cwd(), |
| 4638 | // Use of PATH_MAX here is valid as the path_buf will be passed | 4668 | } else .search, |
| 4639 | // directly to the operating system in posixExecvPath. | 4669 | .search => { |
| 4640 | var path_buf: [PATH_MAX]u8 = undefined; | 4670 | if (Dir.path.isAbsolute(arg0_slice)) continue :exe .{ .path = .cwd() }; |
| 4641 | var it = std.mem.tokenizeScalar(u8, PATH, ':'); | 4671 | var path_buf: [PATH_MAX]u8 = undefined; |
| 4642 | var seen_eacces = false; | 4672 | var it = std.mem.tokenizeScalar(u8, PATH, ':'); |
| 4643 | var err: process.ReplaceError = error.FileNotFound; | 4673 | var err: error{ FileNotFound, NotDir } = error.FileNotFound; |
| 4644 | | 4674 | |
| 4645 | // In case of expanding arg0 we must put it back if we return with an error. | 4675 | while (it.next()) |search_path| { |
| 4646 | const prev_arg0 = child_argv[0]; | 4676 | const path_len = search_path.len + arg0_slice.len + 1; |
| 4647 | defer switch (arg0_expand) { | 4677 | if (path_buf.len < path_len + 1) return error.NameTooLong; |
| 4648 | .expand => child_argv[0] = prev_arg0, | 4678 | @memcpy(path_buf[0..search_path.len], search_path); |
| 4649 | .no_expand => {}, | 4679 | path_buf[search_path.len] = Dir.path.sep; |
| 4650 | }; | 4680 | @memcpy(path_buf[search_path.len + 1 ..][0..arg0_slice.len], arg0_slice); |
| 4651 | | 4681 | path_buf[path_len] = 0; |
| 4652 | while (it.next()) |search_path| { | 4682 | const full_path = path_buf[0..path_len :0].ptr; |
| 4653 | const path_len = search_path.len + file_slice.len + 1; | 4683 | switch (expand_arg0) { |
| 4654 | if (path_buf.len < path_len + 1) return error.NameTooLong; | 4684 | .expand => argv[0] = full_path, |
| 4655 | @memcpy(path_buf[0..search_path.len], search_path); | 4685 | .no_expand => {}, |
| 4656 | path_buf[search_path.len] = '/'; | 4686 | } |
| 4657 | @memcpy(path_buf[search_path.len + 1 ..][0..file_slice.len], file_slice); | 4687 | switch (processExecveat(sync, linux.AT.FDCWD, full_path, argv, env_block)) { |
| 4658 | path_buf[path_len] = 0; | 4688 | error.AccessDenied => {}, |
| 4659 | const full_path = path_buf[0..path_len :0].ptr; | 4689 | error.FileNotFound, error.NotDir => |e| err = e, |
| 4660 | switch (arg0_expand) { | 4690 | else => |e| return e, |
| 4661 | .expand => child_argv[0] = full_path, | 4691 | } |
| 4662 | .no_expand => {}, | 4692 | } |
| 4663 | } | 4693 | return err; |
| 4664 | err = execvPath(sync, full_path, child_argv, env_block); | 4694 | }, |
| 4665 | switch (err) { | 4695 | .path => |dir| return processExecveat(sync, dir.handle, arg0_slice, argv, env_block), |
| 4666 | error.AccessDenied => seen_eacces = true, | 4696 | .file => |file| return processExecveat(sync, file.handle, null, argv, env_block), |
| 4667 | error.FileNotFound, error.NotDir => {}, | 4697 | .explicit => |explicit| { |
| 4668 | else => |e| return e, | 4698 | var path_buffer: [PATH_MAX]u8 = undefined; |
| 4669 | } | 4699 | const path_posix = try pathToPosix(explicit.path, &path_buffer); |
| | 4700 | return processExecveat(sync, explicit.dir.handle, path_posix, argv, env_block); |
| | 4701 | }, |
| 4670 | } | 4702 | } |
| 4671 | if (seen_eacces) return error.AccessDenied; | | |
| 4672 | return err; | | |
| 4673 | } | 4703 | } |
| 4674 | /// This function ignores PATH environment variable. | 4704 | |
| 4675 | pub fn execvPath( | 4705 | fn processExecveat( |
| 4676 | sync: *CancelRegion.Sync, | 4706 | sync: *CancelRegion.Sync, |
| 4677 | path: [*:0]const u8, | 4707 | dir: fd_t, |
| 4678 | child_argv: [*:null]const ?[*:0]const u8, | 4708 | path: ?[*:0]const u8, |
| | 4709 | argv: [*:null]const ?[*:0]const u8, |
| 4679 | env_block: process.Environ.PosixBlock, | 4710 | env_block: process.Environ.PosixBlock, |
| 4680 | ) process.ReplaceError { | 4711 | ) process.ReplaceError { |
| 4681 | try sync.cancel_region.await(.nothing); | 4712 | try sync.cancel_region.await(.nothing); |
| 4682 | switch (linux.errno(linux.execve(path, child_argv, env_block.slice.ptr))) { | 4713 | const rc = if (path) |p| |
| | 4714 | if (dir == linux.AT.FDCWD or Dir.path.isAbsoluteZ(p)) |
| | 4715 | linux.execve(p, argv, env_block.slice.ptr) |
| | 4716 | else |
| | 4717 | linux.execveat(dir, p, argv, env_block.slice.ptr, .{ |
| | 4718 | .EMPTY_PATH = false, |
| | 4719 | .SYMLINK_NOFOLLOW = false, |
| | 4720 | }) |
| | 4721 | else |
| | 4722 | linux.execveat(dir, "", argv, env_block.slice.ptr, .{ |
| | 4723 | .EMPTY_PATH = true, |
| | 4724 | .SYMLINK_NOFOLLOW = false, |
| | 4725 | }); |
| | 4726 | switch (linux.errno(rc)) { |
| 4683 | .FAULT => |err| return errnoBug(err), // Bad pointer parameter. | 4727 | .FAULT => |err| return errnoBug(err), // Bad pointer parameter. |
| 4684 | .@"2BIG" => return error.SystemResources, | 4728 | .@"2BIG" => return error.SystemResources, |
| 4685 | .MFILE => return error.ProcessFdQuotaExceeded, | 4729 | .MFILE => return error.ProcessFdQuotaExceeded, |
| ... | @@ -4697,6 +4741,7 @@ pub fn execvPath( | ... | @@ -4697,6 +4741,7 @@ pub fn execvPath( |
| 4697 | .NOTDIR => return error.NotDir, | 4741 | .NOTDIR => return error.NotDir, |
| 4698 | .TXTBSY => return error.FileBusy, | 4742 | .TXTBSY => return error.FileBusy, |
| 4699 | .LIBBAD => return error.InvalidExe, | 4743 | .LIBBAD => return error.InvalidExe, |
| | 4744 | .NOSYS => return error.OperationUnsupported, |
| 4700 | else => |err| return unexpectedErrno(err), | 4745 | else => |err| return unexpectedErrno(err), |
| 4701 | } | 4746 | } |
| 4702 | } | 4747 | } |
| ... | @@ -4845,6 +4890,28 @@ fn progressParentFile(userdata: ?*anyopaque) std.Progress.ParentFileError!File { | ... | @@ -4845,6 +4890,28 @@ fn progressParentFile(userdata: ?*anyopaque) std.Progress.ParentFileError!File { |
| 4845 | return ev.environ.zig_progress_file; | 4890 | return ev.environ.zig_progress_file; |
| 4846 | } | 4891 | } |
| 4847 | | 4892 | |
| | 4893 | fn inheritParentHandle(ev: *Evented, handle: fd_t) Io.InheritParentHandleError!void { |
| | 4894 | var sync: CancelRegion.Sync = try .init(ev); |
| | 4895 | defer sync.deinit(ev); |
| | 4896 | return setFdFlags(&sync, handle, linux.FD_CLOEXEC); |
| | 4897 | } |
| | 4898 | |
| | 4899 | fn inheritParentDir(userdata: ?*anyopaque, handle: Dir.Handle) Io.InheritParentHandleError!Dir { |
| | 4900 | const ev: *Evented = @ptrCast(@alignCast(userdata)); |
| | 4901 | try inheritParentHandle(ev, handle); |
| | 4902 | return .{ .handle = handle }; |
| | 4903 | } |
| | 4904 | |
| | 4905 | fn inheritParentFile( |
| | 4906 | userdata: ?*anyopaque, |
| | 4907 | handle: File.Handle, |
| | 4908 | flags: File.Flags, |
| | 4909 | ) Io.InheritParentHandleError!File { |
| | 4910 | const ev: *Evented = @ptrCast(@alignCast(userdata)); |
| | 4911 | try inheritParentHandle(ev, handle); |
| | 4912 | return .{ .handle = handle, .flags = flags }; |
| | 4913 | } |
| | 4914 | |
| 4848 | fn scanEnviron(ev: *Evented) Io.Cancelable!void { | 4915 | fn scanEnviron(ev: *Evented) Io.Cancelable!void { |
| 4849 | const ev_io = ev.io(); | 4916 | const ev_io = ev.io(); |
| 4850 | try ev.environ_mutex.lock(ev_io); | 4917 | try ev.environ_mutex.lock(ev_io); |