import { log } from "./logging";
const nodeToZig = {
arm64: "aarch64",
x64: "x86_64",
linux: "linux-gnu",
darwin: "macos",
};
const name = `${nodeToZig[process.arch]}-${nodeToZig[process.platform]}`;
const LINUX_ONLY = [
"getresuid",
"getresgid",
"setresgid",
"setresuid",
"fexecve",
"_fexecve",
];
export interface Hostent {
h_name: string;
h_length: number;
h_addrtype: number;
h_addr_list: string[];
h_aliases: string[];
}
export interface Sockaddr {
sa_len: number;
sa_family: number;
sa_data: Buffer;
}
export interface Addrinfo extends Sockaddr {
ai_flags: number;
ai_family: number;
ai_socktype: number;
ai_protocol: number;
ai_addrlen: number;
ai_canonname?: string;
}
interface StatsVFS {
f_bsize: number;
f_frsize: number;
f_blocks: number;
f_bfree: number;
f_bavail: number;
f_files: number;
f_ffree: number;
f_favail: number;
f_fsid: number;
f_flag: number;
f_namemax: number;
}
interface Termios {
c_iflag: number;
c_oflag: number;
c_cflag: number;
c_lflag: number;
}
type PosixSpawnFileActions = any[];
interface PosixSpawnAttributes {
sched_priority?: number;
schedpolicy?: number;
flags?: number;
pgroup?: number;
sigmask?: Set<number> | number[];
sigdefault?: Set<number> | number[];
}
interface PosixFunctions {
getpid: () => number;
constants: { [name: string]: number };
alarm: (seconds: number) => number;
chroot: (path: string) => void;
getegid: () => number;
geteuid: () => number;
gethostname: () => string;
getpgid: (number) => number;
getpgrp: () => number;
getppid: () => number;
setpgid: (pid: number, pgid: number) => void;
setregid: (rgid: number, egid: number) => void;
setreuid: (ruid: number, euid: number) => void;
setsid: () => number;
setegid: (gid: number) => void;
seteuid: (uid: number) => void;
sethostname: (name: string) => void;
sleep: (seconds: number) => number;
usleep: (microseconds: number) => number;
ttyname: (fd: number) => string;
dup: (oldfd: number) => number;
dup2: (oldfd: number, newfd: number) => number;
chdir: (path: string) => void;
getcwd: () => string;
fork: () => number;
close_event_loop: () => void;
pipe: () => { readfd: number; writefd: number };
pipe2: (flags: number) => { readfd: number; writefd: number };
getresuid: () => { ruid: number; euid: number; suid: number };
getresgid: () => { rgid: number; egid: number; sgid: number };
setresgid: (rgid: number, egid: number, sgid: number) => void;
setresuid: (ruid: number, euid: number, suid: number) => void;
fcntlSetFlags: (fd: number, flags: number) => void;
fcntlGetFlags: (fd: number) => number;
execv: (pathname: string, argv: string[]) => number;
execvp: (file: string, argv: string[]) => number;
execve: (
pathname: string,
argv: string[],
env: { [key: string]: string }
) => number;
_execve: (
pathname: string,
argv: string[],
envp: string[]
) => number;
fexecve: (
fd: number,
argv: string[],
env: { [key: string]: string }
) => number;
_fexecve: (fd: number, argv: string[], envp: string[]) => number;
fork_exec: (args: {
exec_array: string[];
argv: string[];
envp: string[];
cwd: string;
p2cread: number;
p2cwrite: number;
c2pread: number;
c2pwrite: number;
errread: number;
errwrite: number;
errpipe_read: number;
errpipe_write: number;
fds_to_keep: number[];
err_map: number[];
WASI_FD_INFO: string;
}) => number;
set_inheritable: (fd: number, inheritable: boolean) => void;
is_inheritable: (fd: number) => boolean;
lockf: (fd: number, cmd: number, size: BigInt) => void;
pause: () => number;
if_indextoname: (ifindex: number) => string;
if_nametoindex: (ifname: string) => number;
if_nameindex: () => [number, string][];
_posix_spawn: (
path: string,
fileActions,
attrs,
argv: string[],
envp: string[],
p_version: boolean
) => number;
posix_spawn: (
path: string,
fileActions: PosixSpawnFileActions | undefined | null,
attrs: PosixSpawnAttributes | undefined | null,
argv: string[],
envp: { [key: string]: string } | string[]
) => number;
posix_spawnp: (
path: string,
fileActions: PosixSpawnFileActions | undefined | null,
attrs: PosixSpawnAttributes | undefined | null,
argv: string[],
envp: { [key: string]: string } | string[]
) => number;
wait: () => {
wstatus: number;
ret: number;
};
wait3: (options: number) => {
wstatus: number;
ret: number;
};
waitpid: (
pid: number,
options: number
) => {
wstatus: number;
ret: number;
};
login_tty: (fd: number) => void;
statvfs: (path: string) => StatsVFS;
fstatvfs: (fd: number) => StatsVFS;
ctermid: () => string;
gai_strerror: (errcode: number) => string;
hstrerror: (errcode: number) => string;
gethostbyname: (name: string) => Hostent;
gethostbyaddr: (addr: string) => Hostent;
getaddrinfo: (
node: string,
service: string,
hints?: {
flags?: number;
family?: number;
socktype?: number;
protocol?: number;
}
) => Addrinfo[];
accept: (socket: number) => { fd: number; sockaddr: Sockaddr };
bind: (socket: number, sockaddr: Sockaddr) => void;
connect: (socket: number, sockaddr: Sockaddr) => void;
getsockname: (socket: number) => Sockaddr;
getpeername: (socket: number) => Sockaddr;
listen: (socket: number, backlog: number) => void;
recv: (socket: number, buffer: Buffer, flags: number) => number;
send: (socket: number, buffer: Buffer, flags: number) => number;
shutdown: (socket: number, how: number) => void;
socket: (family: number, socktype: number, protocol: number) => number;
getsockopt: (
socket: number,
level: number,
option_name: number,
max_len: number
) => Buffer;
setsockopt: (
socket: number,
level: number,
option_name: number,
option_value: Buffer
) => void;
getChar: () => string;
enableRawInput: () => void;
setEcho: (enabled: boolean) => void;
makeStdinBlocking: () => void;
tcgetattr: (fd: number) => Termios;
tcsetattr: (fd: number, optional_actions: number, tio: Termios) => void;
watchForSignal: (signal: number) => void;
getSignalState: (signal: number) => boolean;
pollSocket: (fd: number, events: number, timeout_ms: number) => void;
}
export type Posix = Partial<PosixFunctions>;
let mod: Posix = {};
let mod1: Posix = {};
try {
mod = require(`./${name}.node`);
if (process.platform != "linux") {
for (const name of LINUX_ONLY) {
delete mod[name];
}
}
mod.getpid = () => process.pid;
mod["getaddrinfo"] = (node, service, hints) => {
const f = mod["_getaddrinfo"];
if (f == null) throw Error("getaddrinfo is not implemented");
return f(
node,
service,
hints?.flags ?? 0,
hints?.family ?? 0,
hints?.socktype ?? 0,
hints?.protocol ?? 0
);
};
mod["statvfs"] = (...args) => JSON.parse(mod["_statvfs"]?.(...args));
mod["fstatvfs"] = (...args) => JSON.parse(mod["_fstatvfs"]?.(...args));
mod["bind"] = (socket: number, sockaddr: Sockaddr) => {
return mod["_bind"](
socket,
sockaddr.sa_len,
sockaddr.sa_family,
sockaddr.sa_data
);
};
mod["connect"] = (socket: number, sockaddr: Sockaddr) => {
return mod["_connect"](
socket,
sockaddr.sa_len,
sockaddr.sa_family,
sockaddr.sa_data
);
};
for (const name of ["execve", "fexecve"]) {
const f = mod["_" + name];
if (f != null) {
mod[name] = (pathname, argv, env) => {
return f(pathname, argv, mapToStrings(env));
};
}
}
const { _posix_spawn } = mod;
if (_posix_spawn != null) {
for (const name of ["posix_spawn", "posix_spawnp"]) {
mod[name] = (
path,
fileActions,
attrs: PosixSpawnAttributes,
argv,
env
) => {
if (attrs == null) {
attrs = {};
} else {
if (attrs.sigmask != null) {
attrs.sigmask = Array.from(attrs.sigmask);
}
if (attrs.sigdefault != null) {
attrs.sigdefault = Array.from(attrs.sigdefault);
}
}
return _posix_spawn(
path,
fileActions ?? [],
attrs ?? {},
argv,
mapToStrings(env),
name.endsWith("spawnp")
);
};
}
}
for (const name in mod) {
exports[name] = mod1[name] = (...args) => {
if (name != "chdir") log(name, args);
const res = mod[name](...args);
if (name != "chdir") log(name, "returned", res);
return res;
};
}
exports["constants"] = mod1.constants = mod["getConstants"]?.();
} catch (_err) {}
export default mod1;
function is_array(obj: any): boolean {
return Object.prototype.toString.call(obj) === "[object Array]";
}
function mapToStrings(obj: object | string[]): string[] {
if (is_array(obj)) {
return obj as unknown as string[];
}
const v: string[] = [];
for (const key in obj) {
v.push(`${key}=${obj[key]}`);
}
return v;
}