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 {...@@ -1122,6 +1122,7 @@ const Syscall = struct {
11221122
1123const max_iovecs_len = 8;1123const max_iovecs_len = 8;
1124const splat_buffer_size = 64;1124const splat_buffer_size = 64;
1125const default_PATH = "/usr/local/bin:/bin/:/usr/bin";
11251126
1126comptime {1127comptime {
1127 if (@TypeOf(posix.IOV_MAX) != void) assert(max_iovecs_len <= posix.IOV_MAX);1128 if (@TypeOf(posix.IOV_MAX) != void) assert(max_iovecs_len <= posix.IOV_MAX);
...@@ -12765,12 +12766,37 @@ fn scanEnviron(t: *Threaded) void {...@@ -12765,12 +12766,37 @@ fn scanEnviron(t: *Threaded) void {
12765}12766}
1276612767
12767fn processReplace(userdata: ?*anyopaque, options: process.ReplaceOptions) process.ReplaceError {12768fn processReplace(userdata: ?*anyopaque, options: process.ReplaceOptions) process.ReplaceError {
12768 _ = userdata;12769 const t: *Threaded = @ptrCast(@alignCast(userdata));
12769 _ = options;12770
12770 @panic("TODO processReplace");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);
12771}12796}
1277212797
12773fn processReplacePath(userdata: ?*anyopaque, dir: Dir, options: process.ReplaceOptions) process.ReplaceError {12798fn processReplacePath(userdata: ?*anyopaque, dir: Dir, options: process.ReplaceOptions) process.ReplaceError {
12799 if (!process.can_replace) return error.OperationUnsupported;
12774 _ = userdata;12800 _ = userdata;
12775 _ = dir;12801 _ = dir;
12776 _ = options;12802 _ = options;
...@@ -12778,6 +12804,7 @@ fn processReplacePath(userdata: ?*anyopaque, dir: Dir, options: process.ReplaceO...@@ -12778,6 +12804,7 @@ fn processReplacePath(userdata: ?*anyopaque, dir: Dir, options: process.ReplaceO
12778}12804}
1277912805
12780fn processSpawnPath(userdata: ?*anyopaque, dir: Dir, options: process.SpawnOptions) process.SpawnError!process.Child {12806fn processSpawnPath(userdata: ?*anyopaque, dir: Dir, options: process.SpawnOptions) process.SpawnError!process.Child {
12807 if (!process.can_spawn) return error.OperationUnsupported;
12781 _ = userdata;12808 _ = userdata;
12782 _ = dir;12809 _ = dir;
12783 _ = options;12810 _ = options;
...@@ -12903,7 +12930,7 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp...@@ -12903,7 +12930,7 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp
12903 errdefer destroyPipe(err_pipe);12930 errdefer destroyPipe(err_pipe);
1290412931
12905 t.scanEnviron(); // for PATH12932 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
12908 const pid_result = try posix.fork();12935 const pid_result = try posix.fork();
12909 if (pid_result == 0) {12936 if (pid_result == 0) {
...@@ -12954,7 +12981,7 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp...@@ -12954,7 +12981,7 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp
12954 }12981 }
12955 }12982 }
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);
12958 forkBail(err_pipe[1], err);12985 forkBail(err_pipe[1], err);
12959 }12986 }
1296012987
...@@ -14361,7 +14388,7 @@ fn testArgvToCommandLineWindows(argv: []const []const u8, expected_cmd_line: []c...@@ -14361,7 +14388,7 @@ fn testArgvToCommandLineWindows(argv: []const []const u8, expected_cmd_line: []c
14361 try std.testing.expectEqualStrings(expected_cmd_line, cmd_line);14388 try std.testing.expectEqualStrings(expected_cmd_line, cmd_line);
14362}14389}
1436314390
14364fn execvpeZ_expandArg0(14391fn posixExecv(
14365 arg0_expand: process.ArgExpansion,14392 arg0_expand: process.ArgExpansion,
14366 file: [*:0]const u8,14393 file: [*:0]const u8,
14367 child_argv: [*:null]?[*:0]const u8,14394 child_argv: [*:null]?[*:0]const u8,
...@@ -14369,10 +14396,10 @@ fn execvpeZ_expandArg0(...@@ -14369,10 +14396,10 @@ fn execvpeZ_expandArg0(
14369 PATH: []const u8,14396 PATH: []const u8,
14370) process.ReplaceError {14397) process.ReplaceError {
14371 const file_slice = std.mem.sliceTo(file, 0);14398 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
14374 // Use of PATH_MAX here is valid as the path_buf will be passed14401 // 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.
14376 var path_buf: [posix.PATH_MAX]u8 = undefined;14403 var path_buf: [posix.PATH_MAX]u8 = undefined;
14377 var it = std.mem.tokenizeScalar(u8, PATH, ':');14404 var it = std.mem.tokenizeScalar(u8, PATH, ':');
14378 var seen_eacces = false;14405 var seen_eacces = false;
...@@ -14397,7 +14424,7 @@ fn execvpeZ_expandArg0(...@@ -14397,7 +14424,7 @@ fn execvpeZ_expandArg0(
14397 .expand => child_argv[0] = full_path,14424 .expand => child_argv[0] = full_path,
14398 .no_expand => {},14425 .no_expand => {},
14399 }14426 }
14400 err = execveZ(full_path, child_argv, envp);14427 err = posixExecvPath(full_path, child_argv, envp);
14401 switch (err) {14428 switch (err) {
14402 error.AccessDenied => seen_eacces = true,14429 error.AccessDenied => seen_eacces = true,
14403 error.FileNotFound, error.NotDir => {},14430 error.FileNotFound, error.NotDir => {},
...@@ -14408,8 +14435,8 @@ fn execvpeZ_expandArg0(...@@ -14408,8 +14435,8 @@ fn execvpeZ_expandArg0(
14408 return err;14435 return err;
14409}14436}
1441014437
14411/// This function ignores PATH environment variable. See `execvpeZ` for that.14438/// This function ignores PATH environment variable.
14412pub fn execveZ(14439pub fn posixExecvPath(
14413 path: [*:0]const u8,14440 path: [*:0]const u8,
14414 child_argv: [*:null]const ?[*:0]const u8,14441 child_argv: [*:null]const ?[*:0]const u8,
14415 envp: [*:null]const ?[*:0]const u8,14442 envp: [*:null]const ?[*:0]const u8,
...@@ -14447,17 +14474,6 @@ pub fn execveZ(...@@ -14447,17 +14474,6 @@ pub fn execveZ(
14447 }14474 }
14448}14475}
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
14461fn windowsMakePipeIn(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const windows.SECURITY_ATTRIBUTES) !void {14477fn windowsMakePipeIn(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const windows.SECURITY_ATTRIBUTES) !void {
14462 var rd_h: windows.HANDLE = undefined;14478 var rd_h: windows.HANDLE = undefined;
14463 var wr_h: windows.HANDLE = undefined;14479 var wr_h: windows.HANDLE = undefined;
lib/std/process.zig+2-2
...@@ -288,11 +288,11 @@ pub const ReplaceError = error{...@@ -288,11 +288,11 @@ pub const ReplaceError = error{
288 FileBusy,288 FileBusy,
289 ProcessFdQuotaExceeded,289 ProcessFdQuotaExceeded,
290 SystemFdQuotaExceeded,290 SystemFdQuotaExceeded,
291} || Io.Dir.PathNameError || Io.Cancelable || Io.UnexpectedError;291} || Allocator.Error || Io.Dir.PathNameError || Io.Cancelable || Io.UnexpectedError;
292292
293pub const ReplaceOptions = struct {293pub const ReplaceOptions = struct {
294 argv: []const []const u8,294 argv: []const []const u8,
295 arg0_expand: ArgExpansion = .no_expand,295 expand_arg0: ArgExpansion = .no_expand,
296 /// Replaces the environment when provided. The PATH value from here is296 /// Replaces the environment when provided. The PATH value from here is
297 /// never used to resolve `argv[0]`.297 /// never used to resolve `argv[0]`.
298 environ_map: ?*const Environ.Map = null,298 environ_map: ?*const Environ.Map = null,