authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-02 22:25:51-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-04 00:27:09-08:00
logbaa49e59294cb8a637eff7c0b3f07c523c91c18b
treee22fa177b4190bc573d4ec6c67e731a0f36d5399
parent08cc9e8d59b8af37cadee1d4529fa4d3a83be0a1

std.Io.Threaded: implement processReplace


2 files changed, 40 insertions(+), 24 deletions(-)

lib/std/Io/Threaded.zig+38-22
......@@ -1122,6 +1122,7 @@ const Syscall = struct {
11221122
11231123const max_iovecs_len = 8;
11241124const splat_buffer_size = 64;
1125const default_PATH = "/usr/local/bin:/bin/:/usr/bin";
11251126
11261127comptime {
11271128 if (@TypeOf(posix.IOV_MAX) != void) assert(max_iovecs_len <= posix.IOV_MAX);
......@@ -12765,12 +12766,37 @@ fn scanEnviron(t: *Threaded) void {
1276512766}
1276612767
1276712768fn processReplace(userdata: ?*anyopaque, options: process.ReplaceOptions) process.ReplaceError {
12768 _ = userdata;
12769 _ = options;
12770 @panic("TODO processReplace");
12769 const t: *Threaded = @ptrCast(@alignCast(userdata));
12770
12771 if (!process.can_replace) return error.OperationUnsupported;
12772
12773 t.scanEnviron(); // for PATH
12774 const PATH = t.environ.string.PATH orelse default_PATH;
12775
12776 var arena_allocator = std.heap.ArenaAllocator.init(t.allocator);
12777 defer arena_allocator.deinit();
12778 const arena = arena_allocator.allocator();
12779
12780 const argv_buf = try arena.allocSentinel(?[*:0]const u8, options.argv.len, null);
12781 for (options.argv, 0..) |arg, i| argv_buf[i] = (try arena.dupeZ(u8, arg)).ptr;
12782
12783 const envp: [*:null]const ?[*:0]const u8 = m: {
12784 const prog_fd: i32 = -1;
12785 if (options.environ_map) |environ_map| {
12786 break :m (try environ_map.createBlockPosix(arena, .{
12787 .zig_progress_fd = prog_fd,
12788 })).ptr;
12789 }
12790 break :m (try process.Environ.createBlockPosix(t.environ.process_environ, arena, .{
12791 .zig_progress_fd = prog_fd,
12792 })).ptr;
12793 };
12794
12795 return posixExecv(options.expand_arg0, argv_buf.ptr[0].?, argv_buf.ptr, envp, PATH);
1277112796}
1277212797
1277312798fn processReplacePath(userdata: ?*anyopaque, dir: Dir, options: process.ReplaceOptions) process.ReplaceError {
12799 if (!process.can_replace) return error.OperationUnsupported;
1277412800 _ = userdata;
1277512801 _ = dir;
1277612802 _ = options;
......@@ -12778,6 +12804,7 @@ fn processReplacePath(userdata: ?*anyopaque, dir: Dir, options: process.ReplaceO
1277812804}
1277912805
1278012806fn processSpawnPath(userdata: ?*anyopaque, dir: Dir, options: process.SpawnOptions) process.SpawnError!process.Child {
12807 if (!process.can_spawn) return error.OperationUnsupported;
1278112808 _ = userdata;
1278212809 _ = dir;
1278312810 _ = options;
......@@ -12903,7 +12930,7 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp
1290312930 errdefer destroyPipe(err_pipe);
1290412931
1290512932 t.scanEnviron(); // for PATH
12906 const PATH = t.environ.string.PATH orelse "/usr/local/bin:/bin/:/usr/bin";
12933 const PATH = t.environ.string.PATH orelse default_PATH;
1290712934
1290812935 const pid_result = try posix.fork();
1290912936 if (pid_result == 0) {
......@@ -12954,7 +12981,7 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp
1295412981 }
1295512982 }
1295612983
12957 const err = execvpeZ_expandArg0(options.expand_arg0, argv_buf.ptr[0].?, argv_buf.ptr, envp, PATH);
12984 const err = posixExecv(options.expand_arg0, argv_buf.ptr[0].?, argv_buf.ptr, envp, PATH);
1295812985 forkBail(err_pipe[1], err);
1295912986 }
1296012987
......@@ -14361,7 +14388,7 @@ fn testArgvToCommandLineWindows(argv: []const []const u8, expected_cmd_line: []c
1436114388 try std.testing.expectEqualStrings(expected_cmd_line, cmd_line);
1436214389}
1436314390
14364fn execvpeZ_expandArg0(
14391fn posixExecv(
1436514392 arg0_expand: process.ArgExpansion,
1436614393 file: [*:0]const u8,
1436714394 child_argv: [*:null]?[*:0]const u8,
......@@ -14369,10 +14396,10 @@ fn execvpeZ_expandArg0(
1436914396 PATH: []const u8,
1437014397) process.ReplaceError {
1437114398 const file_slice = std.mem.sliceTo(file, 0);
14372 if (std.mem.findScalar(u8, file_slice, '/') != null) return execveZ(file, child_argv, envp);
14399 if (std.mem.findScalar(u8, file_slice, '/') != null) return posixExecvPath(file, child_argv, envp);
1437314400
1437414401 // Use of PATH_MAX here is valid as the path_buf will be passed
14375 // directly to the operating system in execveZ.
14402 // directly to the operating system in posixExecvPath.
1437614403 var path_buf: [posix.PATH_MAX]u8 = undefined;
1437714404 var it = std.mem.tokenizeScalar(u8, PATH, ':');
1437814405 var seen_eacces = false;
......@@ -14397,7 +14424,7 @@ fn execvpeZ_expandArg0(
1439714424 .expand => child_argv[0] = full_path,
1439814425 .no_expand => {},
1439914426 }
14400 err = execveZ(full_path, child_argv, envp);
14427 err = posixExecvPath(full_path, child_argv, envp);
1440114428 switch (err) {
1440214429 error.AccessDenied => seen_eacces = true,
1440314430 error.FileNotFound, error.NotDir => {},
......@@ -14408,8 +14435,8 @@ fn execvpeZ_expandArg0(
1440814435 return err;
1440914436}
1441014437
14411/// This function ignores PATH environment variable. See `execvpeZ` for that.
14412pub fn execveZ(
14438/// This function ignores PATH environment variable.
14439pub fn posixExecvPath(
1441314440 path: [*:0]const u8,
1441414441 child_argv: [*:null]const ?[*:0]const u8,
1441514442 envp: [*:null]const ?[*:0]const u8,
......@@ -14447,17 +14474,6 @@ pub fn execveZ(
1444714474 }
1444814475}
1444914476
14450/// This function also uses the PATH environment variable to get the full path to the executable.
14451/// If `file` is an absolute path, this is the same as `execveZ`.
14452pub fn execvpeZ(
14453 file: [*:0]const u8,
14454 argv_ptr: [*:null]const ?[*:0]const u8,
14455 envp: [*:null]const ?[*:0]const u8,
14456 PATH: []const u8,
14457) process.ReplaceError {
14458 return execvpeZ_expandArg0(.no_expand, file, argv_ptr, envp, PATH);
14459}
14460
1446114477fn windowsMakePipeIn(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const windows.SECURITY_ATTRIBUTES) !void {
1446214478 var rd_h: windows.HANDLE = undefined;
1446314479 var wr_h: windows.HANDLE = undefined;
lib/std/process.zig+2-2
......@@ -288,11 +288,11 @@ pub const ReplaceError = error{
288288 FileBusy,
289289 ProcessFdQuotaExceeded,
290290 SystemFdQuotaExceeded,
291} || Io.Dir.PathNameError || Io.Cancelable || Io.UnexpectedError;
291} || Allocator.Error || Io.Dir.PathNameError || Io.Cancelable || Io.UnexpectedError;
292292
293293pub const ReplaceOptions = struct {
294294 argv: []const []const u8,
295 arg0_expand: ArgExpansion = .no_expand,
295 expand_arg0: ArgExpansion = .no_expand,
296296 /// Replaces the environment when provided. The PATH value from here is
297297 /// never used to resolve `argv[0]`.
298298 environ_map: ?*const Environ.Map = null,