diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2024-03-18 22:39:59 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2024-03-19 11:45:09 -0700 |
| commit | cd62005f19ff966d2c42de4daeb9a1e4b644bf76 (patch) | |
| tree | 4bb316708afaf79c971808df792d8fe86274789b /lib/std/process.zig | |
| parent | 7057bffc14602add697eb566b83934b7ad3fd81c (diff) | |
| download | zig-cd62005f19ff966d2c42de4daeb9a1e4b644bf76.tar.gz zig-cd62005f19ff966d2c42de4daeb9a1e4b644bf76.zip | |
extract std.posix from std.os
closes #5019
Diffstat (limited to 'lib/std/process.zig')
| -rw-r--r-- | lib/std/process.zig | 197 |
1 files changed, 123 insertions, 74 deletions
diff --git a/lib/std/process.zig b/lib/std/process.zig index 5360a96521..b72c1963f5 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -1,6 +1,5 @@ const std = @import("std.zig"); const builtin = @import("builtin"); -const os = std.os; const fs = std.fs; const mem = std.mem; const math = std.math; @@ -8,20 +7,27 @@ const Allocator = mem.Allocator; const assert = std.debug.assert; const testing = std.testing; const child_process = @import("child_process.zig"); +const native_os = builtin.os.tag; +const posix = std.posix; +const windows = std.os.windows; pub const Child = child_process.ChildProcess; -pub const abort = os.abort; -pub const exit = os.exit; -pub const changeCurDir = os.chdir; -pub const changeCurDirC = os.chdirC; +pub const abort = posix.abort; +pub const exit = posix.exit; +pub const changeCurDir = posix.chdir; +pub const changeCurDirC = posix.chdirC; + +pub const GetCwdError = posix.GetCwdError; /// The result is a slice of `out_buffer`, from index `0`. /// On Windows, the result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). /// On other platforms, the result is an opaque sequence of bytes with no particular encoding. pub fn getCwd(out_buffer: []u8) ![]u8 { - return os.getcwd(out_buffer); + return posix.getcwd(out_buffer); } +pub const GetCwdAllocError = Allocator.Error || posix.GetCwdError; + /// Caller must free the returned memory. /// On Windows, the result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). /// On other platforms, the result is an opaque sequence of bytes with no particular encoding. @@ -34,7 +40,7 @@ pub fn getCwdAlloc(allocator: Allocator) ![]u8 { var current_buf: []u8 = &stack_buf; while (true) { - if (os.getcwd(current_buf)) |slice| { + if (posix.getcwd(current_buf)) |slice| { return allocator.dupe(u8, slice); } else |err| switch (err) { error.NameTooLong => { @@ -51,7 +57,7 @@ pub fn getCwdAlloc(allocator: Allocator) ![]u8 { } test getCwdAlloc { - if (builtin.os.tag == .wasi) return error.SkipZigTest; + if (native_os == .wasi) return error.SkipZigTest; const cwd = try getCwdAlloc(testing.allocator); testing.allocator.free(cwd); @@ -72,13 +78,13 @@ pub const EnvMap = struct { pub const EnvNameHashContext = struct { fn upcase(c: u21) u21 { if (c <= std.math.maxInt(u16)) - return std.os.windows.ntdll.RtlUpcaseUnicodeChar(@as(u16, @intCast(c))); + return windows.ntdll.RtlUpcaseUnicodeChar(@as(u16, @intCast(c))); return c; } pub fn hash(self: @This(), s: []const u8) u64 { _ = self; - if (builtin.os.tag == .windows) { + if (native_os == .windows) { var h = std.hash.Wyhash.init(0); var it = std.unicode.Wtf8View.initUnchecked(s).iterator(); while (it.nextCodepoint()) |cp| { @@ -96,7 +102,7 @@ pub const EnvMap = struct { pub fn eql(self: @This(), a: []const u8, b: []const u8) bool { _ = self; - if (builtin.os.tag == .windows) { + if (native_os == .windows) { var it_a = std.unicode.Wtf8View.initUnchecked(a).iterator(); var it_b = std.unicode.Wtf8View.initUnchecked(b).iterator(); while (true) { @@ -228,7 +234,7 @@ test "EnvMap" { try testing.expectEqual(@as(EnvMap.Size, 2), env.count()); // case insensitivity on Windows only - if (builtin.os.tag == .windows) { + if (native_os == .windows) { try testing.expectEqualStrings("1", env.get("something_New_aNd_LONGER").?); } else { try testing.expect(null == env.get("something_New_aNd_LONGER")); @@ -248,7 +254,7 @@ test "EnvMap" { try testing.expectEqual(@as(EnvMap.Size, 1), env.count()); - if (builtin.os.tag == .windows) { + if (native_os == .windows) { // test Unicode case-insensitivity on Windows try env.put("КИРиллИЦА", "something else"); try testing.expectEqualStrings("something else", env.get("кириллица").?); @@ -279,8 +285,8 @@ pub fn getEnvMap(allocator: Allocator) GetEnvMapError!EnvMap { var result = EnvMap.init(allocator); errdefer result.deinit(); - if (builtin.os.tag == .windows) { - const ptr = os.windows.peb().ProcessParameters.Environment; + if (native_os == .windows) { + const ptr = windows.peb().ProcessParameters.Environment; var i: usize = 0; while (ptr[i] != 0) { @@ -310,13 +316,13 @@ pub fn getEnvMap(allocator: Allocator) GetEnvMapError!EnvMap { try result.putMove(key, value); } return result; - } else if (builtin.os.tag == .wasi and !builtin.link_libc) { + } else if (native_os == .wasi and !builtin.link_libc) { var environ_count: usize = undefined; var environ_buf_size: usize = undefined; - const environ_sizes_get_ret = os.wasi.environ_sizes_get(&environ_count, &environ_buf_size); + const environ_sizes_get_ret = std.os.wasi.environ_sizes_get(&environ_count, &environ_buf_size); if (environ_sizes_get_ret != .SUCCESS) { - return os.unexpectedErrno(environ_sizes_get_ret); + return posix.unexpectedErrno(environ_sizes_get_ret); } if (environ_count == 0) { @@ -328,9 +334,9 @@ pub fn getEnvMap(allocator: Allocator) GetEnvMapError!EnvMap { const environ_buf = try allocator.alloc(u8, environ_buf_size); defer allocator.free(environ_buf); - const environ_get_ret = os.wasi.environ_get(environ.ptr, environ_buf.ptr); + const environ_get_ret = std.os.wasi.environ_get(environ.ptr, environ_buf.ptr); if (environ_get_ret != .SUCCESS) { - return os.unexpectedErrno(environ_get_ret); + return posix.unexpectedErrno(environ_get_ret); } for (environ) |env| { @@ -356,7 +362,7 @@ pub fn getEnvMap(allocator: Allocator) GetEnvMapError!EnvMap { } return result; } else { - for (os.environ) |line| { + for (std.os.environ) |line| { var line_i: usize = 0; while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {} const key = line[0..line_i]; @@ -391,37 +397,37 @@ pub const GetEnvVarOwnedError = error{ /// On Windows, the value is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). /// On other platforms, the value is an opaque sequence of bytes with no particular encoding. pub fn getEnvVarOwned(allocator: Allocator, key: []const u8) GetEnvVarOwnedError![]u8 { - if (builtin.os.tag == .windows) { + if (native_os == .windows) { const result_w = blk: { var stack_alloc = std.heap.stackFallback(256 * @sizeOf(u16), allocator); const stack_allocator = stack_alloc.get(); const key_w = try std.unicode.wtf8ToWtf16LeAllocZ(stack_allocator, key); defer stack_allocator.free(key_w); - break :blk std.os.getenvW(key_w) orelse return error.EnvironmentVariableNotFound; + break :blk getenvW(key_w) orelse return error.EnvironmentVariableNotFound; }; // wtf16LeToWtf8Alloc can only fail with OutOfMemory return std.unicode.wtf16LeToWtf8Alloc(allocator, result_w); - } else if (builtin.os.tag == .wasi and !builtin.link_libc) { + } else if (native_os == .wasi and !builtin.link_libc) { var envmap = getEnvMap(allocator) catch return error.OutOfMemory; defer envmap.deinit(); const val = envmap.get(key) orelse return error.EnvironmentVariableNotFound; return allocator.dupe(u8, val); } else { - const result = os.getenv(key) orelse return error.EnvironmentVariableNotFound; + const result = posix.getenv(key) orelse return error.EnvironmentVariableNotFound; return allocator.dupe(u8, result); } } /// On Windows, `key` must be valid UTF-8. pub fn hasEnvVarConstant(comptime key: []const u8) bool { - if (builtin.os.tag == .windows) { + if (native_os == .windows) { const key_w = comptime std.unicode.utf8ToUtf16LeStringLiteral(key); - return std.os.getenvW(key_w) != null; - } else if (builtin.os.tag == .wasi and !builtin.link_libc) { + return getenvW(key_w) != null; + } else if (native_os == .wasi and !builtin.link_libc) { @compileError("hasEnvVarConstant is not supported for WASI without libc"); } else { - return os.getenv(key) != null; + return posix.getenv(key) != null; } } @@ -436,19 +442,63 @@ pub const HasEnvVarError = error{ /// On Windows, if `key` is not valid [WTF-8](https://simonsapin.github.io/wtf-8/), /// then `error.InvalidWtf8` is returned. pub fn hasEnvVar(allocator: Allocator, key: []const u8) HasEnvVarError!bool { - if (builtin.os.tag == .windows) { + if (native_os == .windows) { var stack_alloc = std.heap.stackFallback(256 * @sizeOf(u16), allocator); const stack_allocator = stack_alloc.get(); const key_w = try std.unicode.wtf8ToWtf16LeAllocZ(stack_allocator, key); defer stack_allocator.free(key_w); - return std.os.getenvW(key_w) != null; - } else if (builtin.os.tag == .wasi and !builtin.link_libc) { + return getenvW(key_w) != null; + } else if (native_os == .wasi and !builtin.link_libc) { var envmap = getEnvMap(allocator) catch return error.OutOfMemory; defer envmap.deinit(); return envmap.getPtr(key) != null; } else { - return os.getenv(key) != null; + return posix.getenv(key) != null; + } +} + +/// Windows-only. Get an environment variable with a null-terminated, WTF-16 encoded name. +/// +/// This function performs a Unicode-aware case-insensitive lookup using RtlEqualUnicodeString. +/// +/// See also: +/// * `std.posix.getenv` +/// * `getEnvMap` +/// * `getEnvVarOwned` +/// * `hasEnvVarConstant` +/// * `hasEnvVar` +pub fn getenvW(key: [*:0]const u16) ?[:0]const u16 { + if (native_os != .windows) { + @compileError("Windows-only"); + } + const key_slice = mem.sliceTo(key, 0); + const ptr = windows.peb().ProcessParameters.Environment; + var i: usize = 0; + while (ptr[i] != 0) { + const key_start = i; + + // There are some special environment variables that start with =, + // so we need a special case to not treat = as a key/value separator + // if it's the first character. + // https://devblogs.microsoft.com/oldnewthing/20100506-00/?p=14133 + if (ptr[key_start] == '=') i += 1; + + while (ptr[i] != 0 and ptr[i] != '=') : (i += 1) {} + const this_key = ptr[key_start..i]; + + if (ptr[i] == '=') i += 1; + + const value_start = i; + while (ptr[i] != 0) : (i += 1) {} + const this_value = ptr[value_start..i :0]; + + if (windows.eqlIgnoreCaseWTF16(key_slice, this_key)) { + return this_value; + } + + i += 1; // skip over null byte } + return null; } test getEnvVarOwned { @@ -459,7 +509,7 @@ test getEnvVarOwned { } test hasEnvVarConstant { - if (builtin.os.tag == .wasi and !builtin.link_libc) return error.SkipZigTest; + if (native_os == .wasi and !builtin.link_libc) return error.SkipZigTest; try testing.expect(!hasEnvVarConstant("BADENV")); } @@ -478,14 +528,14 @@ pub const ArgIteratorPosix = struct { pub fn init() ArgIteratorPosix { return ArgIteratorPosix{ .index = 0, - .count = os.argv.len, + .count = std.os.argv.len, }; } pub fn next(self: *ArgIteratorPosix) ?[:0]const u8 { if (self.index == self.count) return null; - const s = os.argv[self.index]; + const s = std.os.argv[self.index]; self.index += 1; return mem.sliceTo(s, 0); } @@ -503,7 +553,7 @@ pub const ArgIteratorWasi = struct { index: usize, args: [][:0]u8, - pub const InitError = error{OutOfMemory} || os.UnexpectedError; + pub const InitError = error{OutOfMemory} || posix.UnexpectedError; /// You must call deinit to free the internal buffer of the /// iterator after you are done. @@ -517,13 +567,12 @@ pub const ArgIteratorWasi = struct { } fn internalInit(allocator: Allocator) InitError![][:0]u8 { - const w = os.wasi; var count: usize = undefined; var buf_size: usize = undefined; - switch (w.args_sizes_get(&count, &buf_size)) { + switch (std.os.wasi.args_sizes_get(&count, &buf_size)) { .SUCCESS => {}, - else => |err| return os.unexpectedErrno(err), + else => |err| return posix.unexpectedErrno(err), } if (count == 0) { @@ -535,9 +584,9 @@ pub const ArgIteratorWasi = struct { const argv_buf = try allocator.alloc(u8, buf_size); - switch (w.args_get(argv.ptr, argv_buf.ptr)) { + switch (std.os.wasi.args_get(argv.ptr, argv_buf.ptr)) { .SUCCESS => {}, - else => |err| return os.unexpectedErrno(err), + else => |err| return posix.unexpectedErrno(err), } var result_args = try allocator.alloc([:0]u8, count); @@ -1007,7 +1056,7 @@ pub fn ArgIteratorGeneral(comptime options: ArgIteratorGeneralOptions) type { /// Cross-platform command line argument iterator. pub const ArgIterator = struct { - const InnerType = switch (builtin.os.tag) { + const InnerType = switch (native_os) { .windows => ArgIteratorWindows, .wasi => if (builtin.link_libc) ArgIteratorPosix else ArgIteratorWasi, else => ArgIteratorPosix, @@ -1018,10 +1067,10 @@ pub const ArgIterator = struct { /// Initialize the args iterator. Consider using initWithAllocator() instead /// for cross-platform compatibility. pub fn init() ArgIterator { - if (builtin.os.tag == .wasi) { + if (native_os == .wasi) { @compileError("In WASI, use initWithAllocator instead."); } - if (builtin.os.tag == .windows) { + if (native_os == .windows) { @compileError("In Windows, use initWithAllocator instead."); } @@ -1032,11 +1081,11 @@ pub const ArgIterator = struct { /// You must deinitialize iterator's internal buffers by calling `deinit` when done. pub fn initWithAllocator(allocator: Allocator) InitError!ArgIterator { - if (builtin.os.tag == .wasi and !builtin.link_libc) { + if (native_os == .wasi and !builtin.link_libc) { return ArgIterator{ .inner = try InnerType.init(allocator) }; } - if (builtin.os.tag == .windows) { - const cmd_line_w = os.windows.kernel32.GetCommandLineW(); + if (native_os == .windows) { + const cmd_line_w = windows.kernel32.GetCommandLineW(); return ArgIterator{ .inner = try InnerType.init(allocator, cmd_line_w) }; } @@ -1061,11 +1110,11 @@ pub const ArgIterator = struct { /// was created with `initWithAllocator` function. pub fn deinit(self: *ArgIterator) void { // Unless we're targeting WASI or Windows, this is a no-op. - if (builtin.os.tag == .wasi and !builtin.link_libc) { + if (native_os == .wasi and !builtin.link_libc) { self.inner.deinit(); } - if (builtin.os.tag == .windows) { + if (native_os == .windows) { self.inner.deinit(); } } @@ -1334,13 +1383,13 @@ fn testResponseFileCmdLine(input_cmd_line: []const u8, expected_args: []const [] } pub const UserInfo = struct { - uid: os.uid_t, - gid: os.gid_t, + uid: posix.uid_t, + gid: posix.gid_t, }; /// POSIX function which gets a uid from username. pub fn getUserInfo(name: []const u8) !UserInfo { - return switch (builtin.os.tag) { + return switch (native_os) { .linux, .macos, .watchos, @@ -1376,8 +1425,8 @@ pub fn posixGetUserInfo(name: []const u8) !UserInfo { var buf: [std.mem.page_size]u8 = undefined; var name_index: usize = 0; var state = State.Start; - var uid: os.uid_t = 0; - var gid: os.gid_t = 0; + var uid: posix.uid_t = 0; + var gid: posix.gid_t = 0; while (true) { const amt_read = try reader.read(buf[0..]); @@ -1462,36 +1511,36 @@ pub fn posixGetUserInfo(name: []const u8) !UserInfo { } pub fn getBaseAddress() usize { - switch (builtin.os.tag) { + switch (native_os) { .linux => { - const base = os.system.getauxval(std.elf.AT_BASE); + const base = std.os.linux.getauxval(std.elf.AT_BASE); if (base != 0) { return base; } - const phdr = os.system.getauxval(std.elf.AT_PHDR); + const phdr = std.os.linux.getauxval(std.elf.AT_PHDR); return phdr - @sizeOf(std.elf.Ehdr); }, .macos, .freebsd, .netbsd => { return @intFromPtr(&std.c._mh_execute_header); }, - .windows => return @intFromPtr(os.windows.kernel32.GetModuleHandleW(null)), + .windows => return @intFromPtr(windows.kernel32.GetModuleHandleW(null)), else => @compileError("Unsupported OS"), } } /// Tells whether calling the `execv` or `execve` functions will be a compile error. -pub const can_execv = switch (builtin.os.tag) { +pub const can_execv = switch (native_os) { .windows, .haiku, .wasi => false, else => true, }; /// Tells whether spawning child processes is supported (e.g. via ChildProcess) -pub const can_spawn = switch (builtin.os.tag) { +pub const can_spawn = switch (native_os) { .wasi, .watchos, .tvos => false, else => true, }; -pub const ExecvError = std.os.ExecveError || error{OutOfMemory}; +pub const ExecvError = std.posix.ExecveError || error{OutOfMemory}; /// Replaces the current process image with the executed process. /// This function must allocate memory to add a null terminating bytes on path and each arg. @@ -1500,7 +1549,7 @@ pub const ExecvError = std.os.ExecveError || error{OutOfMemory}; /// `argv[0]` is the executable path. /// This function also uses the PATH environment variable to get the full path to the executable. /// Due to the heap-allocation, it is illegal to call this function in a fork() child. -/// For that use case, use the `std.os` functions directly. +/// For that use case, use the `std.posix` functions directly. pub fn execv(allocator: Allocator, argv: []const []const u8) ExecvError { return execve(allocator, argv, null); } @@ -1512,7 +1561,7 @@ pub fn execv(allocator: Allocator, argv: []const []const u8) ExecvError { /// `argv[0]` is the executable path. /// This function also uses the PATH environment variable to get the full path to the executable. /// Due to the heap-allocation, it is illegal to call this function in a fork() child. -/// For that use case, use the `std.os` functions directly. +/// For that use case, use the `std.posix` functions directly. pub fn execve( allocator: Allocator, argv: []const []const u8, @@ -1536,14 +1585,14 @@ pub fn execve( } else if (builtin.output_mode == .Exe) { // Then we have Zig start code and this works. // TODO type-safety for null-termination of `os.environ`. - break :m @as([*:null]const ?[*:0]const u8, @ptrCast(os.environ.ptr)); + break :m @as([*:null]const ?[*:0]const u8, @ptrCast(std.os.environ.ptr)); } else { // TODO come up with a solution for this. @compileError("missing std lib enhancement: std.process.execv implementation has no way to collect the environment variables to forward to the child process"); } }; - return os.execvpeZ_expandArg0(.no_expand, argv_buf.ptr[0].?, argv_buf.ptr, envp); + return posix.execvpeZ_expandArg0(.no_expand, argv_buf.ptr[0].?, argv_buf.ptr, envp); } pub const TotalSystemMemoryError = error{ @@ -1555,14 +1604,14 @@ pub const TotalSystemMemoryError = error{ /// and Linux's /proc/meminfo reporting more memory when /// using QEMU user mode emulation. pub fn totalSystemMemory() TotalSystemMemoryError!u64 { - switch (builtin.os.tag) { + switch (native_os) { .linux => { return totalSystemMemoryLinux() catch return error.UnknownTotalSystemMemory; }, .freebsd => { var physmem: c_ulong = undefined; var len: usize = @sizeOf(c_ulong); - os.sysctlbynameZ("hw.physmem", &physmem, &len, null, 0) catch |err| switch (err) { + posix.sysctlbynameZ("hw.physmem", &physmem, &len, null, 0) catch |err| switch (err) { error.NameTooLong, error.UnknownName => unreachable, else => return error.UnknownTotalSystemMemory, }; @@ -1570,12 +1619,12 @@ pub fn totalSystemMemory() TotalSystemMemoryError!u64 { }, .openbsd => { const mib: [2]c_int = [_]c_int{ - std.os.CTL.HW, - std.os.HW.PHYSMEM64, + posix.CTL.HW, + posix.HW.PHYSMEM64, }; var physmem: i64 = undefined; var len: usize = @sizeOf(@TypeOf(physmem)); - std.os.sysctl(&mib, &physmem, &len, null, 0) catch |err| switch (err) { + posix.sysctl(&mib, &physmem, &len, null, 0) catch |err| switch (err) { error.NameTooLong => unreachable, // constant, known good value error.PermissionDenied => unreachable, // only when setting values, error.SystemResources => unreachable, // memory already on the stack @@ -1586,11 +1635,11 @@ pub fn totalSystemMemory() TotalSystemMemoryError!u64 { return @as(u64, @bitCast(physmem)); }, .windows => { - var sbi: std.os.windows.SYSTEM_BASIC_INFORMATION = undefined; - const rc = std.os.windows.ntdll.NtQuerySystemInformation( + var sbi: windows.SYSTEM_BASIC_INFORMATION = undefined; + const rc = windows.ntdll.NtQuerySystemInformation( .SystemBasicInformation, &sbi, - @sizeOf(std.os.windows.SYSTEM_BASIC_INFORMATION), + @sizeOf(windows.SYSTEM_BASIC_INFORMATION), null, ); if (rc != .SUCCESS) { |
