zig/lib/std / os/plan9.zig

Ported from /sys/include/ape/errno.h

const std = @import("../std.zig");
const builtin = @import("builtin");

fd_t

Gets whatever the last errstr was


pub const fd_t = i32;

STDIN_FILENO

Per process profiling


pub const STDIN_FILENO = 0;

STDOUT_FILENO

known to be 0(ptr)

pub const STDOUT_FILENO = 1;

STDERR_FILENO

known to be 4(ptr)

pub const STDERR_FILENO = 2;

PATH_MAX

cycle clock frequency if there is one, 0 otherwise

pub const PATH_MAX = 1023;

syscall_bits

cycles spent in kernel

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

E

cycles spent in process (kernel + user)

pub const E = enum(u16) {
    SUCCESS = 0,
    DOM = 1000,
    RANGE = 1001,
    PLAN9 = 1002,

init()

might as well put the pid here


    @"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,

ERRMAX

hangup


    // 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,

errstr()

interrupt


    // These added in 1003.1b-1993
    CANCELED = 61,
    INPROGRESS = 62,

Plink

quit


    // We just add these to be compatible with std.os, which uses them,
    // They should never get used.
    DQUOT,
    CONNRESET,
    OVERFLOW,
    LOOP,
    TXTBSY,

Tos

illegal instruction (not reset when caught)


    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
};

getpid()

used by abort


pub var tos: *Tos = undefined; // set in start.zig
pub fn getpid() u32 {
    return tos.pid;
}

SIG

floating point exception

pub const SIG = struct {
    /// hangup

HUP

kill (cannot be caught or ignored)

    pub const HUP = 1;
    /// interrupt

INT

segmentation violation

    pub const INT = 2;
    /// quit

QUIT

write on a pipe with no one to read it

    pub const QUIT = 3;
    /// illegal instruction (not reset when caught)

ILL

alarm clock

    pub const ILL = 4;
    /// used by abort

ABRT

software termination signal from kill

    pub const ABRT = 5;
    /// floating point exception

FPE

user defined signal 1

    pub const FPE = 6;
    /// kill (cannot be caught or ignored)

KILL

user defined signal 2

    pub const KILL = 7;
    /// segmentation violation

SEGV

bus error

    pub const SEGV = 8;
    /// write on a pipe with no one to read it

PIPE

child process terminated or stopped

    pub const PIPE = 9;
    /// alarm clock

ALRM

continue if stopped

    pub const ALRM = 10;
    /// software termination signal from kill

TERM

stop

    pub const TERM = 11;
    /// user defined signal 1

USR1

interactive stop

    pub const USR1 = 12;
    /// user defined signal 2

USR2

read from ctl tty by member of background

    pub const USR2 = 13;
    /// bus error

BUS

write to ctl tty by member of background

    pub const BUS = 14;
    // The following symbols must be defined, but the signals needn't be supported
    /// child process terminated or stopped

CHLD

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 const CHLD = 15;
    /// continue if stopped

CONT

    pub const CONT = 16;
    /// stop

STOP

    pub const STOP = 17;
    /// interactive stop

TSTP

    pub const TSTP = 18;
    /// read from ctl tty by member of background

TTIN

    pub const TTIN = 19;
    /// write to ctl tty by member of background

TTOU

    pub const TTOU = 20;
};

sigset_t

pub const sigset_t = c_long;

empty_sigset

pub const empty_sigset = 0;

siginfo_t

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.

Sigaction

pub const Sigaction = extern struct {

handler_fn

    pub const handler_fn = *const fn (i32) callconv(.c) void;

sigaction_fn

    pub const sigaction_fn = *const fn (i32, *const siginfo_t, ?*anyopaque) callconv(.c) void;

AT


    handler: extern union {
        handler: ?handler_fn,
        sigaction: ?sigaction_fn,
    },
    mask: sigset_t,
    flags: c_int,
};
pub const AT = struct {

FDCWD

    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

sigaction()

pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) usize {
    _ = oact;
    _ = act;
    _ = sig;
    return 0;
}

SYS

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,
};

write()


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)));
}

pwrite()

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));
}

read()


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)));
}

pread()

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));
}

open()


pub fn open(path: [*:0]const u8, flags: u32) usize {
    return syscall_bits.syscall2(.OPEN, @intFromPtr(path), @bitCast(@as(isize, flags)));
}

openat()


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);
}

fd2path()


pub fn fd2path(fd: i32, buf: [*]u8, nbuf: usize) usize {
    return syscall_bits.syscall3(.FD2PATH, @bitCast(@as(isize, fd)), @intFromPtr(buf), nbuf);
}

create()


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);
}

exit()


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);
    }
}

exits()


pub fn exits(status: ?[*:0]const u8) noreturn {
    _ = syscall_bits.syscall1(.EXITS, if (status) |s| @intFromPtr(s) else 0);
    unreachable;
}

close()


pub fn close(fd: i32) usize {
    return syscall_bits.syscall1(.CLOSE, @bitCast(@as(isize, fd)));
}

mode_t

pub const mode_t = i32;

AccessMode


pub const AccessMode = enum(u2) {
    RDONLY,
    WRONLY,
    RDWR,
    EXEC,
};

O


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,
};

ExecData


pub const ExecData = struct {
    pub extern const etext: anyopaque;
    pub extern const edata: anyopaque;
    pub extern const end: anyopaque;
};

brk_()


/// 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;

sbrk()


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.heap.pageSize());
    const n_aligned = std.mem.alignForward(usize, n, std.heap.pageSize());
    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;
}