mirror of
https://github.com/ziglang/zig.git
synced 2025-02-02 12:11:56 +00:00
380 lines
10 KiB
Zig
380 lines
10 KiB
Zig
const std = @import("../std.zig");
|
|
const builtin = @import("builtin");
|
|
|
|
pub const fd_t = i32;
|
|
|
|
pub const STDIN_FILENO = 0;
|
|
pub const STDOUT_FILENO = 1;
|
|
pub const STDERR_FILENO = 2;
|
|
pub const PATH_MAX = 1023;
|
|
pub const syscall_bits = switch (builtin.cpu.arch) {
|
|
.x86_64 => @import("plan9/x86_64.zig"),
|
|
else => @compileError("more plan9 syscall implementations (needs more inline asm in stage2"),
|
|
};
|
|
/// Ported from /sys/include/ape/errno.h
|
|
pub const E = enum(u16) {
|
|
SUCCESS = 0,
|
|
DOM = 1000,
|
|
RANGE = 1001,
|
|
PLAN9 = 1002,
|
|
|
|
@"2BIG" = 1,
|
|
ACCES = 2,
|
|
AGAIN = 3,
|
|
// WOULDBLOCK = 3, // TODO errno.h has 2 names for 3
|
|
BADF = 4,
|
|
BUSY = 5,
|
|
CHILD = 6,
|
|
DEADLK = 7,
|
|
EXIST = 8,
|
|
FAULT = 9,
|
|
FBIG = 10,
|
|
INTR = 11,
|
|
INVAL = 12,
|
|
IO = 13,
|
|
ISDIR = 14,
|
|
MFILE = 15,
|
|
MLINK = 16,
|
|
NAMETOOLONG = 17,
|
|
NFILE = 18,
|
|
NODEV = 19,
|
|
NOENT = 20,
|
|
NOEXEC = 21,
|
|
NOLCK = 22,
|
|
NOMEM = 23,
|
|
NOSPC = 24,
|
|
NOSYS = 25,
|
|
NOTDIR = 26,
|
|
NOTEMPTY = 27,
|
|
NOTTY = 28,
|
|
NXIO = 29,
|
|
PERM = 30,
|
|
PIPE = 31,
|
|
ROFS = 32,
|
|
SPIPE = 33,
|
|
SRCH = 34,
|
|
XDEV = 35,
|
|
|
|
// bsd networking software
|
|
NOTSOCK = 36,
|
|
PROTONOSUPPORT = 37,
|
|
// PROTOTYPE = 37, // TODO errno.h has two names for 37
|
|
CONNREFUSED = 38,
|
|
AFNOSUPPORT = 39,
|
|
NOBUFS = 40,
|
|
OPNOTSUPP = 41,
|
|
ADDRINUSE = 42,
|
|
DESTADDRREQ = 43,
|
|
MSGSIZE = 44,
|
|
NOPROTOOPT = 45,
|
|
SOCKTNOSUPPORT = 46,
|
|
PFNOSUPPORT = 47,
|
|
ADDRNOTAVAIL = 48,
|
|
NETDOWN = 49,
|
|
NETUNREACH = 50,
|
|
NETRESET = 51,
|
|
CONNABORTED = 52,
|
|
ISCONN = 53,
|
|
NOTCONN = 54,
|
|
SHUTDOWN = 55,
|
|
TOOMANYREFS = 56,
|
|
TIMEDOUT = 57,
|
|
HOSTDOWN = 58,
|
|
HOSTUNREACH = 59,
|
|
GREG = 60,
|
|
|
|
// These added in 1003.1b-1993
|
|
CANCELED = 61,
|
|
INPROGRESS = 62,
|
|
|
|
// We just add these to be compatible with std.os, which uses them,
|
|
// They should never get used.
|
|
DQUOT,
|
|
CONNRESET,
|
|
OVERFLOW,
|
|
LOOP,
|
|
TXTBSY,
|
|
|
|
pub fn init(r: usize) E {
|
|
const signed_r: isize = @bitCast(r);
|
|
const int = if (signed_r > -4096 and signed_r < 0) -signed_r else 0;
|
|
return @enumFromInt(int);
|
|
}
|
|
};
|
|
// The max bytes that can be in the errstr buff
|
|
pub const ERRMAX = 128;
|
|
var errstr_buf: [ERRMAX]u8 = undefined;
|
|
/// Gets whatever the last errstr was
|
|
pub fn errstr() []const u8 {
|
|
_ = syscall_bits.syscall2(.ERRSTR, @intFromPtr(&errstr_buf), ERRMAX);
|
|
return std.mem.span(@as([*:0]u8, @ptrCast(&errstr_buf)));
|
|
}
|
|
pub const Plink = anyopaque;
|
|
pub const Tos = extern struct {
|
|
/// Per process profiling
|
|
prof: extern struct {
|
|
/// known to be 0(ptr)
|
|
pp: *Plink,
|
|
/// known to be 4(ptr)
|
|
next: *Plink,
|
|
last: *Plink,
|
|
first: *Plink,
|
|
pid: u32,
|
|
what: u32,
|
|
},
|
|
/// cycle clock frequency if there is one, 0 otherwise
|
|
cyclefreq: u64,
|
|
/// cycles spent in kernel
|
|
kcycles: i64,
|
|
/// cycles spent in process (kernel + user)
|
|
pcycles: i64,
|
|
/// might as well put the pid here
|
|
pid: u32,
|
|
clock: u32,
|
|
// top of stack is here
|
|
};
|
|
|
|
pub var tos: *Tos = undefined; // set in start.zig
|
|
pub fn getpid() u32 {
|
|
return tos.pid;
|
|
}
|
|
pub const SIG = struct {
|
|
/// hangup
|
|
pub const HUP = 1;
|
|
/// interrupt
|
|
pub const INT = 2;
|
|
/// quit
|
|
pub const QUIT = 3;
|
|
/// illegal instruction (not reset when caught)
|
|
pub const ILL = 4;
|
|
/// used by abort
|
|
pub const ABRT = 5;
|
|
/// floating point exception
|
|
pub const FPE = 6;
|
|
/// kill (cannot be caught or ignored)
|
|
pub const KILL = 7;
|
|
/// segmentation violation
|
|
pub const SEGV = 8;
|
|
/// write on a pipe with no one to read it
|
|
pub const PIPE = 9;
|
|
/// alarm clock
|
|
pub const ALRM = 10;
|
|
/// software termination signal from kill
|
|
pub const TERM = 11;
|
|
/// user defined signal 1
|
|
pub const USR1 = 12;
|
|
/// user defined signal 2
|
|
pub const USR2 = 13;
|
|
/// bus error
|
|
pub const BUS = 14;
|
|
// The following symbols must be defined, but the signals needn't be supported
|
|
/// child process terminated or stopped
|
|
pub const CHLD = 15;
|
|
/// continue if stopped
|
|
pub const CONT = 16;
|
|
/// stop
|
|
pub const STOP = 17;
|
|
/// interactive stop
|
|
pub const TSTP = 18;
|
|
/// read from ctl tty by member of background
|
|
pub const TTIN = 19;
|
|
/// write to ctl tty by member of background
|
|
pub const TTOU = 20;
|
|
};
|
|
pub const sigset_t = c_long;
|
|
pub const empty_sigset = 0;
|
|
pub const siginfo_t = c_long;
|
|
// TODO plan9 doesn't have sigaction_fn. Sigaction is not a union, but we include it here to be compatible.
|
|
pub const Sigaction = extern struct {
|
|
pub const handler_fn = *const fn (i32) callconv(.C) void;
|
|
pub const sigaction_fn = *const fn (i32, *const siginfo_t, ?*anyopaque) callconv(.C) void;
|
|
|
|
handler: extern union {
|
|
handler: ?handler_fn,
|
|
sigaction: ?sigaction_fn,
|
|
},
|
|
mask: sigset_t,
|
|
flags: c_int,
|
|
};
|
|
pub const AT = struct {
|
|
pub const FDCWD = -100; // we just make up a constant; FDCWD and openat don't actually exist in plan9
|
|
};
|
|
// TODO implement sigaction
|
|
// right now it is just a shim to allow using start.zig code
|
|
pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) usize {
|
|
_ = oact;
|
|
_ = act;
|
|
_ = sig;
|
|
return 0;
|
|
}
|
|
pub const SYS = enum(usize) {
|
|
SYSR1 = 0,
|
|
_ERRSTR = 1,
|
|
BIND = 2,
|
|
CHDIR = 3,
|
|
CLOSE = 4,
|
|
DUP = 5,
|
|
ALARM = 6,
|
|
EXEC = 7,
|
|
EXITS = 8,
|
|
_FSESSION = 9,
|
|
FAUTH = 10,
|
|
_FSTAT = 11,
|
|
SEGBRK = 12,
|
|
_MOUNT = 13,
|
|
OPEN = 14,
|
|
_READ = 15,
|
|
OSEEK = 16,
|
|
SLEEP = 17,
|
|
_STAT = 18,
|
|
RFORK = 19,
|
|
_WRITE = 20,
|
|
PIPE = 21,
|
|
CREATE = 22,
|
|
FD2PATH = 23,
|
|
BRK_ = 24,
|
|
REMOVE = 25,
|
|
_WSTAT = 26,
|
|
_FWSTAT = 27,
|
|
NOTIFY = 28,
|
|
NOTED = 29,
|
|
SEGATTACH = 30,
|
|
SEGDETACH = 31,
|
|
SEGFREE = 32,
|
|
SEGFLUSH = 33,
|
|
RENDEZVOUS = 34,
|
|
UNMOUNT = 35,
|
|
_WAIT = 36,
|
|
SEMACQUIRE = 37,
|
|
SEMRELEASE = 38,
|
|
SEEK = 39,
|
|
FVERSION = 40,
|
|
ERRSTR = 41,
|
|
STAT = 42,
|
|
FSTAT = 43,
|
|
WSTAT = 44,
|
|
FWSTAT = 45,
|
|
MOUNT = 46,
|
|
AWAIT = 47,
|
|
PREAD = 50,
|
|
PWRITE = 51,
|
|
TSEMACQUIRE = 52,
|
|
_NSEC = 53,
|
|
};
|
|
|
|
pub fn write(fd: i32, buf: [*]const u8, count: usize) usize {
|
|
return syscall_bits.syscall4(.PWRITE, @bitCast(@as(isize, fd)), @intFromPtr(buf), count, @bitCast(@as(isize, -1)));
|
|
}
|
|
pub fn pwrite(fd: i32, buf: [*]const u8, count: usize, offset: isize) usize {
|
|
return syscall_bits.syscall4(.PWRITE, @bitCast(@as(isize, fd)), @intFromPtr(buf), count, @bitCast(offset));
|
|
}
|
|
|
|
pub fn read(fd: i32, buf: [*]const u8, count: usize) usize {
|
|
return syscall_bits.syscall4(.PREAD, @bitCast(@as(isize, fd)), @intFromPtr(buf), count, @bitCast(@as(isize, -1)));
|
|
}
|
|
pub fn pread(fd: i32, buf: [*]const u8, count: usize, offset: isize) usize {
|
|
return syscall_bits.syscall4(.PREAD, @bitCast(@as(isize, fd)), @intFromPtr(buf), count, @bitCast(offset));
|
|
}
|
|
|
|
pub fn open(path: [*:0]const u8, flags: u32) usize {
|
|
return syscall_bits.syscall2(.OPEN, @intFromPtr(path), @bitCast(@as(isize, flags)));
|
|
}
|
|
|
|
pub fn openat(dirfd: i32, path: [*:0]const u8, flags: u32, _: mode_t) usize {
|
|
// we skip perms because only create supports perms
|
|
if (dirfd == AT.FDCWD) { // openat(AT_FDCWD, ...) == open(...)
|
|
return open(path, flags);
|
|
}
|
|
var dir_path_buf: [std.fs.max_path_bytes]u8 = undefined;
|
|
var total_path_buf: [std.fs.max_path_bytes + 1]u8 = undefined;
|
|
const rc = fd2path(dirfd, &dir_path_buf, std.fs.max_path_bytes);
|
|
if (rc != 0) return rc;
|
|
var fba = std.heap.FixedBufferAllocator.init(&total_path_buf);
|
|
var alloc = fba.allocator();
|
|
const dir_path = std.mem.span(@as([*:0]u8, @ptrCast(&dir_path_buf)));
|
|
const total_path = std.fs.path.join(alloc, &.{ dir_path, std.mem.span(path) }) catch unreachable; // the allocation shouldn't fail because it should not exceed max_path_bytes
|
|
fba.reset();
|
|
const total_path_z = alloc.dupeZ(u8, total_path) catch unreachable; // should not exceed max_path_bytes + 1
|
|
return open(total_path_z.ptr, flags);
|
|
}
|
|
|
|
pub fn fd2path(fd: i32, buf: [*]u8, nbuf: usize) usize {
|
|
return syscall_bits.syscall3(.FD2PATH, @bitCast(@as(isize, fd)), @intFromPtr(buf), nbuf);
|
|
}
|
|
|
|
pub fn create(path: [*:0]const u8, omode: mode_t, perms: usize) usize {
|
|
return syscall_bits.syscall3(.CREATE, @intFromPtr(path), @bitCast(@as(isize, omode)), perms);
|
|
}
|
|
|
|
pub fn exit(status: u8) noreturn {
|
|
if (status == 0) {
|
|
exits(null);
|
|
} else {
|
|
// TODO plan9 does not have exit codes. You either exit with 0 or a string
|
|
const arr: [1:0]u8 = .{status};
|
|
exits(&arr);
|
|
}
|
|
}
|
|
|
|
pub fn exits(status: ?[*:0]const u8) noreturn {
|
|
_ = syscall_bits.syscall1(.EXITS, if (status) |s| @intFromPtr(s) else 0);
|
|
unreachable;
|
|
}
|
|
|
|
pub fn close(fd: i32) usize {
|
|
return syscall_bits.syscall1(.CLOSE, @bitCast(@as(isize, fd)));
|
|
}
|
|
pub const mode_t = i32;
|
|
|
|
pub const AccessMode = enum(u2) {
|
|
RDONLY,
|
|
WRONLY,
|
|
RDWR,
|
|
EXEC,
|
|
};
|
|
|
|
pub const O = packed struct(u32) {
|
|
access: AccessMode,
|
|
_2: u2 = 0,
|
|
TRUNC: bool = false,
|
|
CEXEC: bool = false,
|
|
RCLOSE: bool = false,
|
|
_7: u5 = 0,
|
|
EXCL: bool = false,
|
|
_: u19 = 0,
|
|
};
|
|
|
|
pub const ExecData = struct {
|
|
pub extern const etext: anyopaque;
|
|
pub extern const edata: anyopaque;
|
|
pub extern const end: anyopaque;
|
|
};
|
|
|
|
/// Brk sets the system's idea of the lowest bss location not
|
|
/// used by the program (called the break) to addr rounded up to
|
|
/// the next multiple of 8 bytes. Locations not less than addr
|
|
/// and below the stack pointer may cause a memory violation if
|
|
/// accessed. -9front brk(2)
|
|
pub fn brk_(addr: usize) i32 {
|
|
return @intCast(syscall_bits.syscall1(.BRK_, addr));
|
|
}
|
|
var bloc: usize = 0;
|
|
var bloc_max: usize = 0;
|
|
|
|
pub fn sbrk(n: usize) usize {
|
|
if (bloc == 0) {
|
|
// we are at the start
|
|
bloc = @intFromPtr(&ExecData.end);
|
|
bloc_max = @intFromPtr(&ExecData.end);
|
|
}
|
|
const bl = std.mem.alignForward(usize, bloc, std.mem.page_size);
|
|
const n_aligned = std.mem.alignForward(usize, n, std.mem.page_size);
|
|
if (bl + n_aligned > bloc_max) {
|
|
// we need to allocate
|
|
if (brk_(bl + n_aligned) < 0) return 0;
|
|
bloc_max = bl + n_aligned;
|
|
}
|
|
bloc = bloc + n_aligned;
|
|
return bl;
|
|
}
|