626 lines
18 KiB
Plaintext
626 lines
18 KiB
Plaintext
const std = #import("std");
|
|
const Allocator = std.Allocator;
|
|
const assert = std.assert;
|
|
const builtin = #import("builtin");
|
|
const current = builtin.os;
|
|
const link_libc = builtin.link_libc;
|
|
|
|
const linux = #import("os/linux.nat");
|
|
const macos = #import("os/macos.nat");
|
|
const windows = #import("os/windows.nat");
|
|
|
|
const c = std.c;
|
|
|
|
const system = switch (link_libc) {
|
|
true => c,
|
|
false => switch (current) {
|
|
.linux => linux,
|
|
.macos => c,
|
|
.windows => windows,
|
|
},
|
|
};
|
|
|
|
const unwrap_syscall = system.unwrap_syscall;
|
|
|
|
const exit = fn(exit_code: s32) noreturn {
|
|
switch (current) {
|
|
.linux => _ = #syscall(#cast(linux.Syscall.exit_group), #cast(exit_code)),
|
|
.macos => system.exit(exit_code),
|
|
.windows => windows.ExitProcess(#cast(exit_code)),
|
|
}
|
|
}
|
|
|
|
const max_file_operation_byte_count = switch (current) {
|
|
.linux => 0x7ffff000,
|
|
.macos => 0x7fffffff,
|
|
else => #error("OS not supported"),
|
|
};
|
|
|
|
const FileDescriptor = struct{
|
|
handle: system.FileDescriptor,
|
|
|
|
const ReadError = error{
|
|
failed,
|
|
};
|
|
|
|
const read = fn(file_descriptor: FileDescriptor, bytes: []u8) ReadError!usize {
|
|
if (bytes.length > 0) {
|
|
switch (current) {
|
|
.linux, .macos => {
|
|
const len: usize = #min(max_file_operation_byte_count, bytes.length);
|
|
const syscall_result = system.read(file_descriptor.handle, bytes.pointer, bytes.length);
|
|
const byte_count = unwrap_syscall(syscall_result) catch |err| switch (err) {
|
|
else => return ReadError.failed,
|
|
};
|
|
return byte_count;
|
|
},
|
|
else => #error("OS not supported"),
|
|
}
|
|
} else {
|
|
const result: usize = 0;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
const read_all = fn(file_descriptor: FileDescriptor, bytes: []u8) ReadError!void {
|
|
var bytes_read: usize = 0;
|
|
|
|
while (bytes_read < bytes.length) {
|
|
const iteration_read_byte_count = try file_descriptor.read(bytes = bytes[bytes_read..]);
|
|
bytes_read += iteration_read_byte_count;
|
|
}
|
|
|
|
assert(bytes_read == bytes.length);
|
|
}
|
|
|
|
const WriteError = error{
|
|
write_failed,
|
|
};
|
|
|
|
const write = fn (file_descriptor: FileDescriptor, bytes: []const u8) WriteError!usize {
|
|
switch (current) {
|
|
.linux => {
|
|
const length: usize = #min(max_file_operation_byte_count, bytes.length);
|
|
const syscall_result = system.write(file_descriptor.handle, bytes[0..length]);
|
|
const byte_count = unwrap_syscall(syscall_result) catch |err| switch (err) {
|
|
else => return WriteError.write_failed,
|
|
};
|
|
return byte_count;
|
|
},
|
|
.macos => {
|
|
const length: usize = #min(max_file_operation_byte_count, bytes.length);
|
|
const syscall_result = system.write(file_descriptor.handle, bytes.pointer, length);
|
|
const byte_count = unwrap_syscall(syscall_result) catch |err| switch (err) {
|
|
else => return WriteError.write_failed,
|
|
};
|
|
return byte_count;
|
|
},
|
|
else => #error("OS not supported"),
|
|
}
|
|
}
|
|
|
|
const write_all = fn(file_descriptor: FileDescriptor, bytes: []const u8) WriteError!void {
|
|
var bytes_written: usize = 0;
|
|
|
|
while (bytes_written < bytes.length) {
|
|
const iteration_written_byte_count = try file_descriptor.write(bytes = bytes[bytes_written..]);
|
|
bytes_written += iteration_written_byte_count;
|
|
}
|
|
|
|
assert(bytes_written == bytes.length);
|
|
}
|
|
|
|
const get_size = fn (file_descriptor: FileDescriptor) GetAttributesError!u64 {
|
|
const file_attributes = try file_descriptor.get_attributes();
|
|
return file_attributes.size;
|
|
}
|
|
|
|
const GetAttributesError = error{
|
|
failed,
|
|
};
|
|
|
|
const get_attributes = fn (file_descriptor: FileDescriptor) GetAttributesError!FileAttributes {
|
|
switch (current) {
|
|
.linux, .macos => {
|
|
var stat_buffer: system.Stat = undefined;
|
|
const raw_result = system.fstat(file_descriptor.handle, stat_buffer.&);
|
|
const result = unwrap_syscall(raw_result) catch |err| switch (err) {
|
|
else => return GetAttributesError.failed,
|
|
};
|
|
|
|
const size: u64 = #cast(stat_buffer.size);
|
|
|
|
const file_attributes = FileAttributes{
|
|
.size = size,
|
|
};
|
|
|
|
return file_attributes;
|
|
},
|
|
else => #error("OS not supported"),
|
|
}
|
|
}
|
|
};
|
|
|
|
const FileAttributes = struct{
|
|
size: u64,
|
|
};
|
|
|
|
const StdFileDescriptor = enum {
|
|
stdin = 0,
|
|
stdout = 1,
|
|
stderr = 2,
|
|
|
|
const get = fn(descriptor: StdFileDescriptor) FileDescriptor{
|
|
switch (current) {
|
|
.linux, .macos => {
|
|
return FileDescriptor{
|
|
.handle = #cast(descriptor),
|
|
};
|
|
},
|
|
else => #error("OS not supported"),
|
|
}
|
|
}
|
|
};
|
|
|
|
const ProtectionFlags = bitfield(u32){
|
|
read: bool,
|
|
write: bool,
|
|
execute: bool,
|
|
};
|
|
|
|
const MapFlags = bitfield(u32){
|
|
reserve: bool,
|
|
commit: bool,
|
|
};
|
|
|
|
const VirtualAllocateError = error{
|
|
allocation_failed,
|
|
};
|
|
|
|
const allocate_virtual_memory = fn(address: ?[&]u8, length: usize, general_protection_flags: ProtectionFlags, general_map_flags: MapFlags) VirtualAllocateError![&]u8 {
|
|
const protection_flags = system.get_protection_flags(flags = general_protection_flags);
|
|
const map_flags = system.get_map_flags(flags = general_map_flags);
|
|
const file_descriptor = -1;
|
|
const offset = 0;
|
|
|
|
switch (current) {
|
|
.linux, .macos => {
|
|
const syscall_result = system.mmap(address, length, protection_flags, map_flags, file_descriptor, offset);
|
|
if (link_libc) {
|
|
if (syscall_result != system.MAP_FAILED) {
|
|
const result_address: [&]u8 = #cast(syscall_result);
|
|
return result_address;
|
|
} else {
|
|
// TODO:
|
|
unreachable;
|
|
}
|
|
} else {
|
|
const result = unwrap_syscall(syscall_result) catch |err| switch (err) {
|
|
else => unreachable,
|
|
};
|
|
|
|
const pointer: [&]u8 = #cast(result);
|
|
return pointer;
|
|
}
|
|
},
|
|
else => #error("OS not supported"),
|
|
}
|
|
}
|
|
|
|
const FreeError = error{
|
|
free_failed,
|
|
};
|
|
const free_virtual_memory = fn(bytes: []const u8) FreeError!void {
|
|
switch (current) {
|
|
.linux => {
|
|
const syscall_result = system.munmap(bytes);
|
|
_ = unwrap_syscall(syscall_result) catch |err| switch (err) {
|
|
else => unreachable,
|
|
};
|
|
},
|
|
.macos => {
|
|
const syscall_result = system.munmap(bytes.pointer, bytes.length);
|
|
_ = unwrap_syscall(syscall_result) catch |err| switch (err) {
|
|
else => unreachable,
|
|
};
|
|
},
|
|
else => #error("OS not supported"),
|
|
}
|
|
}
|
|
|
|
const ReadLinkError = error{
|
|
failed,
|
|
};
|
|
|
|
const readlink = fn(file_path: [&:0]const u8, buffer: []u8) ReadLinkError![]u8 {
|
|
switch (current) {
|
|
.linux, .macos => {
|
|
const syscall_result = system.readlink(file_path, buffer);
|
|
|
|
const byte_count = unwrap_syscall(syscall_result) catch |err| switch (err) {
|
|
else => unreachable,
|
|
};
|
|
|
|
const bytes = buffer[0..byte_count];
|
|
return bytes;
|
|
},
|
|
else => #error("OS not supported"),
|
|
}
|
|
}
|
|
|
|
const max_path_byte_count = switch (current) {
|
|
.linux => 0x1000,
|
|
.macos => 1024,
|
|
else => #error("OS not supported"),
|
|
};
|
|
|
|
const CurrentExecutablePath = error{
|
|
failed,
|
|
};
|
|
const current_executable_path = fn(buffer: [:0]u8) CurrentExecutablePath![]u8 {
|
|
switch (current) {
|
|
.linux => {
|
|
const bytes = readlink("/proc/self/exe", buffer) catch |err| switch (err) {
|
|
else => unreachable,
|
|
};
|
|
return bytes;
|
|
},
|
|
.macos => {
|
|
var symlink_path_buffer: [max_path_byte_count:0]u8 = undefined;
|
|
var symlink_path_len: u32 = symlink_path_buffer.length + 1;
|
|
const ns_result = c._NSGetExecutablePath(symlink_path_buffer.&, symlink_path_len.&);
|
|
if (ns_result == 0) {
|
|
const symlink_path = symlink_path_buffer[0..symlink_path_len];
|
|
if (c.realpath(symlink_path.pointer, buffer.pointer)) |result| {
|
|
var i: usize = 0;
|
|
while (i < buffer.length) {
|
|
if (result[i] == 0) {
|
|
break;
|
|
}
|
|
i += 1;
|
|
}
|
|
assert(i < buffer.length);
|
|
|
|
const r: []u8 = result[0..i];
|
|
return r;
|
|
} else {
|
|
return CurrentExecutablePath.failed;
|
|
}
|
|
} else {
|
|
return CurrentExecutablePath.failed;
|
|
}
|
|
},
|
|
else => #error("OS not supported"),
|
|
}
|
|
}
|
|
|
|
const Process = struct{
|
|
const Id = system.ProcessId;
|
|
};
|
|
|
|
const DuplicateProcessError = error{
|
|
system_resources,
|
|
out_of_memory,
|
|
};
|
|
|
|
const duplicate_process = fn () DuplicateProcessError!Process.Id {
|
|
switch (current) {
|
|
.linux, .macos => {
|
|
const syscall_result = system.fork();
|
|
const result = unwrap_syscall(syscall_result) catch |err| return switch (err) {
|
|
.AGAIN, .NOMEM => DuplicateProcessError.system_resources,
|
|
else => unreachable,
|
|
};
|
|
const truncated: u32 = #cast(result);
|
|
|
|
const process_id: Process.Id = #cast(truncated);
|
|
return process_id;
|
|
},
|
|
else => #error("OS not supported"),
|
|
}
|
|
}
|
|
|
|
const ExecveError = error{
|
|
execve_failed,
|
|
};
|
|
const execute = fn(path: [&:0]const u8, argv: [&:null]const ?[&:0]const u8, env: [&:null]const ?[&:null]const u8) ExecveError!noreturn {
|
|
switch (current) {
|
|
.linux, .macos => {
|
|
const syscall_result = system.execve(path, argv, env);
|
|
_ = unwrap_syscall(syscall_result) catch |err| switch (err) {
|
|
else => return ExecveError.execve_failed,
|
|
};
|
|
unreachable;
|
|
},
|
|
else => #error("OS not supported"),
|
|
}
|
|
}
|
|
|
|
const EventFileDescriptorError = error{
|
|
};
|
|
const event_file_descriptor = fn(initial_value: u32, flags: u32) EventFileDescriptorError!FileDescriptor {
|
|
switch (current) {
|
|
.linux => {
|
|
const syscall_result = linux.event_file_descriptor(count = initial_value, flags);
|
|
const result = unwrap_syscall(syscall_result) catch |err| switch (err) {
|
|
else => unreachable,
|
|
};
|
|
const file_descriptor: system.FileDescriptor = #cast(result);
|
|
return file_descriptor;
|
|
},
|
|
else => #error("OS not supported"),
|
|
}
|
|
}
|
|
|
|
const Dup2Error = error{
|
|
};
|
|
const dup2 = fn(old_file_descriptor: system.FileDescriptor, new_file_descriptor: system.FileDescriptor) Dup2Error!void {
|
|
switch (current) {
|
|
.linux => {
|
|
const syscall_result = linux.dup2(old = old_file_descriptor, new = new_file_descriptor);
|
|
_ = unwrap_syscall(syscall_result) catch |err| switch (err) {
|
|
else => unreachable,
|
|
};
|
|
},
|
|
else => #error("OS not supported"),
|
|
}
|
|
}
|
|
|
|
const OpenError = error{
|
|
failed,
|
|
};
|
|
|
|
const OpenFlags = bitfield(u32) {
|
|
access_mode: AccessMode = .read_only,
|
|
};
|
|
|
|
const AccessMode = enum(u2) {
|
|
read_only = 0,
|
|
write_only = 1,
|
|
read_write = 2,
|
|
};
|
|
|
|
const open = fn(path: [&:0]const u8, open_flags: OpenFlags) OpenError!FileDescriptor{
|
|
switch (current) {
|
|
.linux => {
|
|
const flags = system.OpenFlags{
|
|
.access_mode = switch (open_flags.access_mode) {
|
|
.read_only => .read_only,
|
|
.write_only => .write_only,
|
|
.read_write => .read_write,
|
|
},
|
|
};
|
|
const syscall_result = system.open(path, flags, 0);
|
|
const result = unwrap_syscall(syscall_result) catch |err| switch (err) {
|
|
else => unreachable,
|
|
};
|
|
|
|
const r: u32 = #cast(result);
|
|
|
|
const file_descriptor = FileDescriptor{
|
|
.handle = #cast(r),
|
|
};
|
|
return file_descriptor;
|
|
},
|
|
.macos => {
|
|
const flags = system.OpenFlags{
|
|
.access_mode = switch (open_flags.access_mode) {
|
|
.read_only => .read_only,
|
|
.write_only => .write_only,
|
|
.read_write => .read_write,
|
|
},
|
|
};
|
|
const syscall_result = system.open(path, flags);
|
|
const result = unwrap_syscall(syscall_result) catch |err| switch (err) {
|
|
else => unreachable,
|
|
};
|
|
|
|
const r: u32 = #cast(result);
|
|
|
|
const file_descriptor = FileDescriptor{
|
|
.handle = #cast(r),
|
|
};
|
|
return file_descriptor;
|
|
},
|
|
else => #error("OS not supported"),
|
|
}
|
|
}
|
|
|
|
const CloseError = error{
|
|
};
|
|
const close = fn(file_descriptor: system.FileDescriptor) CloseError!void {
|
|
switch (current) {
|
|
.linux => {
|
|
const syscall_result = system.close(file_descriptor);
|
|
_ = unwrap_syscall(syscall_result) catch |err| switch (err) {
|
|
else => unreachable,
|
|
};
|
|
},
|
|
else => #error("OS not supported"),
|
|
}
|
|
}
|
|
|
|
const Pipe2Error = error{
|
|
};
|
|
const pipe2 = fn(flags: u32) Pipe2Error![2]system.FileDescriptor{
|
|
switch (current) {
|
|
.linux => {
|
|
var pipe: [2]system.FileDescriptor = undefined;
|
|
const syscall_result = linux.pipe2(pipe.&, flags);
|
|
_ = unwrap_syscall(syscall_result) catch |err| switch (err) {
|
|
else => unreachable,
|
|
};
|
|
|
|
return pipe;
|
|
},
|
|
else => #error("OS not supported"),
|
|
}
|
|
}
|
|
|
|
const PollFileDescriptor = system.PollFileDescriptor;
|
|
|
|
const poll = fn(file_descriptors: []PollFileDescriptor, timeout: s32) ?usize {
|
|
switch (current) {
|
|
.linux => {
|
|
if (linux.unwrap_syscall(syscall_result = linux.poll(file_descriptors = file_descriptors.pointer, file_descriptor_count = file_descriptors.length, timeout = timeout))) |result| {
|
|
return result;
|
|
} else {
|
|
return null;
|
|
}
|
|
},
|
|
else => #error("OS not supported"),
|
|
}
|
|
}
|
|
|
|
const termsig = fn(status: u32) u32 {
|
|
return status & 0x7f;
|
|
}
|
|
|
|
const ifexited = fn(status: u32) bool {
|
|
return termsig(status) == 0;
|
|
}
|
|
|
|
const exitstatus = fn(status: u32) u8 {
|
|
const result: u8 = #cast((status & 0xff00) >> 8);
|
|
return result;
|
|
}
|
|
|
|
const stopsig = fn(status: u32) u32 {
|
|
return exitstatus(status);
|
|
}
|
|
|
|
const ifstopped = fn(status: u32) bool {
|
|
const result: u16 = #cast(((status & 0xffff) * 0x10001) >> 8);
|
|
return result > 0x7f00;
|
|
}
|
|
|
|
const ifsignaled = fn(status: u32) bool {
|
|
return (status & 0xffff) - 1 < 0xff;
|
|
}
|
|
|
|
const WaitPidError = error{
|
|
failed,
|
|
};
|
|
const waitpid = fn(pid: Process.Id, flags: u32) WaitPidError!u32 {
|
|
switch (current) {
|
|
.linux => {
|
|
var status: u32 = undefined;
|
|
while (true) {
|
|
const syscall_result = linux.waitpid(pid, status = status.&, flags, resource_usage = 0);
|
|
const signed_syscall_result: ssize = #cast(syscall_result);
|
|
if (signed_syscall_result != -4) {
|
|
_ = unwrap_syscall(syscall_result) catch |err| switch (err) {
|
|
else => unreachable,
|
|
};
|
|
return status;
|
|
}
|
|
}
|
|
},
|
|
.macos => {
|
|
var status: s32 = undefined;
|
|
if (system.waitpid(pid, status.&, #cast(flags)) != -1) {
|
|
const status_u: u32 = #cast(status);
|
|
return status_u;
|
|
} else {
|
|
unreachable;
|
|
}
|
|
},
|
|
else => #error("OS not supported"),
|
|
}
|
|
|
|
}
|
|
|
|
const reserve = fn (size: u64) *!&any {
|
|
switch (current) {
|
|
.linux, .macos => {
|
|
const syscall_result = system.mmap(null, size, .{
|
|
.read = false,
|
|
.write = false,
|
|
.execute = false,
|
|
}, .{
|
|
.anonymous = true,
|
|
.private = true,
|
|
.shared = false,
|
|
.fixed = false,
|
|
}, -1, 0);
|
|
|
|
switch (link_libc) {
|
|
true => {
|
|
if (syscall_result != system.MAP_FAILED) {
|
|
const result: &any = #cast(syscall_result);
|
|
return result;
|
|
} else {
|
|
unreachable;
|
|
}
|
|
},
|
|
false => {
|
|
const result = unwrap_syscall(syscall_result) catch |err| switch (err) {
|
|
else => unreachable,
|
|
};
|
|
const pointer: &any = #cast(result);
|
|
return pointer;
|
|
},
|
|
}
|
|
|
|
},
|
|
else => #error("OS not supported"),
|
|
}
|
|
}
|
|
|
|
const commit = fn (reserved_memory: &any, size: u64) *!void {
|
|
switch (current) {
|
|
.linux, .macos => {
|
|
const syscall_result = system.mprotect(reserved_memory, size, .{
|
|
.read = true,
|
|
.write = true,
|
|
.execute = false,
|
|
});
|
|
},
|
|
else => #error("OS not supported"),
|
|
}
|
|
}
|
|
|
|
const MemFdCreateError = error{
|
|
};
|
|
|
|
const memfd_create = fn(name: [&:0]const u8, flags: u32) MemFdCreateError!FileDescriptor{
|
|
switch (current) {
|
|
.linux => {
|
|
const syscall_result = linux.memfd_create(path, flags);
|
|
const result = unwrap_syscall(syscall_result) catch |err| switch (err) {
|
|
else => unreachable,
|
|
};
|
|
|
|
const file_descriptor = FileDescriptor{
|
|
.handle = #cast(result),
|
|
};
|
|
|
|
return file_descriptor;
|
|
},
|
|
else => #error("OS not supported"),
|
|
}
|
|
}
|
|
|
|
const basename = fn (path: []const u8) ?[]const u8 {
|
|
var i: usize = path.length;
|
|
|
|
while (i > 0) {
|
|
i -= 1;
|
|
|
|
if (path[i] == '/') {
|
|
i = i + 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
const p = path[i..];
|
|
return p;
|
|
}
|
|
|
|
const IoChannelBehavior = enum{
|
|
pipe,
|
|
close,
|
|
inherit,
|
|
ignore,
|
|
};
|