feature. See also
. The project being documented here (as the example) is the Zig library itself.
File
Code
const builtin = @import("builtin");
const native_os = builtin.os.tag;
const std = @import("std.zig");
const Io = std.Io;
const Dir = std.Io.Dir;
const File = std.Io.File;
const fs = std.fs;
const mem = std.mem;
const math = std.math;
const Allocator = std.mem.Allocator;
const assert = std.debug.assert;
const testing = std.testing;
const posix = std.posix;
const windows = std.os.windows;
const unicode = std.unicode;
const max_path_bytes = std.fs.max_path_bytes;
pub const Child = @import("process/Child.zig");
pub const Args = @import("process/Args.zig");
pub const Environ = @import("process/Environ.zig");
pub const Preopens = @import("process/Preopens.zig");
pub const Init = struct {
minimal: Minimal,
arena: *std.heap.ArenaAllocator,
gpa: Allocator,
io: Io,
environ_map: *Environ.Map,
preopens: Preopens,
pub const Minimal = struct {
environ: Environ,
args: Args,
};
};
pub const CurrentPathError = error{
NameTooLong,
CurrentDirUnlinked,
} || Io.Cancelable || Io.UnexpectedError;
pub fn currentPath(io: Io, buffer: []u8) CurrentPathError!usize {
return io.vtable.processCurrentPath(io.userdata, buffer);
}
pub const CurrentPathAllocError = Allocator.Error || error{
CurrentDirUnlinked,
} || Io.Cancelable || Io.UnexpectedError;
pub fn currentPathAlloc(io: Io, allocator: Allocator) CurrentPathAllocError![:0]u8 {
var buffer: [max_path_bytes]u8 = undefined;
const n = currentPath(io, &buffer) catch |err| switch (err) {
error.NameTooLong => unreachable,
else => |e| return e,
};
return allocator.dupeSentinel(u8, buffer[0..n], 0);
}
test currentPathAlloc {
const cwd = try currentPathAlloc(testing.io, testing.allocator);
testing.allocator.free(cwd);
}
pub const UserInfo = struct {
uid: posix.uid_t,
gid: posix.gid_t,
};
pub fn getUserInfo(name: []const u8) !UserInfo {
return switch (native_os) {
.linux,
.driverkit,
.ios,
.maccatalyst,
.macos,
.tvos,
.visionos,
.watchos,
.freebsd,
.netbsd,
.openbsd,
.haiku,
.illumos,
.serenity,
=> posixGetUserInfo(name),
else => @compileError("Unsupported OS"),
};
}
pub fn posixGetUserInfo(io: Io, name: []const u8) !UserInfo {
const file = try Io.Dir.openFileAbsolute(io, "/etc/passwd", .{});
defer file.close(io);
var buffer: [4096]u8 = undefined;
var file_reader = file.reader(&buffer);
return posixGetUserInfoPasswdStream(name, &file_reader.interface) catch |err| switch (err) {
error.ReadFailed => return file_reader.err.?,
error.EndOfStream => return error.UserNotFound,
error.CorruptPasswordFile => |e| return e,
};
}
fn posixGetUserInfoPasswdStream(name: []const u8, reader: *std.Io.Reader) !UserInfo {
const State = enum {
start,
wait_for_next_line,
skip_password,
read_user_id,
read_group_id,
};
var name_index: usize = 0;
var uid: posix.uid_t = 0;
var gid: posix.gid_t = 0;
sw: switch (State.start) {
.start => switch (try reader.takeByte()) {
':' => {
if (name_index == name.len) {
continue :sw .skip_password;
} else {
continue :sw .wait_for_next_line;
}
},
'\n' => return error.CorruptPasswordFile,
else => |byte| {
if (name_index == name.len or name[name_index] != byte) {
continue :sw .wait_for_next_line;
}
name_index += 1;
continue :sw .start;
},
},
.wait_for_next_line => switch (try reader.takeByte()) {
'\n' => {
name_index = 0;
continue :sw .start;
},
else => continue :sw .wait_for_next_line,
},
.skip_password => switch (try reader.takeByte()) {
'\n' => return error.CorruptPasswordFile,
':' => {
continue :sw .read_user_id;
},
else => continue :sw .skip_password,
},
.read_user_id => switch (try reader.takeByte()) {
':' => {
continue :sw .read_group_id;
},
'\n' => return error.CorruptPasswordFile,
else => |byte| {
const digit = switch (byte) {
'0'...'9' => byte - '0',
else => return error.CorruptPasswordFile,
};
{
const ov = @mulWithOverflow(uid, 10);
if (ov[1] != 0) return error.CorruptPasswordFile;
uid = ov[0];
}
{
const ov = @addWithOverflow(uid, digit);
if (ov[1] != 0) return error.CorruptPasswordFile;
uid = ov[0];
}
continue :sw .read_user_id;
},
},
.read_group_id => switch (try reader.takeByte()) {
'\n', ':' => return .{
.uid = uid,
.gid = gid,
},
else => |byte| {
const digit = switch (byte) {
'0'...'9' => byte - '0',
else => return error.CorruptPasswordFile,
};
{
const ov = @mulWithOverflow(gid, 10);
if (ov[1] != 0) return error.CorruptPasswordFile;
gid = ov[0];
}
{
const ov = @addWithOverflow(gid, digit);
if (ov[1] != 0) return error.CorruptPasswordFile;
gid = ov[0];
}
continue :sw .read_group_id;
},
},
}
comptime unreachable;
}
pub fn getBaseAddress() usize {
switch (native_os) {
.linux => {
const phdrs = std.posix.getSelfPhdrs();
var base: usize = 0;
for (phdrs) |phdr| switch (phdr.type) {
.LOAD => return base + phdr.vaddr,
.PHDR => base = @intFromPtr(phdrs.ptr) - phdr.vaddr,
else => {},
} else unreachable;
},
.driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => {
return @intFromPtr(&std.c._mh_execute_header);
},
.windows => return @intFromPtr(windows.peb().ImageBaseAddress),
else => @compileError("Unsupported OS"),
}
}
pub const can_replace = switch (native_os) {
.windows, .haiku, .wasi => false,
else => true,
};
pub const can_spawn = switch (native_os) {
.wasi, .ios, .tvos, .visionos, .watchos => false,
else => true,
};
pub const ReplaceError = error{
OperationUnsupported,
SystemResources,
AccessDenied,
PermissionDenied,
InvalidExe,
FileSystem,
IsDir,
FileNotFound,
NotDir,
FileBusy,
ProcessFdQuotaExceeded,
SystemFdQuotaExceeded,
} || Allocator.Error || Io.Dir.PathNameError || Io.Cancelable || Io.UnexpectedError;
pub const ReplaceOptions = struct {
argv: []const []const u8,
expand_arg0: ArgExpansion = .no_expand,
environ_map: ?*const Environ.Map = null,
};
pub fn replace(io: Io, options: ReplaceOptions) ReplaceError {
return io.vtable.processReplace(io.userdata, options);
}
pub fn replacePath(io: Io, dir: Io.Dir, options: ReplaceOptions) ReplaceError {
return io.vtable.processReplacePath(io.userdata, dir, options);
}
pub const ArgExpansion = enum { expand, no_expand };
pub const WindowsExtension = enum {
bat,
cmd,
com,
exe,
pub const max_len = 3;
};
pub const SpawnError = error{
OperationUnsupported,
OutOfMemory,
NoDevice,
InvalidWtf8,
InvalidBatchScriptArg,
SystemResources,
AccessDenied,
PermissionDenied,
InvalidExe,
FileSystem,
IsDir,
FileNotFound,
NotDir,
FileBusy,
ProcessFdQuotaExceeded,
SystemFdQuotaExceeded,
ResourceLimitReached,
InvalidUserId,
InvalidProcessGroupId,
SymLinkLoop,
InvalidName,
ProcessAlreadyExec,
UnrecognizedVolume,
} || Io.File.OpenError || Io.Dir.PathNameError || Io.Cancelable || Io.UnexpectedError;
pub const SpawnOptions = struct {
argv: []const []const u8,
cwd: Child.Cwd = .inherit,
environ_map: ?*const Environ.Map = null,
expand_arg0: ArgExpansion = .no_expand,
progress_node: std.Progress.Node = std.Progress.Node.none,
stdin: StdIo = .inherit,
stdout: StdIo = .inherit,
stderr: StdIo = .inherit,
request_resource_usage_statistics: bool = false,
uid: ?posix.uid_t = null,
gid: ?posix.gid_t = null,
pgid: ?posix.pid_t = null,
start_suspended: bool = false,
create_no_window: bool = false,
disable_aslr: bool = false,
pub const StdIo = union(enum) {
inherit,
file: File,
ignore,
pipe,
close,
};
};
pub fn spawn(io: Io, options: SpawnOptions) SpawnError!Child {
return io.vtable.processSpawn(io.userdata, options);
}
pub fn spawnPath(io: Io, dir: Io.Dir, options: SpawnOptions) SpawnError!Child {
return io.vtable.processSpawnPath(io.userdata, dir, options);
}
pub const RunError = error{
StreamTooLong,
} || SpawnError || Io.File.MultiReader.UnendingError || Io.Timeout.Error;
pub const RunOptions = struct {
argv: []const []const u8,
stderr_limit: Io.Limit = .unlimited,
stdout_limit: Io.Limit = .unlimited,
reserve_amount: usize = 64,
cwd: Child.Cwd = .inherit,
environ_map: ?*const Environ.Map = null,
expand_arg0: ArgExpansion = .no_expand,
progress_node: std.Progress.Node = std.Progress.Node.none,
create_no_window: bool = true,
disable_aslr: bool = false,
timeout: Io.Timeout = .none,
};
pub const RunResult = struct {
term: Child.Term,
stdout: []u8,
stderr: []u8,
};
pub fn run(gpa: Allocator, io: Io, options: RunOptions) RunError!RunResult {
var child = try spawn(io, .{
.argv = options.argv,
.cwd = options.cwd,
.environ_map = options.environ_map,
.expand_arg0 = options.expand_arg0,
.progress_node = options.progress_node,
.create_no_window = options.create_no_window,
.disable_aslr = options.disable_aslr,
.stdin = .ignore,
.stdout = .pipe,
.stderr = .pipe,
});
defer child.kill(io);
var multi_reader_buffer: Io.File.MultiReader.Buffer(2) = undefined;
var multi_reader: Io.File.MultiReader = undefined;
multi_reader.init(gpa, io, multi_reader_buffer.toStreams(), &.{ child.stdout.?, child.stderr.? });
defer multi_reader.deinit();
const stdout_reader = multi_reader.reader(0);
const stderr_reader = multi_reader.reader(1);
while (multi_reader.fill(options.reserve_amount, options.timeout)) |_| {
if (options.stdout_limit.toInt()) |limit| {
if (stdout_reader.buffered().len > limit)
return error.StreamTooLong;
}
if (options.stderr_limit.toInt()) |limit| {
if (stderr_reader.buffered().len > limit)
return error.StreamTooLong;
}
} else |err| switch (err) {
error.EndOfStream => {},
else => |e| return e,
}
try multi_reader.checkAnyError();
const term = try child.wait(io);
const stdout_slice = try multi_reader.toOwnedSlice(0);
errdefer gpa.free(stdout_slice);
const stderr_slice = try multi_reader.toOwnedSlice(1);
errdefer gpa.free(stderr_slice);
return .{
.stdout = stdout_slice,
.stderr = stderr_slice,
.term = term,
};
}
pub const TotalSystemMemoryError = error{
UnknownTotalSystemMemory,
};
pub fn totalSystemMemory() TotalSystemMemoryError!u64 {
switch (native_os) {
.linux => {
var info: std.os.linux.Sysinfo = undefined;
const result: usize = std.os.linux.sysinfo(&info);
if (std.os.linux.errno(result) != .SUCCESS) {
return error.UnknownTotalSystemMemory;
}
return @as(u64, info.totalram) * info.mem_unit;
},
.dragonfly, .freebsd, .netbsd => {
const name = if (native_os == .netbsd) "hw.physmem64" else "hw.physmem";
var physmem: c_ulong = undefined;
var len: usize = @sizeOf(c_ulong);
switch (posix.errno(posix.system.sysctlbyname(name, &physmem, &len, null, 0))) {
.SUCCESS => return @intCast(physmem),
.FAULT => unreachable,
.PERM => unreachable,
.NOMEM => unreachable,
.NOENT => unreachable,
else => return error.UnknownTotalSystemMemory,
}
},
.driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => {
var physmem: u64 = undefined;
var len: usize = @sizeOf(u64);
switch (posix.errno(posix.system.sysctlbyname("hw.memsize", &physmem, &len, null, 0))) {
.SUCCESS => return physmem,
.FAULT => unreachable,
.PERM => unreachable,
.NOMEM => unreachable,
.NOENT => unreachable,
else => return error.UnknownTotalSystemMemory,
}
},
.openbsd => {
const mib: [2]c_int = [_]c_int{
posix.CTL.HW,
posix.HW.PHYSMEM64,
};
var physmem: i64 = undefined;
var len: usize = @sizeOf(@TypeOf(physmem));
posix.sysctl(&mib, &physmem, &len, null, 0) catch |err| switch (err) {
error.NameTooLong => unreachable,
error.PermissionDenied => unreachable,
error.SystemResources => unreachable,
error.UnknownName => unreachable,
else => return error.UnknownTotalSystemMemory,
};
assert(physmem >= 0);
return @as(u64, @bitCast(physmem));
},
.windows => {
var sbi: windows.SYSTEM.BASIC_INFORMATION = undefined;
const rc = windows.ntdll.NtQuerySystemInformation(
.Basic,
&sbi,
@sizeOf(windows.SYSTEM.BASIC_INFORMATION),
null,
);
if (rc != .SUCCESS) {
return error.UnknownTotalSystemMemory;
}
return @as(u64, sbi.NumberOfPhysicalPages) * sbi.PageSize;
},
else => return error.UnknownTotalSystemMemory,
}
}
pub fn cleanExit(io: Io) void {
if (builtin.mode == .debug) return;
_ = io.lockStderr(&.{}, .no_color) catch {};
exit(0);
}
pub fn raiseFileDescriptorLimit() void {
const have_rlimit = posix.rlimit_resource != void;
if (!have_rlimit) return;
var lim = posix.getrlimit(.NOFILE) catch return;
if (native_os.isDarwin()) {
// According to the man pages for setrlimit():
// setrlimit() now returns with errno set to EINVAL in places that historically succeeded.
// It no longer accepts "rlim_cur = RLIM.INFINITY" for RLIM.NOFILE.
// Use "rlim_cur = min(OPEN_MAX, rlim_max)".
lim.max = @min(std.c.OPEN_MAX, lim.max);
}
if (lim.cur == lim.max) return;
var min: posix.rlim_t = lim.cur;
var max: posix.rlim_t = 1 << 20;
if (lim.max != posix.RLIM.INFINITY) {
min = lim.max;
max = lim.max;
}
while (true) {
lim.cur = min + @divTrunc(max - min, 2);
if (posix.setrlimit(.NOFILE, lim)) |_| {
min = lim.cur;
} else |_| {
max = lim.cur;
}
if (min + 1 >= max) break;
}
}
test raiseFileDescriptorLimit {
raiseFileDescriptorLimit();
}
pub fn fatal(comptime format: []const u8, format_arguments: anytype) noreturn {
std.log.err(format, format_arguments);
exit(1);
}
pub const ExecutablePathBaseError = error{
FileNotFound,
AccessDenied,
OperationUnsupported,
NotDir,
SymLinkLoop,
InputOutput,
FileTooBig,
IsDir,
ProcessFdQuotaExceeded,
SystemFdQuotaExceeded,
NoDevice,
SystemResources,
NoSpaceLeft,
FileSystem,
BadPathName,
DeviceBusy,
PipeBusy,
NotLink,
PathAlreadyExists,
NetworkNotFound,
ProcessNotFound,
AntivirusInterference,
UnrecognizedVolume,
PermissionDenied,
} || Io.Cancelable || Io.UnexpectedError;
pub const ExecutablePathAllocError = ExecutablePathBaseError || Allocator.Error;
pub fn executablePathAlloc(io: Io, allocator: Allocator) ExecutablePathAllocError![:0]u8 {
var buffer: [max_path_bytes]u8 = undefined;
const n = executablePath(io, &buffer) catch |err| switch (err) {
error.NameTooLong => unreachable,
else => |e| return e,
};
return allocator.dupeSentinel(u8, buffer[0..n], 0);
}
pub const ExecutablePathError = ExecutablePathBaseError || error{NameTooLong};
pub fn executablePath(io: Io, out_buffer: []u8) ExecutablePathError!usize {
return io.vtable.processExecutablePath(io.userdata, out_buffer);
}
pub fn executableDirPath(io: Io, out_buffer: []u8) ExecutablePathError!usize {
const n = try executablePath(io, out_buffer);
// will not return null.
return std.fs.path.dirname(out_buffer[0..n]).?.len;
}
pub fn executableDirPathAlloc(io: Io, allocator: Allocator) ExecutablePathAllocError![]u8 {
var buffer: [max_path_bytes]u8 = undefined;
const dir_path_len = executableDirPath(io, &buffer) catch |err| switch (err) {
error.NameTooLong => unreachable,
else => |e| return e,
};
return allocator.dupe(u8, buffer[0..dir_path_len]);
}
pub const OpenExecutableError = File.OpenError || ExecutablePathError || File.LockError;
pub fn openExecutable(io: Io, flags: Dir.OpenFileOptions) OpenExecutableError!File {
return io.vtable.processExecutableOpen(io.userdata, flags);
}
pub fn abort() noreturn {
@branchHint(.cold);
// even when linking libc on Windows we use our own abort implementation.
// See https://github.com/ziglang/zig/issues/2071 for more details.
if (native_os == .windows) {
if (builtin.mode == .debug and windows.peb().BeingDebugged.toBool()) {
@breakpoint();
}
windows.ntdll.RtlExitUserProcess(3);
}
if (!builtin.link_libc and native_os == .linux) {
// "first unblocks the SIGABRT signal", but this is a footgun
// for user-defined signal handlers that want to restore some state in
// some program sections and crash in others.
// So, the user-installed SIGABRT handler is run, if present.
posix.raise(.ABRT) catch {};
const filledset = std.os.linux.sigfillset();
posix.sigprocmask(posix.SIG.BLOCK, &filledset, null);
if (!builtin.single_threaded) {
const global = struct {
var abort_entered: bool = false;
};
while (@cmpxchgWeak(bool, &global.abort_entered, false, true, .seq_cst, .seq_cst)) |_| {}
}
const sigact: posix.Sigaction = .{
.handler = .{ .handler = posix.SIG.DFL },
.mask = posix.sigemptyset(),
.flags = 0,
};
posix.sigaction(.ABRT, &sigact, null);
_ = std.os.linux.tkill(std.os.linux.gettid(), .ABRT);
var sigabrtmask = posix.sigemptyset();
posix.sigaddset(&sigabrtmask, .ABRT);
posix.sigprocmask(posix.SIG.UNBLOCK, &sigabrtmask, null);
@as(*allowzero volatile u8, @ptrFromInt(0)).* = 0;
posix.raise(.KILL) catch {};
exit(127);
}
switch (native_os) {
.uefi, .wasi, .emscripten, .cuda, .amdhsa, .other, .freestanding => @trap(),
else => posix.system.abort(),
}
}
pub fn exit(status: u8) noreturn {
if (builtin.link_libc) {
std.c.exit(status);
} else switch (native_os) {
.windows => windows.ntdll.RtlExitUserProcess(status),
.wasi => std.os.wasi.proc_exit(status),
.linux => {
if (!builtin.single_threaded) std.os.linux.exit_group(status);
posix.system.exit(status);
},
.uefi => {
const uefi = std.os.uefi;
// This call to exit should not fail, so we catch-ignore errors.
if (uefi.system_table.boot_services) |bs| {
bs.exit(uefi.handle, @fromBackingInt(@intCast(status)), null) catch {};
}
uefi.system_table.runtime_services.resetSystem(.cold, @fromBackingInt(@intCast(status)), null);
},
else => posix.system.exit(status),
}
}
pub const SetCurrentDirError = error{
AccessDenied,
BadPathName,
FileNotFound,
FileSystem,
NameTooLong,
NoDevice,
NotDir,
OperationUnsupported,
UnrecognizedVolume,
} || Io.Cancelable || Io.UnexpectedError;
pub fn setCurrentDir(io: Io, dir: Io.Dir) !void {
return io.vtable.processSetCurrentDir(io.userdata, dir);
}
pub const SetCurrentPathError = error{
AccessDenied,
SymLinkLoop,
SystemResources,
BadPathName,
FileNotFound,
FileSystem,
NoDevice,
NotDir,
NameTooLong,
OperationUnsupported,
InvalidWtf8,
} || Io.Cancelable || Io.UnexpectedError;
pub fn setCurrentPath(io: Io, path: []const u8) !void {
return io.vtable.processSetCurrentPath(io.userdata, path);
}
pub const LockMemoryError = error{
UnsupportedOperation,
PermissionDenied,
LockedMemoryLimitExceeded,
SystemResources,
} || Io.UnexpectedError;
pub const LockMemoryOptions = struct {
on_fault: bool = false,
};
pub fn lockMemory(memory: []align(std.heap.page_size_min) const u8, options: LockMemoryOptions) LockMemoryError!void {
if (native_os == .windows) {
}
if (!options.on_fault and @TypeOf(posix.system.mlock) != void) {
switch (posix.errno(posix.system.mlock(memory.ptr, memory.len))) {
.SUCCESS => return,
.INVAL => |err| return std.Io.Threaded.errnoBug(err),
.PERM => return error.PermissionDenied,
.NOMEM => return error.LockedMemoryLimitExceeded,
.AGAIN => return error.SystemResources,
else => |err| return posix.unexpectedErrno(err),
}
}
if (@TypeOf(posix.system.mlock2) != void) {
const flags: posix.MLOCK = .{ .ONFAULT = options.on_fault };
switch (posix.errno(posix.system.mlock2(memory.ptr, memory.len, flags))) {
.SUCCESS => return,
.INVAL => |err| return std.Io.Threaded.errnoBug(err),
.PERM => return error.PermissionDenied,
.NOMEM => return error.LockedMemoryLimitExceeded,
.AGAIN => return error.SystemResources,
else => |err| return posix.unexpectedErrno(err),
}
}
return error.UnsupportedOperation;
}
pub const UnlockMemoryError = error{
PermissionDenied,
OutOfMemory,
SystemResources,
} || Io.UnexpectedError;
pub fn unlockMemory(memory: []align(std.heap.page_size_min) const u8) UnlockMemoryError!void {
if (@TypeOf(posix.system.munlock) == void) return;
switch (posix.errno(posix.system.munlock(memory.ptr, memory.len))) {
.SUCCESS => return,
.INVAL => |err| return std.Io.Threaded.errnoBug(err),
.PERM => return error.PermissionDenied,
.NOMEM => return error.OutOfMemory,
.AGAIN => return error.SystemResources,
else => |err| return posix.unexpectedErrno(err),
}
}
pub const LockMemoryAllOptions = struct {
current: bool = false,
future: bool = false,
on_fault: bool = false,
};
pub fn lockMemoryAll(options: LockMemoryAllOptions) LockMemoryError!void {
if (@TypeOf(posix.system.mlockall) == void) return error.UnsupportedOperation;
var flags: posix.MCL = .{
.CURRENT = options.current,
.FUTURE = options.future,
};
if (options.on_fault) {
assert(options.current or options.future);
if (@hasField(posix.MCL, "ONFAULT")) {
flags.ONFAULT = true;
} else {
return error.UnsupportedOperation;
}
}
switch (posix.errno(posix.system.mlockall(flags))) {
.SUCCESS => return,
.INVAL => |err| return std.Io.Threaded.errnoBug(err),
.PERM => return error.PermissionDenied,
.NOMEM => return error.LockedMemoryLimitExceeded,
.AGAIN => return error.SystemResources,
else => |err| return posix.unexpectedErrno(err),
}
}
pub fn unlockMemoryAll() UnlockMemoryError!void {
if (@TypeOf(posix.system.munlockall) == void) return;
switch (posix.errno(posix.system.munlockall())) {
.SUCCESS => return,
.PERM => return error.PermissionDenied,
.NOMEM => return error.OutOfMemory,
.AGAIN => return error.SystemResources,
else => |err| return posix.unexpectedErrno(err),
}
}
pub const ProtectMemoryError = error{
UnsupportedOperation,
PermissionDenied,
AccessDenied,
OutOfMemory,
} || Io.UnexpectedError;
pub const MemoryProtection = packed struct(u3) {
read: bool = false,
write: bool = false,
execute: bool = false,
};
pub fn protectMemory(memory: []align(std.heap.page_size_min) u8, protection: MemoryProtection) ProtectMemoryError!void {
if (native_os == .windows) {
var addr = memory.ptr;
var size = memory.len;
var old: windows.PAGE = undefined;
const current_process: windows.HANDLE = @ptrFromInt(@as(usize, @bitCast(@as(isize, -1))));
const new = windows.PAGE.fromProtection(protection) orelse return error.AccessDenied;
switch (windows.ntdll.NtProtectVirtualMemory(current_process, @ptrCast(&addr), &size, new, &old)) {
.SUCCESS => return,
.INVALID_ADDRESS => return error.AccessDenied,
else => |st| return windows.unexpectedStatus(st),
}
} else if (posix.PROT != void) {
const flags: posix.PROT = .{
.READ = protection.read,
.WRITE = protection.write,
.EXEC = protection.execute,
};
switch (posix.errno(posix.system.mprotect(memory.ptr, memory.len, flags))) {
.SUCCESS => return,
.PERM => return error.PermissionDenied,
.INVAL => |err| return std.Io.Threaded.errnoBug(err),
.ACCES => return error.AccessDenied,
.NOMEM => return error.OutOfMemory,
else => |err| return posix.unexpectedErrno(err),
}
}
return error.UnsupportedOperation;
}
var test_page: [std.heap.page_size_max]u8 align(std.heap.page_size_max) = undefined;
test lockMemory {
lockMemory(&test_page, .{}) catch return error.SkipZigTest;
unlockMemory(&test_page) catch return error.SkipZigTest;
}
test lockMemoryAll {
lockMemoryAll(.{ .current = true }) catch return error.SkipZigTest;
unlockMemoryAll() catch return error.SkipZigTest;
}
test protectMemory {
protectMemory(&test_page, .{}) catch return error.SkipZigTest;
protectMemory(&test_page, .{ .read = true, .write = true }) catch return error.SkipZigTest;
}
test {
_ = Child;
_ = Args;
_ = Environ;
_ = Preopens;
}