mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-05-28 00:02:28 +00:00
Add seccomp bpf sandboxing to redbean
It's now possible to pass the `-S` or `-SS` flags to sandbox redbean worker proecsses after they've been forked. The first `-S` flag is intended to be a permissive builtin policy that limits system calls to only that which the various parts of redbean serving need. The second `-SS` flag is intended to be more restrictive, preventing things like the Lua extensions you download off the web from using the HTTP client or sockets APIs. In upcoming changes you'll be able to implement your own Berkeley Packet Filter sandbox programs and load them via Lua.
This commit is contained in:
parent
7166679620
commit
5a132f9652
79 changed files with 2271 additions and 651 deletions
|
@ -45,6 +45,7 @@ FLAGS
|
|||
-s increase silence [repeatable]
|
||||
-v increase verbosity [repeatable]
|
||||
-V increase ssl verbosity [repeatable]
|
||||
-S increase bpf seccomp sandboxing [repeatable]
|
||||
-H K:V sets http header globally [repeatable]
|
||||
-D DIR overlay assets in local directory [repeatable]
|
||||
-r /X=/Y redirect X to Y [repeatable]
|
||||
|
@ -1231,7 +1232,7 @@ UNIX MODULE
|
|||
|
||||
Reads from file descriptor.
|
||||
|
||||
unix.write(fd:int, data[, offset]) → rc:int, errno:int
|
||||
unix.write(fd:int, data[, offset]) → rc:int[, errno:int]
|
||||
|
||||
Writes to file descriptor.
|
||||
|
||||
|
@ -1242,25 +1243,25 @@ UNIX MODULE
|
|||
`flags` should have one of `O_RDONLY`, `O_WRONLY`, or `O_RDWR`.
|
||||
The following values may also be OR'd into `flags`:
|
||||
|
||||
- `O_CREAT`: Create file if it doesn't exist.
|
||||
- `O_TRUNC` Automatic truncate(fd,0) if exists.
|
||||
- `O_CLOEXEC`: Automatic close() upon execve().
|
||||
- `O_EXCL`: Exclusive access. See below.
|
||||
- `O_APPEND`: Open file for append only.
|
||||
- `O_DIRECT` (not supported on Apple and OpenBSD)
|
||||
- `O_DIRECTORY` (hint on UNIX but required on NT)
|
||||
- `O_TMPFILE` (for Linux and Windows only)
|
||||
- `O_NOFOLLOW` (zero on Windows)
|
||||
- `O_DSYNC` (zero on non-Linux/Apple)
|
||||
- `O_RSYNC` (zero on non-Linux/Apple)
|
||||
- `O_PATH` (zero on non-Linux)
|
||||
- `O_VERIFY` (zero on non-FreeBSD)
|
||||
- `O_SHLOCK` (zero on non-BSD)
|
||||
- `O_EXLOCK` (zero on non-BSD)
|
||||
- `O_RANDOM` (zero on non-Windows)
|
||||
- `O_SEQUENTIAL` (zero on non-Windows)
|
||||
- `O_COMPRESSED` (zero on non-Windows)
|
||||
- `O_INDEXED` (zero on non-Windows)
|
||||
- `O_CREAT`: create file if it doesn't exist
|
||||
- `O_TRUNC` automatic ftruncate(fd,0) if exists
|
||||
- `O_CLOEXEC`: automatic close() upon execve()
|
||||
- `O_EXCL`: exclusive access (see below)
|
||||
- `O_APPEND`: open file for append only
|
||||
- `O_DIRECT` it's complicated (not supported on Apple and OpenBSD)
|
||||
- `O_DIRECTORY` useful for stat'ing (hint on UNIX but required on NT)
|
||||
- `O_TMPFILE` try to make temp more secure (Linux and Windows only)
|
||||
- `O_NOFOLLOW` fail if it's a symlink (zero on Windows)
|
||||
- `O_DSYNC` it's complicated (zero on non-Linux/Apple)
|
||||
- `O_RSYNC` it's complicated (zero on non-Linux/Apple)
|
||||
- `O_PATH` it's complicated (zero on non-Linux)
|
||||
- `O_VERIFY` it's complicated (zero on non-FreeBSD)
|
||||
- `O_SHLOCK` it's complicated (zero on non-BSD)
|
||||
- `O_EXLOCK` it's complicated (zero on non-BSD)
|
||||
- `O_RANDOM` hint random access intent (zero on non-Windows)
|
||||
- `O_SEQUENTIAL` hint sequential access intent (zero on non-Windows)
|
||||
- `O_COMPRESSED` ask fs to abstract compression (zero on non-Windows)
|
||||
- `O_INDEXED` turns on that slow performance (zero on non-Windows)
|
||||
|
||||
There are three regular combinations for the above flags:
|
||||
|
||||
|
@ -1277,7 +1278,7 @@ UNIX MODULE
|
|||
already. If it does exist then `nil` is returned along with
|
||||
`errno` set to `EEXIST`.
|
||||
|
||||
unix.close(fd:int) → rc:int, errno:int
|
||||
unix.close(fd:int) → rc:int[, errno:int]
|
||||
|
||||
Closes file descriptor.
|
||||
|
||||
|
@ -1288,12 +1289,12 @@ UNIX MODULE
|
|||
will be closed. Any open connections it owns will be reset. This
|
||||
function never returns.
|
||||
|
||||
unix.fork() → childpid|0, errno:int
|
||||
unix.fork() → childpid|0:int[, errno:int]
|
||||
|
||||
Creates a new process mitosis style. This returns twice. The
|
||||
parent process gets the nonzero pid. The child gets zero.
|
||||
|
||||
unix.commandv(prog) → path, errno:int
|
||||
unix.commandv(prog:str) → path:str[, errno:int]
|
||||
|
||||
Performs `$PATH` lookup of executable. We automatically suffix
|
||||
`.com` and `.exe` automatically for all platforms when path
|
||||
|
@ -1302,6 +1303,11 @@ UNIX MODULE
|
|||
`prog` contains slashes then it's not path searched either and
|
||||
will be returned if it exists.
|
||||
|
||||
unix.realpath(filename:str) → abspath:str[, errno:int]
|
||||
|
||||
Returns absolute path of filename, with `.` and `..` components
|
||||
removed, and symlinks will be resolved.
|
||||
|
||||
unix.execve(prog, argv[, envp]) → errno
|
||||
|
||||
Exits current process, replacing it with a new instance of the
|
||||
|
@ -1315,29 +1321,29 @@ UNIX MODULE
|
|||
The first element in `argv` should be `prog`. This function is
|
||||
normally called after forking.
|
||||
|
||||
unix.access(path:str, how) → rc:int, errno:int
|
||||
unix.access(path:str, how) → rc:int[, errno:int]
|
||||
|
||||
Checks if effective user of current process has permission to
|
||||
access file. `how` can be `R_OK`, `W_OK`, `X_OK`, or `F_OK` to
|
||||
check for read, write, execute, and existence respectively.
|
||||
|
||||
unix.mkdir(path:str, mode) → rc:int, errno:int
|
||||
unix.mkdir(path:str, mode) → rc:int[, errno:int]
|
||||
|
||||
Makes directory. `mode` should be octal, e.g. `0755`.
|
||||
|
||||
unix.chdir(path:str) → rc:int, errno:int
|
||||
unix.chdir(path:str) → rc:int[, errno:int]
|
||||
|
||||
Changes current directory to `path`.
|
||||
|
||||
unix.unlink(path:str) → rc:int, errno:int
|
||||
unix.unlink(path:str) → rc:int[, errno:int]
|
||||
|
||||
Removes file at `path`.
|
||||
|
||||
unix.rmdir(path:str) → rc:int, errno:int
|
||||
unix.rmdir(path:str) → rc:int[, errno:int]
|
||||
|
||||
Removes empty directory at `path`.
|
||||
|
||||
unix.chroot(path:str) → rc:int, errno:int
|
||||
unix.chroot(path:str) → rc:int[, errno:int]
|
||||
|
||||
Changes root directory. Raises `ENOSYS` on Windows.
|
||||
|
||||
|
@ -1352,44 +1358,95 @@ UNIX MODULE
|
|||
writing. `flags` can have `O_CLOEXEC`. On error, `reader` and
|
||||
`writer` will be `nil` and `errno` will be set to non-nil.
|
||||
|
||||
unix.rename(oldpath, newpath) → rc:int, errno:int
|
||||
unix.link(existingpath, newpath) → rc:int, errno:int
|
||||
unix.symlink(target, linkpath) → rc:int, errno:int
|
||||
unix.chown(path:str, uid, gid) → rc:int, errno:int
|
||||
unix.chmod(path:str, mode) → rc:int, errno:int
|
||||
unix.getcwd(path:str, mode) → rc:int, errno:int
|
||||
unix.rename(oldpath:str, newpath:str) → rc:int[, errno:int]
|
||||
|
||||
Renames file.
|
||||
|
||||
unix.link(existingpath:str, newpath:str) → rc:int[, errno:int]
|
||||
|
||||
Creates hard link, so your underlying inode has two names.
|
||||
|
||||
unix.symlink(target:str, linkpath:str) → rc:int[, errno:int]
|
||||
|
||||
Creates soft link, or a symbolic link.
|
||||
|
||||
unix.chown(path:str, uid, gid) → rc:int[, errno:int]
|
||||
|
||||
Changes user and gorup on file.
|
||||
|
||||
unix.chmod(path:str, mode) → rc:int[, errno:int]
|
||||
unix.getcwd(path:str, mode) → rc:int[, errno:int]
|
||||
unix.getpid() → pid
|
||||
unix.getppid() → pid
|
||||
unix.kill(pid, sig) → rc:int, errno:int
|
||||
unix.raise(sig) → rc:int, errno:int
|
||||
unix.kill(pid, sig) → rc:int[, errno:int]
|
||||
unix.raise(sig) → rc:int[, errno:int]
|
||||
unix.wait(pid[, options]) → pid, wstatus, nil, errno:int
|
||||
unix.fcntl(fd:int, cmd[, arg]) → rc:int, errno:int
|
||||
unix.fcntl(fd:int, cmd[, arg]) → rc:int[, errno:int]
|
||||
unix.getsid(pid) → sid, errno:int
|
||||
unix.getpgrp() → pgid, errno:int
|
||||
unix.getpgid(pid) → pgid, errno:int
|
||||
unix.setpgid(pid, pgid) → pgid, errno:int
|
||||
unix.setsid() → sid, errno:int
|
||||
unix.getuid() → uid, errno:int
|
||||
unix.setuid(uid:int) → rc:int[, errno:int]
|
||||
unix.getgid() → gid, errno:int
|
||||
unix.umask(mask) → rc:int, errno:int
|
||||
unix.gettime([clock]) → seconds, nanos, errno:int
|
||||
unix.nanosleep(seconds, nanos) → remseconds, remnanos, errno:int
|
||||
unix.sync(fd:int)
|
||||
unix.fsync(fd:int) → rc:int, errno:int
|
||||
unix.fdatasync(fd:int) → rc:int, errno:int
|
||||
unix.setgid(gid:int) → rc:int[, errno:int]
|
||||
unix.umask(mask) → rc:int[, errno:int]
|
||||
|
||||
unix.seek(fd:int, offset, whence) → newpos, errno:int
|
||||
where whence ∈ {SEEK_SET, SEEK_CUR, SEEK_END}
|
||||
whence defaults to SEEK_SET
|
||||
unix.truncate(path:str, length) → rc:int, errno:int
|
||||
unix.truncate(fd:int, length) → rc:int, errno:int
|
||||
unix.syslog(priority:str, msg:str)
|
||||
|
||||
Generates a log message which will be distributed by syslogd.
|
||||
|
||||
`priority` is a bitmask containing the facility value and the
|
||||
level value. If no facility value is ORed into priority, then
|
||||
the default value set by openlog() is used. it set to NULL, the
|
||||
program name is used. Level is one of `LOG_EMERG`, `LOG_ALERT`,
|
||||
`LOG_CRIT`, `LOG_ERR`, `LOG_WARNING`, `LOG_NOTICE`, `LOG_INFO`,
|
||||
`LOG_DEBUG`.
|
||||
|
||||
This function currently works on Linux, Windows, and NetBSD. On
|
||||
WIN32 it uses the ReportEvent() facility.
|
||||
|
||||
unix.clock_gettime([clock]) → seconds, nanos, errno:int
|
||||
|
||||
Returns nanosecond precision timestamp from the system.
|
||||
|
||||
`clock` should be `CLOCK_REALTIME`, `CLOCK_MONOTONIC`, or
|
||||
`CLOCK_MONOTONIC_RAW` since they work across platforms.
|
||||
You may also try your luck with `CLOCK_REALTIME_COARSE`,
|
||||
`CLOCK_MONOTONIC_COARSE`, `CLOCK_PROCESS_CPUTIME_ID`,
|
||||
`CLOCK_TAI`, `CLOCK_PROF`, `CLOCK_BOOTTIME`,
|
||||
`CLOCK_REALTIME_ALARM`, and `CLOCK_BOOTTIME_ALARM`,
|
||||
|
||||
unix.nanosleep(seconds, nanos) → remseconds, remnanos, errno:int
|
||||
|
||||
Sleeps with nanosecond precision.
|
||||
|
||||
unix.sync(fd:int)
|
||||
unix.fsync(fd:int) → rc:int[, errno:int]
|
||||
unix.fdatasync(fd:int) → rc:int[, errno:int]
|
||||
|
||||
These functions are used to make programs slower by asking the
|
||||
operating system to flush data to the physical medium.
|
||||
|
||||
unix.seek(fd:int, offset:int, whence:int) → newpos:int[, errno:int]
|
||||
|
||||
Seeks to file position.
|
||||
|
||||
`whence` can be one of `SEEK_SET`, `SEEK_CUR`, or `SEEK_END`.
|
||||
|
||||
unix.truncate(path:str, length) → rc:int[, errno:int]
|
||||
unix.ftruncate(fd:int, length) → rc:int[, errno:int]
|
||||
|
||||
Reduces or extends underlying physical medium of file.
|
||||
If file was originally larger, content >length is lost.
|
||||
|
||||
unix.socket([family[, type[, protocol]]]) → fd:int[, errno:int]
|
||||
|
||||
`SOCK_CLOEXEC` may be or'd into type
|
||||
`family` defaults to `AF_INET`
|
||||
`type` defaults to `SOCK_STREAM`
|
||||
`protocol` defaults to `IPPROTO_TCP`
|
||||
`family` defaults to `AF_INET` but can be `AF_UNIX`
|
||||
`type` defaults to `SOCK_STREAM` but can be `SOCK_DGRAM`
|
||||
`protocol` defaults to `IPPROTO_TCP` but can be `IPPROTO_UDP`
|
||||
|
||||
unix.socketpair([family[, type[, protocol]]]) → fd1, fd2, errno:int
|
||||
|
||||
|
@ -1398,11 +1455,11 @@ UNIX MODULE
|
|||
`type` defaults to `SOCK_STREAM`
|
||||
`protocol` defaults to `IPPROTO_TCP`
|
||||
|
||||
unix.bind(fd:int, ip, port) → rc:int, errno:int
|
||||
unix.bind(fd:int, ip, port) → rc:int[, errno:int]
|
||||
|
||||
unix.connect(fd:int, ip, port) → rc:int, errno:int
|
||||
unix.connect(fd:int, ip, port) → rc:int[, errno:int]
|
||||
|
||||
unix.listen(fd:int[, backlog]) → rc:int, errno:int
|
||||
unix.listen(fd:int[, backlog]) → rc:int[, errno:int]
|
||||
|
||||
unix.getsockname(fd:int) → ip, port, errno:int
|
||||
|
||||
|
@ -1430,7 +1487,7 @@ UNIX MODULE
|
|||
addresses. The `flags` parameter can have `MSG_OOB`,
|
||||
`MSG_DONTROUTE`, or `MSG_NOSIGNAL`.
|
||||
|
||||
unix.shutdown(fd:int, how:int) → rc:int, errno:int
|
||||
unix.shutdown(fd:int, how:int) → rc:int[, errno:int]
|
||||
|
||||
Partially closes socket. `how` can be `SHUT_RD`, `SHUT_WR`, or
|
||||
`SHUT_RDWR`.
|
||||
|
@ -1602,21 +1659,6 @@ UNIX MODULE
|
|||
- `ENOSYS`: System call not available on this platform. On Windows
|
||||
this is raised by chroot(), setuid(), setgid().
|
||||
|
||||
- `EPERM`: Operation not permitted. Raised by accept(), adjtimex(),
|
||||
arch_prctl(), bdflush(), capget(), chmod(), chown(), chroot(),
|
||||
clock_getres(), copy_file_range(), execve(), fcntl(),
|
||||
get_robust_list(), getdomainname(), getgroups(), gethostname(),
|
||||
getpriority(), getrlimit(), getsid(), gettimeofday(), kill(),
|
||||
link(), mbind(), membarrier(), migrate_pages(), mkdir(), mknod(),
|
||||
mlock(), mmap(), msgctl(), nice(), open(), prctl(), ptrace(),
|
||||
reboot(), rename(), rmdir(), sched_setaffinity(),
|
||||
sched_setattr(), sched_setparam(), sched_setscheduler(),
|
||||
seteuid(), setfsgid(), setfsuid(), setgid(), setpgid(),
|
||||
setresuid(), setreuid(), setsid(), setuid(), setup(), shmget(),
|
||||
sigaltstack(), stime(), swapon(), symlink(), syslog(),
|
||||
timer_create(), timerfd_create(), tkill(), truncate(), u
|
||||
unlink(), utime(), utimensat(), vhangup(), vm86(), write().
|
||||
|
||||
- `ENOENT`: no such file or directory. Raised by access(),
|
||||
alloc_hugepages(), bind(), chdir(), chmod(), chown(), chroot(),
|
||||
clock_getres(), execve(), opendir(), inotify_add_watch(), kcmp(),
|
||||
|
@ -1692,6 +1734,28 @@ UNIX MODULE
|
|||
rmdir(), semget(), send(), setpgid(), shmget(), socket(), stat(),
|
||||
symlink(), truncate(), unlink(), uselib(), utime(), utimensat(),
|
||||
|
||||
- `EPERM`: Operation not permitted. Raised by accept(), chmod(),
|
||||
chown(), chroot(), copy_file_range(), execve(), fallocate(),
|
||||
fanotify_init(), fcntl(), futex(), get_robust_list(),
|
||||
getdomainname(), getgroups(), gethostname(), getpriority(),
|
||||
getrlimit(), getsid(), gettimeofday(), idle(), init_module(),
|
||||
io_submit(), ioctl_console(), ioctl_ficlonerange(),
|
||||
ioctl_fideduperange(), ioctl_ns(), ioctl_tty(), ioperm(), iopl(),
|
||||
ioprio_set(), kcmp(), kexec_load(), keyctl(), kill(), link(),
|
||||
lookup_dcookie(), madvise(), mbind(), membarrier(),
|
||||
migrate_pages(), mkdir(), mknod(), mlock(), mmap(), mount(),
|
||||
move_pages(), msgctl(), nice(), open(), open_by_handle_at(),
|
||||
pciconfig_read(), perf_event_open(), pidfd_getfd(),
|
||||
pidfd_send_signal(), pivot_root(), prctl(), process_vm_readv(),
|
||||
ptrace(), quotactl(), reboot(), rename(), request_key(), rmdir(),
|
||||
rt_sigqueueinfo(), sched_setaffinity(), sched_setattr(),
|
||||
sched_setparam(), sched_setscheduler(), semctl(), seteuid(),
|
||||
setfsgid(), setfsuid(), setgid(), setns(), setpgid(),
|
||||
setresuid(), setreuid(), setsid(), setuid(), setup(), setxattr(),
|
||||
shmctl(), shmget(), sigaltstack(), spu_create(), stime(),
|
||||
swapon(), symlink(), syslog(), truncate(), unlink(), utime(),
|
||||
utimensat(), write()
|
||||
|
||||
- `ENOTBLK`: Block device required. Raised by umount().
|
||||
|
||||
- `EBUSY`: Device or resource busy. Raised by bdflush(), dup(),
|
||||
|
|
260
tool/net/lunix.c
260
tool/net/lunix.c
|
@ -37,6 +37,7 @@
|
|||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sock/sock.h"
|
||||
#include "libc/sock/syslog.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/af.h"
|
||||
#include "libc/sysv/consts/at.h"
|
||||
|
@ -46,6 +47,7 @@
|
|||
#include "libc/sysv/consts/fd.h"
|
||||
#include "libc/sysv/consts/ipproto.h"
|
||||
#include "libc/sysv/consts/itimer.h"
|
||||
#include "libc/sysv/consts/log.h"
|
||||
#include "libc/sysv/consts/msg.h"
|
||||
#include "libc/sysv/consts/nr.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
|
@ -104,6 +106,16 @@ static int ReturnRc(lua_State *L, int64_t rc, int olderr) {
|
|||
return 2;
|
||||
}
|
||||
|
||||
static int ReturnErrno(lua_State *L, int nils, int olderr) {
|
||||
int i;
|
||||
for (i = 0; i < nils; ++i) {
|
||||
lua_pushnil(L);
|
||||
}
|
||||
lua_pushinteger(L, errno);
|
||||
errno = olderr;
|
||||
return nils + 1;
|
||||
}
|
||||
|
||||
static char **ConvertLuaArrayToStringList(lua_State *L, int i) {
|
||||
int j, n;
|
||||
char **p;
|
||||
|
@ -133,13 +145,13 @@ static void FreeStringList(char **p) {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
// System Calls
|
||||
|
||||
// unix.exit([exitcode]) → ⊥
|
||||
// unix.exit([exitcode:int]) → ⊥
|
||||
static wontreturn int LuaUnixExit(lua_State *L) {
|
||||
_Exit(luaL_optinteger(L, 1, 0));
|
||||
}
|
||||
|
||||
// unix.access(path, mode) → rc[, errno]
|
||||
// mode can be: R_OK, W_OK, X_OK, F_OK
|
||||
// unix.access(path:str, how:int) → rc:int[, errno:int]
|
||||
// how can be: R_OK, W_OK, X_OK, F_OK
|
||||
static int LuaUnixAccess(lua_State *L) {
|
||||
const char *file;
|
||||
int rc, mode, olderr;
|
||||
|
@ -150,19 +162,31 @@ static int LuaUnixAccess(lua_State *L) {
|
|||
return ReturnRc(L, rc, olderr);
|
||||
}
|
||||
|
||||
// unix.mkdir(path, mode) → rc[, errno]
|
||||
// unix.mkdir(path:str[, mode:int]) → rc:int[, errno:int]
|
||||
// mode should be octal
|
||||
static int LuaUnixMkdir(lua_State *L) {
|
||||
const char *file;
|
||||
int rc, mode, olderr;
|
||||
olderr = errno;
|
||||
file = luaL_checklstring(L, 1, 0);
|
||||
mode = luaL_checkinteger(L, 2);
|
||||
mode = luaL_optinteger(L, 2, 0755);
|
||||
rc = mkdir(file, mode);
|
||||
return ReturnRc(L, rc, olderr);
|
||||
}
|
||||
|
||||
// unix.chdir(path) → rc[, errno]
|
||||
// unix.makedirs(path:str[, mode:int]) → rc:int[, errno:int]
|
||||
// mode should be octal
|
||||
static int LuaUnixMakedirs(lua_State *L) {
|
||||
const char *file;
|
||||
int rc, mode, olderr;
|
||||
olderr = errno;
|
||||
file = luaL_checklstring(L, 1, 0);
|
||||
mode = luaL_optinteger(L, 2, 0755);
|
||||
rc = makedirs(file, mode);
|
||||
return ReturnRc(L, rc, olderr);
|
||||
}
|
||||
|
||||
// unix.chdir(path:str) → rc:int[, errno:int]
|
||||
static int LuaUnixChdir(lua_State *L) {
|
||||
int rc, olderr;
|
||||
const char *file;
|
||||
|
@ -172,7 +196,7 @@ static int LuaUnixChdir(lua_State *L) {
|
|||
return ReturnRc(L, rc, olderr);
|
||||
}
|
||||
|
||||
// unix.unlink(path) → rc[, errno]
|
||||
// unix.unlink(path:str) → rc:int[, errno:int]
|
||||
static int LuaUnixUnlink(lua_State *L) {
|
||||
int rc, olderr;
|
||||
const char *file;
|
||||
|
@ -182,7 +206,7 @@ static int LuaUnixUnlink(lua_State *L) {
|
|||
return ReturnRc(L, rc, olderr);
|
||||
}
|
||||
|
||||
// unix.rmdir(path) → rc[, errno]
|
||||
// unix.rmdir(path:str) → rc:int[, errno:int]
|
||||
static int LuaUnixRmdir(lua_State *L) {
|
||||
const char *file;
|
||||
int rc, olderr;
|
||||
|
@ -192,7 +216,7 @@ static int LuaUnixRmdir(lua_State *L) {
|
|||
return ReturnRc(L, rc, olderr);
|
||||
}
|
||||
|
||||
// unix.rename(oldpath, newpath) → rc[, errno]
|
||||
// unix.rename(oldpath:str, newpath:str) → rc:int[, errno:int]
|
||||
static int LuaUnixRename(lua_State *L) {
|
||||
const char *oldpath, *newpath;
|
||||
int rc, olderr;
|
||||
|
@ -203,7 +227,7 @@ static int LuaUnixRename(lua_State *L) {
|
|||
return ReturnRc(L, rc, olderr);
|
||||
}
|
||||
|
||||
// unix.link(existingpath, newpath) → rc[, errno]
|
||||
// unix.link(existingpath:str, newpath:str) → rc:int[, errno:int]
|
||||
static int LuaUnixLink(lua_State *L) {
|
||||
const char *existingpath, *newpath;
|
||||
int rc, olderr;
|
||||
|
@ -214,7 +238,7 @@ static int LuaUnixLink(lua_State *L) {
|
|||
return ReturnRc(L, rc, olderr);
|
||||
}
|
||||
|
||||
// unix.symlink(target, linkpath) → rc[, errno]
|
||||
// unix.symlink(target:str, linkpath:str) → rc:int[, errno:int]
|
||||
static int LuaUnixSymlink(lua_State *L) {
|
||||
const char *target, *linkpath;
|
||||
int rc, olderr;
|
||||
|
@ -225,7 +249,7 @@ static int LuaUnixSymlink(lua_State *L) {
|
|||
return ReturnRc(L, rc, olderr);
|
||||
}
|
||||
|
||||
// unix.chown(path, uid, gid) → rc[, errno]
|
||||
// unix.chown(path:str, uid:int, gid:int) → rc:int[, errno:int]
|
||||
static int LuaUnixChown(lua_State *L) {
|
||||
const char *file;
|
||||
int rc, uid, gid, olderr;
|
||||
|
@ -237,7 +261,7 @@ static int LuaUnixChown(lua_State *L) {
|
|||
return ReturnRc(L, rc, olderr);
|
||||
}
|
||||
|
||||
// unix.chmod(path, mode) → rc[, errno]
|
||||
// unix.chmod(path:str, mode:int) → rc:int[, errno:int]
|
||||
static int LuaUnixChmod(lua_State *L) {
|
||||
const char *file;
|
||||
int rc, mode, olderr;
|
||||
|
@ -248,7 +272,7 @@ static int LuaUnixChmod(lua_State *L) {
|
|||
return ReturnRc(L, rc, olderr);
|
||||
}
|
||||
|
||||
// unix.getcwd(path, mode) → rc[, errno]
|
||||
// unix.getcwd(path:str, mode:int) → rc:int[, errno:int]
|
||||
static int LuaUnixGetcwd(lua_State *L) {
|
||||
char *path;
|
||||
path = getcwd(0, 0);
|
||||
|
@ -258,7 +282,7 @@ static int LuaUnixGetcwd(lua_State *L) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
// unix.fork() → childpid|0, errno
|
||||
// unix.fork() → childpid|0:int[, errno:int]
|
||||
static int LuaUnixFork(lua_State *L) {
|
||||
int rc, olderr;
|
||||
olderr = errno;
|
||||
|
@ -298,7 +322,7 @@ static int LuaUnixExecve(lua_State *L) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
// unix.commandv(prog) → path[, errno]
|
||||
// unix.commandv(prog:str) → path:str[, errno:int]
|
||||
static int LuaUnixCommandv(lua_State *L) {
|
||||
const char *prog;
|
||||
int rc, olderr, pushed;
|
||||
|
@ -319,7 +343,29 @@ static int LuaUnixCommandv(lua_State *L) {
|
|||
return pushed;
|
||||
}
|
||||
|
||||
// unix.chroot(path) → rc[, errno]
|
||||
// unix.realpath(path:str) → path:str[, errno:int]
|
||||
static int LuaUnixRealpath(lua_State *L) {
|
||||
char *resolved;
|
||||
int rc, olderr;
|
||||
const char *path;
|
||||
olderr = errno;
|
||||
path = luaL_checkstring(L, 1);
|
||||
if ((resolved = realpath(path, 0))) {
|
||||
lua_pushstring(L, resolved);
|
||||
free(resolved);
|
||||
return 1;
|
||||
} else {
|
||||
return ReturnErrno(L, 1, olderr);
|
||||
}
|
||||
}
|
||||
|
||||
// unix.syslog(priority:str, msg:str)
|
||||
static int LuaUnixSyslog(lua_State *L) {
|
||||
syslog(luaL_checkinteger(L, 1), "%s", luaL_checkstring(L, 2));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// unix.chroot(path:str) → rc:int[, errno:int]
|
||||
static int LuaUnixChroot(lua_State *L) {
|
||||
int rc, olderr;
|
||||
const char *path;
|
||||
|
@ -351,27 +397,23 @@ static int LuaUnixGetrlimit(lua_State *L) {
|
|||
lua_pushinteger(L, rlim.rlim_max);
|
||||
return 2;
|
||||
} else {
|
||||
lua_pushnil(L);
|
||||
lua_pushnil(L);
|
||||
lua_pushinteger(L, errno);
|
||||
errno = olderr;
|
||||
return 3;
|
||||
return ReturnErrno(L, 2, olderr);
|
||||
}
|
||||
}
|
||||
|
||||
// unix.getpid() → pid
|
||||
// unix.getpid() → pid:int
|
||||
static int LuaUnixGetpid(lua_State *L) {
|
||||
lua_pushinteger(L, getpid());
|
||||
return 1;
|
||||
}
|
||||
|
||||
// unix.getppid() → pid
|
||||
// unix.getppid() → pid:int
|
||||
static int LuaUnixGetppid(lua_State *L) {
|
||||
lua_pushinteger(L, getppid());
|
||||
return 1;
|
||||
}
|
||||
|
||||
// unix.kill(pid, sig) → rc[, errno]
|
||||
// unix.kill(pid, sig) → rc:int[, errno:int]
|
||||
static int LuaUnixKill(lua_State *L) {
|
||||
int rc, pid, sig, olderr;
|
||||
olderr = errno;
|
||||
|
@ -381,7 +423,7 @@ static int LuaUnixKill(lua_State *L) {
|
|||
return ReturnRc(L, rc, olderr);
|
||||
}
|
||||
|
||||
// unix.raise(sig) → rc[, errno]
|
||||
// unix.raise(sig) → rc:int[, errno:int]
|
||||
static int LuaUnixRaise(lua_State *L) {
|
||||
int rc, sig, olderr;
|
||||
olderr = errno;
|
||||
|
@ -402,16 +444,11 @@ static int LuaUnixWait(lua_State *L) {
|
|||
lua_pushinteger(L, wstatus);
|
||||
return 2;
|
||||
} else {
|
||||
lua_pushnil(L);
|
||||
lua_pushnil(L);
|
||||
lua_pushnil(L); // for future use
|
||||
lua_pushinteger(L, errno);
|
||||
errno = olderr;
|
||||
return 4;
|
||||
return ReturnErrno(L, 3, olderr);
|
||||
}
|
||||
}
|
||||
|
||||
// unix.fcntl(fd, cmd[, arg]) → rc[, errno]
|
||||
// unix.fcntl(fd, cmd[, arg]) → rc:int[, errno:int]
|
||||
static int LuaUnixFcntl(lua_State *L) {
|
||||
intptr_t arg;
|
||||
int rc, fd, cmd, olderr;
|
||||
|
@ -450,15 +487,11 @@ static int LuaUnixPipe(lua_State *L) {
|
|||
lua_pushinteger(L, pipefd[1]);
|
||||
return 2;
|
||||
} else {
|
||||
lua_pushnil(L);
|
||||
lua_pushnil(L);
|
||||
lua_pushinteger(L, errno);
|
||||
errno = olderr;
|
||||
return 3;
|
||||
return ReturnErrno(L, 2, olderr);
|
||||
}
|
||||
}
|
||||
|
||||
// unix.getsid(pid) → sid, errno
|
||||
// unix.getsid(pid) → sid:int[, errno:int]
|
||||
static int LuaUnixGetsid(lua_State *L) {
|
||||
int rc, pid, olderr;
|
||||
olderr = errno;
|
||||
|
@ -467,7 +500,7 @@ static int LuaUnixGetsid(lua_State *L) {
|
|||
return ReturnRc(L, rc, olderr);
|
||||
}
|
||||
|
||||
// unix.getpgrp() → pgid, errno
|
||||
// unix.getpgrp() → pgid:int[, errno:int]
|
||||
static int LuaUnixGetpgrp(lua_State *L) {
|
||||
int rc, olderr;
|
||||
olderr = errno;
|
||||
|
@ -475,7 +508,7 @@ static int LuaUnixGetpgrp(lua_State *L) {
|
|||
return ReturnRc(L, rc, olderr);
|
||||
}
|
||||
|
||||
// unix.getpgid(pid) → pgid, errno
|
||||
// unix.getpgid(pid:int) → pgid:int[, errno:int]
|
||||
static int LuaUnixGetpgid(lua_State *L) {
|
||||
int rc, pid, olderr;
|
||||
olderr = errno;
|
||||
|
@ -484,7 +517,7 @@ static int LuaUnixGetpgid(lua_State *L) {
|
|||
return ReturnRc(L, rc, olderr);
|
||||
}
|
||||
|
||||
// unix.umask(mask) → rc[, errno]
|
||||
// unix.umask(mask:int) → rc:int[, errno:int]
|
||||
static int LuaUnixUmask(lua_State *L) {
|
||||
int rc, mask, olderr;
|
||||
olderr = errno;
|
||||
|
@ -493,7 +526,7 @@ static int LuaUnixUmask(lua_State *L) {
|
|||
return ReturnRc(L, rc, olderr);
|
||||
}
|
||||
|
||||
// unix.setpgid(pid, pgid) → pgid, errno
|
||||
// unix.setpgid(pid:int, pgid:int) → pgid:int[, errno:int]
|
||||
static int LuaUnixSetpgid(lua_State *L) {
|
||||
int rc, pid, pgid, olderr;
|
||||
olderr = errno;
|
||||
|
@ -503,7 +536,7 @@ static int LuaUnixSetpgid(lua_State *L) {
|
|||
return ReturnRc(L, rc, olderr);
|
||||
}
|
||||
|
||||
// unix.setsid() → sid, errno
|
||||
// unix.setsid() → sid:int[, errno:int]
|
||||
static int LuaUnixSetsid(lua_State *L) {
|
||||
int rc, olderr;
|
||||
olderr = errno;
|
||||
|
@ -511,7 +544,7 @@ static int LuaUnixSetsid(lua_State *L) {
|
|||
return ReturnRc(L, rc, olderr);
|
||||
}
|
||||
|
||||
// unix.getuid() → uid, errno
|
||||
// unix.getuid() → uid[, errno]
|
||||
static int LuaUnixGetuid(lua_State *L) {
|
||||
int rc, olderr;
|
||||
olderr = errno;
|
||||
|
@ -519,7 +552,13 @@ static int LuaUnixGetuid(lua_State *L) {
|
|||
return ReturnRc(L, rc, olderr);
|
||||
}
|
||||
|
||||
// unix.getgid() → gid, errno
|
||||
// unix.setuid(uid:int) → rc:int[, errno:int]
|
||||
static int LuaUnixSetuid(lua_State *L) {
|
||||
int olderr = errno;
|
||||
return ReturnRc(L, setuid(luaL_checkinteger(L, 1)), olderr);
|
||||
}
|
||||
|
||||
// unix.getgid() → gid:int[, errno:int]
|
||||
static int LuaUnixGetgid(lua_State *L) {
|
||||
int rc, olderr;
|
||||
olderr = errno;
|
||||
|
@ -527,7 +566,13 @@ static int LuaUnixGetgid(lua_State *L) {
|
|||
return ReturnRc(L, rc, olderr);
|
||||
}
|
||||
|
||||
// unix.gettime([clock]) → seconds, nanos, errno
|
||||
// unix.setgid(gid:int) → rc:int[, errno:int]
|
||||
static int LuaUnixSetgid(lua_State *L) {
|
||||
int olderr = errno;
|
||||
return ReturnRc(L, setgid(luaL_checkinteger(L, 1)), olderr);
|
||||
}
|
||||
|
||||
// unix.clock_gettime([clock]) → seconds, nanos, errno
|
||||
static int LuaUnixGettime(lua_State *L) {
|
||||
struct timespec ts;
|
||||
int rc, clock, olderr;
|
||||
|
@ -539,11 +584,7 @@ static int LuaUnixGettime(lua_State *L) {
|
|||
lua_pushinteger(L, ts.tv_nsec);
|
||||
return 2;
|
||||
} else {
|
||||
lua_pushnil(L);
|
||||
lua_pushnil(L);
|
||||
lua_pushinteger(L, errno);
|
||||
errno = olderr;
|
||||
return 3;
|
||||
return ReturnErrno(L, 2, olderr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -560,21 +601,17 @@ static int LuaUnixNanosleep(lua_State *L) {
|
|||
lua_pushinteger(L, rem.tv_nsec);
|
||||
return 2;
|
||||
} else {
|
||||
lua_pushnil(L);
|
||||
lua_pushnil(L);
|
||||
lua_pushinteger(L, errno);
|
||||
errno = olderr;
|
||||
return 3;
|
||||
return ReturnErrno(L, 2, olderr);
|
||||
}
|
||||
}
|
||||
|
||||
// unix.sync(fd)
|
||||
// unix.sync(fd:int)
|
||||
static int LuaUnixSync(lua_State *L) {
|
||||
sync();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// unix.fsync(fd) → rc[, errno]
|
||||
// unix.fsync(fd:int) → rc:int[, errno:int]
|
||||
static int LuaUnixFsync(lua_State *L) {
|
||||
int rc, fd, olderr;
|
||||
olderr = errno;
|
||||
|
@ -583,7 +620,7 @@ static int LuaUnixFsync(lua_State *L) {
|
|||
return ReturnRc(L, rc, olderr);
|
||||
}
|
||||
|
||||
// unix.fdatasync(fd) → rc[, errno]
|
||||
// unix.fdatasync(fd:int) → rc:int[, errno:int]
|
||||
static int LuaUnixFdatasync(lua_State *L) {
|
||||
int rc, fd, olderr;
|
||||
olderr = errno;
|
||||
|
@ -604,7 +641,7 @@ static int LuaUnixOpen(lua_State *L) {
|
|||
return ReturnRc(L, rc, olderr);
|
||||
}
|
||||
|
||||
// unix.close(fd) → rc[, errno]
|
||||
// unix.close(fd:int) → rc:int[, errno:int]
|
||||
static int LuaUnixClose(lua_State *L) {
|
||||
int rc, fd, olderr;
|
||||
olderr = errno;
|
||||
|
@ -627,8 +664,8 @@ static int LuaUnixSeek(lua_State *L) {
|
|||
return ReturnRc(L, newpos, olderr);
|
||||
}
|
||||
|
||||
// unix.truncate(path, length) → rc[, errno]
|
||||
// unix.truncate(fd, length) → rc[, errno]
|
||||
// unix.truncate(path, length) → rc:int[, errno:int]
|
||||
// unix.truncate(fd, length) → rc:int[, errno:int]
|
||||
static int LuaUnixTruncate(lua_State *L) {
|
||||
int64_t length;
|
||||
const char *path;
|
||||
|
@ -649,7 +686,7 @@ static int LuaUnixTruncate(lua_State *L) {
|
|||
return ReturnRc(L, rc, olderr);
|
||||
}
|
||||
|
||||
// unix.read(fd[, bufsiz, offset]) → data, errno
|
||||
// unix.read(fd:int[, bufsiz:str, offset:int]) → data:str, errno:int
|
||||
static int LuaUnixRead(lua_State *L) {
|
||||
char *buf;
|
||||
size_t got;
|
||||
|
@ -680,7 +717,7 @@ static int LuaUnixRead(lua_State *L) {
|
|||
return pushed;
|
||||
}
|
||||
|
||||
// unix.write(fd, data[, offset]) → rc[, errno]
|
||||
// unix.write(fd:int, data:str[, offset:int]) → rc:int[, errno:int]
|
||||
static int LuaUnixWrite(lua_State *L) {
|
||||
size_t size;
|
||||
int fd, olderr;
|
||||
|
@ -699,8 +736,8 @@ static int LuaUnixWrite(lua_State *L) {
|
|||
return ReturnRc(L, rc, olderr);
|
||||
}
|
||||
|
||||
// unix.stat(path) → UnixStat*[, errno]
|
||||
// unix.stat(fd) → UnixStat*[, errno]
|
||||
// unix.stat(path:str) → UnixStat*[, errno]
|
||||
// unix.stat(fd:int) → UnixStat*[, errno]
|
||||
static int LuaUnixStat(lua_State *L) {
|
||||
const char *path;
|
||||
int rc, fd, olderr;
|
||||
|
@ -731,8 +768,8 @@ static int LuaUnixStat(lua_State *L) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
// unix.opendir(path) → UnixDir*[, errno]
|
||||
// unix.opendir(fd) → UnixDir*[, errno]
|
||||
// unix.opendir(path:str) → UnixDir*[, errno]
|
||||
// unix.opendir(fd:int) → UnixDir*[, errno]
|
||||
static int LuaUnixOpendir(lua_State *L) {
|
||||
DIR *rc;
|
||||
int fd, olderr;
|
||||
|
@ -797,15 +834,11 @@ static int LuaUnixSocketpair(lua_State *L) {
|
|||
lua_pushinteger(L, sv[1]);
|
||||
return 2;
|
||||
} else {
|
||||
lua_pushnil(L);
|
||||
lua_pushnil(L);
|
||||
lua_pushinteger(L, errno);
|
||||
errno = olderr;
|
||||
return 3;
|
||||
return ReturnErrno(L, 2, olderr);
|
||||
}
|
||||
}
|
||||
|
||||
// unix.bind(fd, ip, port) → rc[, errno]
|
||||
// unix.bind(fd, ip, port) → rc:int[, errno:int]
|
||||
// SOCK_CLOEXEC may be or'd into type
|
||||
// family defaults to AF_INET
|
||||
// type defaults to SOCK_STREAM
|
||||
|
@ -822,7 +855,7 @@ static int LuaUnixBind(lua_State *L) {
|
|||
return ReturnRc(L, rc, olderr);
|
||||
}
|
||||
|
||||
// unix.connect(fd, ip, port) → rc[, errno]
|
||||
// unix.connect(fd, ip, port) → rc:int[, errno:int]
|
||||
// SOCK_CLOEXEC may be or'd into type
|
||||
// family defaults to AF_INET
|
||||
// type defaults to SOCK_STREAM
|
||||
|
@ -839,7 +872,7 @@ static int LuaUnixConnect(lua_State *L) {
|
|||
return ReturnRc(L, rc, olderr);
|
||||
}
|
||||
|
||||
// unix.listen(fd[, backlog]) → rc[, errno]
|
||||
// unix.listen(fd[, backlog]) → rc:int[, errno:int]
|
||||
static int LuaUnixListen(lua_State *L) {
|
||||
int rc, fd, olderr, backlog;
|
||||
olderr = errno;
|
||||
|
@ -863,11 +896,7 @@ static int LuaUnixGetsockname(lua_State *L) {
|
|||
lua_pushinteger(L, ntohs(sa.sin_port));
|
||||
return 2;
|
||||
} else {
|
||||
lua_pushnil(L);
|
||||
lua_pushnil(L);
|
||||
lua_pushinteger(L, errno);
|
||||
errno = olderr;
|
||||
return 3;
|
||||
return ReturnErrno(L, 2, olderr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -885,11 +914,7 @@ static int LuaUnixGetpeername(lua_State *L) {
|
|||
lua_pushinteger(L, ntohs(sa.sin_port));
|
||||
return 2;
|
||||
} else {
|
||||
lua_pushnil(L);
|
||||
lua_pushnil(L);
|
||||
lua_pushinteger(L, errno);
|
||||
errno = olderr;
|
||||
return 3;
|
||||
return ReturnErrno(L, 2, olderr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -909,12 +934,7 @@ static int LuaUnixAccept(lua_State *L) {
|
|||
lua_pushinteger(L, ntohs(sa.sin_port));
|
||||
return 3;
|
||||
} else {
|
||||
lua_pushnil(L);
|
||||
lua_pushnil(L);
|
||||
lua_pushnil(L);
|
||||
lua_pushinteger(L, errno);
|
||||
errno = olderr;
|
||||
return 4;
|
||||
return ReturnErrno(L, 3, olderr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1016,7 +1036,7 @@ static int LuaUnixSendto(lua_State *L) {
|
|||
return ReturnRc(L, rc, olderr);
|
||||
}
|
||||
|
||||
// unix.shutdown(fd, how) → rc[, errno]
|
||||
// unix.shutdown(fd, how) → rc:int[, errno:int]
|
||||
// how can be SHUT_RD, SHUT_WR, or SHUT_RDWR
|
||||
static int LuaUnixShutdown(lua_State *L) {
|
||||
int rc, fd, how, olderr;
|
||||
|
@ -1149,12 +1169,7 @@ static int LuaUnixSigaction(lua_State *L) {
|
|||
lua_pushinteger(L, oldsa.sa_mask.__bits[0]);
|
||||
return 3;
|
||||
} else {
|
||||
lua_pushnil(L);
|
||||
lua_pushnil(L);
|
||||
lua_pushnil(L);
|
||||
lua_pushinteger(L, errno);
|
||||
errno = olderr;
|
||||
return 4;
|
||||
return ReturnErrno(L, 3, olderr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1206,13 +1221,7 @@ static int LuaUnixSetitimer(lua_State *L) {
|
|||
lua_pushinteger(L, oldit.it_value.tv_usec);
|
||||
return 4;
|
||||
} else {
|
||||
lua_pushnil(L);
|
||||
lua_pushnil(L);
|
||||
lua_pushnil(L);
|
||||
lua_pushnil(L);
|
||||
lua_pushinteger(L, errno);
|
||||
errno = olderr;
|
||||
return 5;
|
||||
return ReturnErrno(L, 4, olderr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1362,7 +1371,7 @@ static int FreeUnixDir(struct UnixDir *dir) {
|
|||
return closedir(dir->dir);
|
||||
}
|
||||
|
||||
// UnixDir:close() → rc[, errno]
|
||||
// UnixDir:close() → rc:int[, errno:int]
|
||||
// may be called multiple times
|
||||
// called by the garbage collector too
|
||||
static int LuaUnixDirClose(lua_State *L) {
|
||||
|
@ -1393,13 +1402,7 @@ static int LuaUnixDirRead(lua_State *L) {
|
|||
} else if (!ent && !errno) {
|
||||
return 0; // end of listing
|
||||
} else {
|
||||
lua_pushnil(L);
|
||||
lua_pushnil(L);
|
||||
lua_pushnil(L);
|
||||
lua_pushnil(L);
|
||||
lua_pushinteger(L, errno);
|
||||
errno = olderr;
|
||||
return 5;
|
||||
return ReturnErrno(L, 4, olderr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1414,10 +1417,7 @@ static int LuaUnixDirFd(lua_State *L) {
|
|||
lua_pushinteger(L, fd);
|
||||
return 1;
|
||||
} else {
|
||||
lua_pushnil(L);
|
||||
lua_pushinteger(L, errno);
|
||||
errno = olderr;
|
||||
return 2;
|
||||
return ReturnErrno(L, 1, olderr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1478,12 +1478,15 @@ static const luaL_Reg kLuaUnix[] = {
|
|||
{"fork", LuaUnixFork}, // make child process via mitosis
|
||||
{"execve", LuaUnixExecve}, // replace process with program
|
||||
{"commandv", LuaUnixCommandv}, // resolve program on $PATH
|
||||
{"realpath", LuaUnixRealpath}, // abspath without dots/symlinks
|
||||
{"syslog", LuaUnixSyslog}, // logs to system log
|
||||
{"kill", LuaUnixKill}, // signal child process
|
||||
{"raise", LuaUnixRaise}, // signal this process
|
||||
{"wait", LuaUnixWait}, // wait for child to change status
|
||||
{"pipe", LuaUnixPipe}, // create two anon fifo fds
|
||||
{"dup", LuaUnixDup}, // copy fd to lowest empty slot
|
||||
{"mkdir", LuaUnixMkdir}, // make directory
|
||||
{"makedirs", LuaUnixMakedirs}, // make directory and parents too
|
||||
{"rmdir", LuaUnixRmdir}, // remove empty directory
|
||||
{"opendir", LuaUnixOpendir}, // read directory entry list
|
||||
{"rename", LuaUnixRename}, // rename file or directory
|
||||
|
@ -1494,8 +1497,9 @@ static const luaL_Reg kLuaUnix[] = {
|
|||
{"fsync", LuaUnixFsync}, // flush open file
|
||||
{"fdatasync", LuaUnixFdatasync}, // flush open file w/o metadata
|
||||
{"truncate", LuaUnixTruncate}, // shrink or extend file medium
|
||||
{"umask", LuaUnixChroot}, // change root directory
|
||||
{"chroot", LuaUnixGetppid}, // get parent process id
|
||||
{"ftruncate", LuaUnixTruncate}, // shrink or extend file medium
|
||||
{"umask", LuaUnixUmask}, // change root directory
|
||||
{"chroot", LuaUnixChroot}, // get parent process id
|
||||
{"setrlimit", LuaUnixSetrlimit}, // prevent cpu memory bombs
|
||||
{"getrlimit", LuaUnixGetrlimit}, // query resource limits
|
||||
{"getppid", LuaUnixGetppid}, // get parent process id
|
||||
|
@ -1506,8 +1510,10 @@ static const luaL_Reg kLuaUnix[] = {
|
|||
{"setsid", LuaUnixSetsid}, // create a new session id
|
||||
{"getpid", LuaUnixGetpid}, // get id of this process
|
||||
{"getuid", LuaUnixGetuid}, // get real user id of process
|
||||
{"setuid", LuaUnixSetuid}, // set real user id of process
|
||||
{"getgid", LuaUnixGetgid}, // get real group id of process
|
||||
{"gettime", LuaUnixGettime}, // get timestamp w/ nano precision
|
||||
{"setgid", LuaUnixSetgid}, // set real group id of process
|
||||
{"clock_gettime", LuaUnixGettime}, // get timestamp w/ nano precision
|
||||
{"nanosleep", LuaUnixNanosleep}, // sleep w/ nano precision
|
||||
{"socket", LuaUnixSocket}, // create network communication fd
|
||||
{"socketpair", LuaUnixSocketpair}, // create bidirectional pipe
|
||||
|
@ -1685,5 +1691,15 @@ int LuaUnix(lua_State *L) {
|
|||
LuaSetIntField(L, "ITIMER_PROF", ITIMER_PROF);
|
||||
LuaSetIntField(L, "ITIMER_VIRTUAL", ITIMER_VIRTUAL);
|
||||
|
||||
// syslog() stuff
|
||||
LuaSetIntField(L, "LOG_EMERG", LOG_EMERG);
|
||||
LuaSetIntField(L, "LOG_ALERT", LOG_ALERT);
|
||||
LuaSetIntField(L, "LOG_CRIT", LOG_CRIT);
|
||||
LuaSetIntField(L, "LOG_ERR", LOG_ERR);
|
||||
LuaSetIntField(L, "LOG_WARNING", LOG_WARNING);
|
||||
LuaSetIntField(L, "LOG_NOTICE", LOG_NOTICE);
|
||||
LuaSetIntField(L, "LOG_INFO", LOG_INFO);
|
||||
LuaSetIntField(L, "LOG_DEBUG", LOG_DEBUG);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -22,11 +22,11 @@
|
|||
#include "libc/bits/popcnt.h"
|
||||
#include "libc/bits/safemacros.internal.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/issandboxed.h"
|
||||
#include "libc/calls/math.h"
|
||||
#include "libc/calls/sigbits.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/struct/dirent.h"
|
||||
#include "libc/calls/struct/filter.h"
|
||||
#include "libc/calls/struct/flock.h"
|
||||
#include "libc/calls/struct/rusage.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
|
@ -71,6 +71,7 @@
|
|||
#include "libc/str/str.h"
|
||||
#include "libc/str/undeflate.h"
|
||||
#include "libc/sysv/consts/af.h"
|
||||
#include "libc/sysv/consts/audit.h"
|
||||
#include "libc/sysv/consts/auxv.h"
|
||||
#include "libc/sysv/consts/dt.h"
|
||||
#include "libc/sysv/consts/ex.h"
|
||||
|
@ -90,7 +91,6 @@
|
|||
#include "libc/sysv/consts/prot.h"
|
||||
#include "libc/sysv/consts/rusage.h"
|
||||
#include "libc/sysv/consts/s.h"
|
||||
#include "libc/sysv/consts/seccomp.h"
|
||||
#include "libc/sysv/consts/shut.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/consts/so.h"
|
||||
|
@ -144,6 +144,7 @@
|
|||
#include "tool/build/lib/case.h"
|
||||
#include "tool/build/lib/psk.h"
|
||||
#include "tool/net/luacheck.h"
|
||||
#include "tool/net/sandbox.h"
|
||||
|
||||
STATIC_STACK_SIZE(0x40000);
|
||||
STATIC_YOINK("zip_uri_support");
|
||||
|
@ -367,7 +368,6 @@ static bool terminated;
|
|||
static bool uniprocess;
|
||||
static bool invalidated;
|
||||
static bool logmessages;
|
||||
static bool issandboxed;
|
||||
static bool isinitialized;
|
||||
static bool checkedmethod;
|
||||
static bool sslinitialized;
|
||||
|
@ -393,6 +393,7 @@ static int zfd;
|
|||
static int frags;
|
||||
static int gmtoff;
|
||||
static int client;
|
||||
static int sandboxed;
|
||||
static int changeuid;
|
||||
static int changegid;
|
||||
static int isyielding;
|
||||
|
@ -1168,10 +1169,10 @@ static void ReportWorkerExit(int pid, int ws) {
|
|||
static void ReportWorkerResources(int pid, struct rusage *ru) {
|
||||
char *s, *b = 0;
|
||||
if (logrusage || LOGGABLE(kLogDebug)) {
|
||||
AppendResourceReport(&b, ru, "\n");
|
||||
AppendResourceReport(&b, ru, "\r\n");
|
||||
if (b) {
|
||||
if ((s = IndentLines(b, appendz(b).i - 1, 0, 1))) {
|
||||
LOGF(kLogDebug, "(stat) resource report for pid %d\n%s", pid, s);
|
||||
LOGF(kLogDebug, "(stat) resource report for pid %d\r\n%s", pid, s);
|
||||
free(s);
|
||||
}
|
||||
free(b);
|
||||
|
@ -2217,15 +2218,44 @@ static void *LoadAsset(struct Asset *a, size_t *out_size) {
|
|||
}
|
||||
}
|
||||
|
||||
static wontreturn void PrintUsage(FILE *f, int rc) {
|
||||
static const char *GetPagerPath(char path[PATH_MAX]) {
|
||||
const char *s;
|
||||
if ((s = commandv("less", path))) return s;
|
||||
if ((s = commandv("more", path))) return s;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static wontreturn void PrintUsage(int fd, int rc) {
|
||||
size_t n;
|
||||
int pip[2];
|
||||
const char *p;
|
||||
struct Asset *a;
|
||||
if ((a = GetAssetZip("/help.txt", 9)) && (p = LoadAsset(a, &n))) {
|
||||
fwrite(p, 1, n, f);
|
||||
free(p);
|
||||
char *args[2] = {0};
|
||||
char pathbuf[PATH_MAX];
|
||||
if (!(a = GetAssetZip("/help.txt", 9)) || !(p = LoadAsset(a, &n))) {
|
||||
fprintf(stderr, "error: /help.txt is not a zip asset\n");
|
||||
exit(1);
|
||||
}
|
||||
if (isatty(0) && isatty(1) && (args[0] = GetPagerPath(pathbuf))) {
|
||||
sigaction(SIGPIPE, &(struct sigaction){.sa_handler = SIG_IGN}, 0);
|
||||
close(0);
|
||||
pipe(pip);
|
||||
if (!fork()) {
|
||||
close(pip[1]);
|
||||
execv(args[0], args);
|
||||
_Exit(127);
|
||||
}
|
||||
close(0);
|
||||
WritevAll(pip[1], &(struct iovec){p, n}, 1);
|
||||
close(pip[1]);
|
||||
wait(0);
|
||||
free(p);
|
||||
exit(0);
|
||||
} else {
|
||||
WritevAll(fd, &(struct iovec){p, n}, 1);
|
||||
free(p);
|
||||
exit(rc);
|
||||
}
|
||||
exit(rc);
|
||||
}
|
||||
|
||||
static void AppendLogo(void) {
|
||||
|
@ -3623,7 +3653,7 @@ static void LogMessage(const char *d, const char *s, size_t n) {
|
|||
while (n && (s[n - 1] == '\r' || s[n - 1] == '\n')) --n;
|
||||
if ((s2 = DecodeLatin1(s, n, &n2))) {
|
||||
if ((s3 = IndentLines(s2, n2, &n3, 1))) {
|
||||
INFOF("(stat) %s %,ld byte message\n%.*s", d, n, n3, s3);
|
||||
INFOF("(stat) %s %,ld byte message\r\n%.*s", d, n, n3, s3);
|
||||
free(s3);
|
||||
}
|
||||
free(s2);
|
||||
|
@ -3638,7 +3668,7 @@ static void LogBody(const char *d, const char *s, size_t n) {
|
|||
while (n && (s[n - 1] == '\r' || s[n - 1] == '\n')) --n;
|
||||
if ((s2 = VisualizeControlCodes(s, n, &n2))) {
|
||||
if ((s3 = IndentLines(s2, n2, &n3, 1))) {
|
||||
INFOF("(stat) %s %,ld byte payload\n%.*s", d, n, n3, s3);
|
||||
INFOF("(stat) %s %,ld byte payload\r\n%.*s", d, n, n3, s3);
|
||||
free(s3);
|
||||
}
|
||||
free(s2);
|
||||
|
@ -3772,8 +3802,8 @@ static int LuaFetch(lua_State *L) {
|
|||
*/
|
||||
DEBUGF("(ftch) client resolving %s", host);
|
||||
if ((rc = getaddrinfo(host, port, &hints, &addr)) != EAI_SUCCESS) {
|
||||
luaL_error(L, "getaddrinfo(%s:%s) error: EAI_%s", host, port,
|
||||
gai_strerror(rc));
|
||||
luaL_error(L, "getaddrinfo(%s:%s) error: EAI_%s %s", host, port,
|
||||
gai_strerror(rc), strerror(errno));
|
||||
unreachable;
|
||||
}
|
||||
|
||||
|
@ -5516,11 +5546,6 @@ static const luaL_Reg kLuaFuncs[] = {
|
|||
{"Underlong", LuaUnderlong}, //
|
||||
{"VisualizeControlCodes", LuaVisualizeControlCodes}, //
|
||||
{"Write", LuaWrite}, //
|
||||
{"bsf", LuaBsf}, //
|
||||
{"bsr", LuaBsr}, //
|
||||
{"crc32", LuaCrc32}, //
|
||||
{"crc32c", LuaCrc32c}, //
|
||||
{"popcnt", LuaPopcnt}, //
|
||||
#ifndef UNSECURE
|
||||
{"Fetch", LuaFetch}, //
|
||||
{"EvadeDragnetSurveillance", LuaEvadeDragnetSurveillance}, //
|
||||
|
@ -6581,13 +6606,90 @@ static void CloseServerFds(void) {
|
|||
}
|
||||
|
||||
static int ExitWorker(void) {
|
||||
if (IsModeDbg() && !issandboxed) {
|
||||
if (IsModeDbg() && !sandboxed) {
|
||||
isexitingworker = true;
|
||||
return eintr();
|
||||
}
|
||||
_Exit(0);
|
||||
}
|
||||
|
||||
static const struct sock_filter kSandboxOnline[] = {
|
||||
_SECCOMP_MACHINE(AUDIT_ARCH_X86_64), //
|
||||
_SECCOMP_LOAD_SYSCALL_NR(), //
|
||||
_SECCOMP_ALLOW_SYSCALL(0x0013), // readv
|
||||
_SECCOMP_ALLOW_SYSCALL(0x0014), // writev
|
||||
_SECCOMP_ALLOW_SYSCALL(0x0009), // mmap
|
||||
_SECCOMP_ALLOW_SYSCALL(0x000b), // munmap
|
||||
_SECCOMP_ALLOW_SYSCALL(0x0000), // read
|
||||
_SECCOMP_ALLOW_SYSCALL(0x0001), // write
|
||||
_SECCOMP_ALLOW_SYSCALL(0x0003), // close
|
||||
_SECCOMP_ALLOW_SYSCALL(0x0008), // lseek
|
||||
_SECCOMP_ALLOW_SYSCALL(0x000f), // rt_sigreturn
|
||||
_SECCOMP_ALLOW_SYSCALL(0x00e7), // exit_group
|
||||
_SECCOMP_ALLOW_SYSCALL(0x0106), // newfstatat
|
||||
_SECCOMP_ALLOW_SYSCALL(0x00e4), // clock_gettime
|
||||
_SECCOMP_ALLOW_SYSCALL(0x003f), // uname
|
||||
_SECCOMP_ALLOW_SYSCALL(0x0048), // fcntl
|
||||
_SECCOMP_ALLOW_SYSCALL(0x0029), // socket
|
||||
_SECCOMP_ALLOW_SYSCALL(0x002a), // connect
|
||||
_SECCOMP_ALLOW_SYSCALL(0x002c), // sendto
|
||||
_SECCOMP_ALLOW_SYSCALL(0x002d), // recvfrom
|
||||
_SECCOMP_ALLOW_SYSCALL(0x0036), // setsockopt
|
||||
_SECCOMP_LOG_AND_RETURN_ERRNO(1), // EPERM
|
||||
};
|
||||
|
||||
static const struct sock_filter kSandboxOffline[] = {
|
||||
_SECCOMP_MACHINE(AUDIT_ARCH_X86_64), //
|
||||
_SECCOMP_LOAD_SYSCALL_NR(), //
|
||||
_SECCOMP_ALLOW_SYSCALL(0x0013), // readv
|
||||
_SECCOMP_ALLOW_SYSCALL(0x0014), // writev
|
||||
_SECCOMP_ALLOW_SYSCALL(0x0000), // read
|
||||
_SECCOMP_ALLOW_SYSCALL(0x0001), // write
|
||||
_SECCOMP_ALLOW_SYSCALL(0x0009), // mmap
|
||||
_SECCOMP_ALLOW_SYSCALL(0x000b), // munmap
|
||||
_SECCOMP_ALLOW_SYSCALL(0x0003), // close
|
||||
_SECCOMP_ALLOW_SYSCALL(0x0008), // lseek
|
||||
_SECCOMP_ALLOW_SYSCALL(0x000f), // rt_sigreturn
|
||||
_SECCOMP_ALLOW_SYSCALL(0x00e7), // exit_group
|
||||
_SECCOMP_ALLOW_SYSCALL(0x0106), // newfstatat
|
||||
_SECCOMP_ALLOW_SYSCALL(0x00e4), // clock_gettime
|
||||
_SECCOMP_ALLOW_SYSCALL(0x003f), // uname
|
||||
_SECCOMP_ALLOW_SYSCALL(0x0048), // fcntl
|
||||
_SECCOMP_LOG_AND_RETURN_ERRNO(1), // EPERM
|
||||
};
|
||||
|
||||
static const struct sock_fprog kSandboxOnlineProg = {
|
||||
.len = ARRAYLEN(kSandboxOnline),
|
||||
.filter = kSandboxOnline,
|
||||
};
|
||||
|
||||
static const struct sock_fprog kSandboxOfflineProg = {
|
||||
.len = ARRAYLEN(kSandboxOffline),
|
||||
.filter = kSandboxOffline,
|
||||
};
|
||||
|
||||
static int EnableSandbox(void) {
|
||||
const struct sock_fprog *sandbox;
|
||||
switch (sandboxed) {
|
||||
case 0:
|
||||
return 0;
|
||||
case 1:
|
||||
DEBUGF("(stat) applying '%s' sandbox policy", "online");
|
||||
sandbox = &kSandboxOnlineProg;
|
||||
break;
|
||||
default:
|
||||
DEBUGF("(stat) applying '%s' sandbox policy", "offline");
|
||||
sandbox = &kSandboxOfflineProg;
|
||||
break;
|
||||
}
|
||||
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != -1 &&
|
||||
prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, sandbox) != -1) {
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// returns 0 otherwise -1 if worker needs to unwind stack and exit
|
||||
static int HandleConnection(size_t i) {
|
||||
int pid, rc = 0;
|
||||
|
@ -6595,6 +6697,7 @@ static int HandleConnection(size_t i) {
|
|||
if ((client = accept4(servers.p[i].fd, &clientaddr, &clientaddrsize,
|
||||
SOCK_CLOEXEC)) != -1) {
|
||||
startconnection = nowl();
|
||||
VERBOSEF("(srvr) accept %s via %s", DescribeClient(), DescribeServer());
|
||||
messageshandled = 0;
|
||||
if (hasonclientconnection && LuaOnClientConnection()) {
|
||||
close(client);
|
||||
|
@ -6607,11 +6710,19 @@ static int HandleConnection(size_t i) {
|
|||
switch ((pid = fork())) {
|
||||
case 0:
|
||||
meltdown = false;
|
||||
__isworker = true;
|
||||
connectionclose = false;
|
||||
if (!IsTiny()) {
|
||||
if (systrace) __strace = 1;
|
||||
if (funtrace) ftrace_install();
|
||||
if (systrace) {
|
||||
extern unsigned long long __kbirth;
|
||||
__strace = 1;
|
||||
__kbirth = rdtsc();
|
||||
}
|
||||
if (funtrace) {
|
||||
ftrace_install();
|
||||
}
|
||||
}
|
||||
CHECK_NE(-1, EnableSandbox());
|
||||
if (hasonworkerstart) {
|
||||
CallSimpleHook("OnWorkerStart");
|
||||
}
|
||||
|
@ -6632,7 +6743,6 @@ static int HandleConnection(size_t i) {
|
|||
if (!pid && !IsWindows()) {
|
||||
CloseServerFds();
|
||||
}
|
||||
VERBOSEF("(srvr) accept %s via %s", DescribeClient(), DescribeServer());
|
||||
HandleMessages();
|
||||
DEBUGF("(stat) %s closing after %,ldµs", DescribeClient(),
|
||||
(long)((nowl() - startconnection) * 1e6L));
|
||||
|
@ -6809,7 +6919,7 @@ static void Listen(void) {
|
|||
INFOF("(srvr) listen http://%hhu.%hhu.%hhu.%hhu:%d", ip >> 24, ip >> 16,
|
||||
ip >> 8, ip, port);
|
||||
if (printport && !ports.p[j]) {
|
||||
printf("%d\n", port);
|
||||
printf("%d\r\n", port);
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
@ -7004,6 +7114,7 @@ static void GetOpts(int argc, char *argv[]) {
|
|||
bool storeasset = false;
|
||||
while ((opt = getopt(argc, argv, GETOPTS)) != -1) {
|
||||
switch (opt) {
|
||||
CASE('S', ++sandboxed);
|
||||
CASE('v', ++__log_level);
|
||||
CASE('s', --__log_level);
|
||||
CASE('f', funtrace = true);
|
||||
|
@ -7014,7 +7125,6 @@ static void GetOpts(int argc, char *argv[]) {
|
|||
CASE('a', logrusage = true);
|
||||
CASE('u', uniprocess = true);
|
||||
CASE('g', loglatency = true);
|
||||
CASE('S', issandboxed = true);
|
||||
CASE('m', logmessages = true);
|
||||
CASE('l', ProgramAddr(optarg));
|
||||
CASE('H', ProgramHeader(optarg));
|
||||
|
@ -7028,7 +7138,7 @@ static void GetOpts(int argc, char *argv[]) {
|
|||
CASE('c', ProgramCache(ParseInt(optarg)));
|
||||
CASE('r', ProgramRedirectArg(307, optarg));
|
||||
CASE('t', ProgramTimeout(ParseInt(optarg)));
|
||||
CASE('h', PrintUsage(stdout, EXIT_SUCCESS));
|
||||
CASE('h', PrintUsage(1, EXIT_SUCCESS));
|
||||
CASE('M', ProgramMaxPayloadSize(ParseInt(optarg)));
|
||||
#ifndef STATIC
|
||||
CASE('e', LuaEvalCode(optarg));
|
||||
|
@ -7046,7 +7156,7 @@ static void GetOpts(int argc, char *argv[]) {
|
|||
CASE('K', ProgramFile(optarg, ProgramPrivateKey));
|
||||
#endif
|
||||
default:
|
||||
PrintUsage(stderr, EX_USAGE);
|
||||
PrintUsage(2, EX_USAGE);
|
||||
}
|
||||
}
|
||||
// if storing asset(s) is requested, don't need to continue
|
||||
|
@ -7107,6 +7217,8 @@ void RedBean(int argc, char *argv[]) {
|
|||
#ifdef STATIC
|
||||
EventLoop(-1, HEARTBEAT);
|
||||
#else
|
||||
GetResolvConf(); // for effect
|
||||
GetHostsTxt(); // for effect
|
||||
if (!IsWindows() && isatty(0)) {
|
||||
ReplEventLoop();
|
||||
} else {
|
||||
|
@ -7127,7 +7239,6 @@ void RedBean(int argc, char *argv[]) {
|
|||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (!IsTiny()) {
|
||||
setenv("GDB", "", true);
|
||||
ShowCrashReports();
|
||||
}
|
||||
RedBean(argc, argv);
|
||||
|
|
23
tool/net/sandbox.h
Normal file
23
tool/net/sandbox.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
#ifndef COSMOPOLITAN_TOOL_NET_SANDBOX_H_
|
||||
#define COSMOPOLITAN_TOOL_NET_SANDBOX_H_
|
||||
#include "libc/calls/struct/bpf.h"
|
||||
#include "libc/calls/struct/filter.h"
|
||||
#include "libc/calls/struct/seccomp.h"
|
||||
// clang-format off
|
||||
|
||||
#define _SECCOMP_MACHINE(MAGNUM) \
|
||||
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, arch)), \
|
||||
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, AUDIT_ARCH_X86_64, 1, 0), \
|
||||
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS)
|
||||
|
||||
#define _SECCOMP_LOAD_SYSCALL_NR() \
|
||||
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, nr))
|
||||
|
||||
#define _SECCOMP_ALLOW_SYSCALL(MAGNUM) \
|
||||
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, MAGNUM, 0, 1), \
|
||||
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW)
|
||||
|
||||
#define _SECCOMP_LOG_AND_RETURN_ERRNO(MAGNUM) \
|
||||
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | (MAGNUM & SECCOMP_RET_DATA))
|
||||
|
||||
#endif /* COSMOPOLITAN_TOOL_NET_SANDBOX_H_ */
|
Loading…
Add table
Add a link
Reference in a new issue