mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 11:37:35 +00:00
13ee75150c
This change turns symbol table compression back on using Puff, which noticeably reduces the size of programs like redbean and Python. The redbean web server receives some minor API additions for controlling things like SSL in addition to filling gaps in the documentation.
2631 lines
79 KiB
C
2631 lines
79 KiB
C
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
|
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
|
│ Copyright 2021 Justine Alexandra Roberts Tunney │
|
|
│ │
|
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
|
│ any purpose with or without fee is hereby granted, provided that the │
|
|
│ above copyright notice and this permission notice appear in all copies. │
|
|
│ │
|
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
#include "libc/assert.h"
|
|
#include "libc/bits/bits.h"
|
|
#include "libc/calls/calls.h"
|
|
#include "libc/calls/ioctl.h"
|
|
#include "libc/calls/makedev.h"
|
|
#include "libc/calls/sigbits.h"
|
|
#include "libc/calls/strace.internal.h"
|
|
#include "libc/calls/struct/bpf.h"
|
|
#include "libc/calls/struct/dirent.h"
|
|
#include "libc/calls/struct/itimerval.h"
|
|
#include "libc/calls/struct/rusage.h"
|
|
#include "libc/calls/struct/siginfo.h"
|
|
#include "libc/calls/struct/sigset.h"
|
|
#include "libc/calls/struct/stat.h"
|
|
#include "libc/calls/struct/timespec.h"
|
|
#include "libc/calls/struct/timeval.h"
|
|
#include "libc/calls/ucontext.h"
|
|
#include "libc/dns/dns.h"
|
|
#include "libc/errno.h"
|
|
#include "libc/fmt/fmt.h"
|
|
#include "libc/fmt/magnumstrs.internal.h"
|
|
#include "libc/intrin/kprintf.h"
|
|
#include "libc/log/log.h"
|
|
#include "libc/macros.internal.h"
|
|
#include "libc/mem/fmt.h"
|
|
#include "libc/mem/mem.h"
|
|
#include "libc/nt/runtime.h"
|
|
#include "libc/runtime/clktck.h"
|
|
#include "libc/runtime/memtrack.internal.h"
|
|
#include "libc/runtime/runtime.h"
|
|
#include "libc/sock/sock.h"
|
|
#include "libc/sock/syslog.h"
|
|
#include "libc/stdio/append.internal.h"
|
|
#include "libc/stdio/stdio.h"
|
|
#include "libc/str/str.h"
|
|
#include "libc/sysv/consts/af.h"
|
|
#include "libc/sysv/consts/at.h"
|
|
#include "libc/sysv/consts/clock.h"
|
|
#include "libc/sysv/consts/dt.h"
|
|
#include "libc/sysv/consts/f.h"
|
|
#include "libc/sysv/consts/fd.h"
|
|
#include "libc/sysv/consts/ip.h"
|
|
#include "libc/sysv/consts/ipproto.h"
|
|
#include "libc/sysv/consts/itimer.h"
|
|
#include "libc/sysv/consts/limits.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"
|
|
#include "libc/sysv/consts/ok.h"
|
|
#include "libc/sysv/consts/poll.h"
|
|
#include "libc/sysv/consts/rlim.h"
|
|
#include "libc/sysv/consts/rlimit.h"
|
|
#include "libc/sysv/consts/rusage.h"
|
|
#include "libc/sysv/consts/sa.h"
|
|
#include "libc/sysv/consts/shut.h"
|
|
#include "libc/sysv/consts/sig.h"
|
|
#include "libc/sysv/consts/sio.h"
|
|
#include "libc/sysv/consts/so.h"
|
|
#include "libc/sysv/consts/sock.h"
|
|
#include "libc/sysv/consts/sol.h"
|
|
#include "libc/sysv/consts/tcp.h"
|
|
#include "libc/sysv/consts/w.h"
|
|
#include "libc/sysv/errfuns.h"
|
|
#include "libc/time/struct/tm.h"
|
|
#include "libc/time/time.h"
|
|
#include "libc/x/x.h"
|
|
#include "third_party/lua/cosmo.h"
|
|
#include "third_party/lua/lauxlib.h"
|
|
#include "third_party/lua/lgc.h"
|
|
#include "third_party/lua/lua.h"
|
|
#include "third_party/lua/luaconf.h"
|
|
#include "tool/net/luacheck.h"
|
|
|
|
/**
|
|
* @fileoverview UNIX system calls thinly wrapped for Lua
|
|
* @support Linux, Mac, Windows, FreeBSD, NetBSD, OpenBSD
|
|
*/
|
|
|
|
struct UnixErrno {
|
|
int errno_;
|
|
int winerr;
|
|
const char *call;
|
|
};
|
|
|
|
static lua_State *GL;
|
|
|
|
void *LuaRealloc(lua_State *L, void *p, size_t n) {
|
|
void *p2;
|
|
if ((p2 = realloc(p, n))) {
|
|
return p2;
|
|
}
|
|
if (IsLegalSize(n)) {
|
|
WARNF("reacting to malloc() failure by running lua garbage collector...");
|
|
luaC_fullgc(L, 1);
|
|
p2 = realloc(p, n);
|
|
}
|
|
return p2;
|
|
}
|
|
|
|
void *LuaAlloc(lua_State *L, size_t n) {
|
|
return LuaRealloc(L, 0, n);
|
|
}
|
|
|
|
void *LuaAllocOrDie(lua_State *L, size_t n) {
|
|
void *p;
|
|
if ((p = LuaAlloc(L, n))) {
|
|
return p;
|
|
} else {
|
|
luaL_error(L, "out of memory");
|
|
unreachable;
|
|
}
|
|
}
|
|
|
|
static lua_Integer FixLimit(long x) {
|
|
if (0 <= x && x < RLIM_INFINITY) {
|
|
return x;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static void LuaPushSigset(lua_State *L, struct sigset set) {
|
|
struct sigset *sp = lua_newuserdatauv(L, sizeof(*sp), 1);
|
|
luaL_setmetatable(L, "unix.Sigset");
|
|
*sp = set;
|
|
}
|
|
|
|
static void LuaPushStat(lua_State *L, struct stat *st) {
|
|
struct stat *stp = lua_newuserdatauv(L, sizeof(*stp), 1);
|
|
luaL_setmetatable(L, "unix.Stat");
|
|
*stp = *st;
|
|
}
|
|
|
|
static void LuaPushRusage(lua_State *L, struct rusage *set) {
|
|
struct rusage *sp = lua_newuserdatauv(L, sizeof(*sp), 1);
|
|
luaL_setmetatable(L, "unix.Rusage");
|
|
*sp = *set;
|
|
}
|
|
|
|
static void LuaSetIntField(lua_State *L, const char *k, lua_Integer v) {
|
|
lua_pushinteger(L, v);
|
|
lua_setfield(L, -2, k);
|
|
}
|
|
|
|
static dontinline int ReturnInteger(lua_State *L, lua_Integer x) {
|
|
lua_pushinteger(L, x);
|
|
return 1;
|
|
}
|
|
|
|
static dontinline int ReturnBoolean(lua_State *L, int x) {
|
|
lua_pushboolean(L, !!x);
|
|
return 1;
|
|
}
|
|
|
|
static dontinline int ReturnString(lua_State *L, const char *x) {
|
|
lua_pushstring(L, x);
|
|
return 1;
|
|
}
|
|
|
|
static int SysretErrno(lua_State *L, const char *call, int olderr) {
|
|
struct UnixErrno *ep;
|
|
int i, unixerr, winerr;
|
|
unixerr = errno;
|
|
winerr = GetLastError();
|
|
if (!IsTiny() && !(0 < unixerr && unixerr < (!IsWindows() ? 4096 : 65536))) {
|
|
WARNF("errno should not be %d", unixerr);
|
|
}
|
|
lua_pushnil(L);
|
|
ep = lua_newuserdatauv(L, sizeof(*ep), 1);
|
|
luaL_setmetatable(L, "unix.Errno");
|
|
ep->errno_ = unixerr;
|
|
ep->winerr = winerr;
|
|
ep->call = call;
|
|
errno = olderr;
|
|
return 2;
|
|
}
|
|
|
|
int SysretBool(lua_State *L, const char *call, int olderr, int rc) {
|
|
if (!IsTiny() && (rc != 0 && rc != -1)) {
|
|
WARNF("syscall supposed to return 0 / -1 but got %d", rc);
|
|
}
|
|
if (rc != -1) {
|
|
lua_pushboolean(L, true);
|
|
return 1;
|
|
} else {
|
|
return SysretErrno(L, call, olderr);
|
|
}
|
|
}
|
|
|
|
static int SysretInteger(lua_State *L, const char *call, int olderr,
|
|
int64_t rc) {
|
|
if (rc != -1) {
|
|
if (!IsTiny() && olderr != errno) {
|
|
WARNF("errno unexpectedly changed %d → %d", olderr, errno);
|
|
}
|
|
lua_pushinteger(L, rc);
|
|
return 1;
|
|
} else {
|
|
return SysretErrno(L, call, olderr);
|
|
}
|
|
}
|
|
|
|
static void CheckOptvalsize(lua_State *L, uint32_t want, uint32_t got) {
|
|
if (!IsTiny()) {
|
|
if (want == got) return;
|
|
WARNF("getsockopt optvalsize should be %d but was %d", want, got);
|
|
}
|
|
}
|
|
|
|
static void FreeStringList(char **p) {
|
|
int i;
|
|
if (p) {
|
|
for (i = 0; p[i]; ++i) {
|
|
free(p[i]);
|
|
}
|
|
free(p);
|
|
}
|
|
}
|
|
|
|
static char **ConvertLuaArrayToStringList(lua_State *L, int i) {
|
|
int j, n;
|
|
char **p, *s;
|
|
luaL_checktype(L, i, LUA_TTABLE);
|
|
lua_len(L, i);
|
|
n = lua_tointeger(L, -1);
|
|
lua_pop(L, 1);
|
|
if ((p = LuaAlloc(L, (n + 1) * sizeof(*p)))) {
|
|
for (j = 1; j <= n; ++j) {
|
|
lua_geti(L, i, j);
|
|
s = strdup(lua_tostring(L, -1));
|
|
lua_pop(L, 1);
|
|
if (s) {
|
|
p[j - 1] = s;
|
|
} else {
|
|
FreeStringList(p);
|
|
p = 0;
|
|
break;
|
|
}
|
|
}
|
|
p[j - 1] = 0;
|
|
}
|
|
return p;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// System Calls
|
|
|
|
// unix.exit([exitcode:int])
|
|
// └─→ ⊥
|
|
static wontreturn int LuaUnixExit(lua_State *L) {
|
|
_Exit(luaL_optinteger(L, 1, 0));
|
|
}
|
|
|
|
static dontinline int LuaUnixGetid(lua_State *L, int f(void)) {
|
|
return ReturnInteger(L, f());
|
|
}
|
|
|
|
// unix.getpid()
|
|
// └─→ pid:int
|
|
static int LuaUnixGetpid(lua_State *L) {
|
|
return LuaUnixGetid(L, getpid);
|
|
}
|
|
|
|
// unix.getppid()
|
|
// └─→ pid:int
|
|
static int LuaUnixGetppid(lua_State *L) {
|
|
return LuaUnixGetid(L, getppid);
|
|
}
|
|
|
|
// unix.getuid()
|
|
// └─→ uid:int
|
|
static int LuaUnixGetuid(lua_State *L) {
|
|
return LuaUnixGetid(L, getuid);
|
|
}
|
|
|
|
// unix.getgid()
|
|
// └─→ gid:int
|
|
static int LuaUnixGetgid(lua_State *L) {
|
|
return LuaUnixGetid(L, getgid);
|
|
}
|
|
|
|
// unix.geteuid()
|
|
// └─→ uid:int
|
|
static int LuaUnixGeteuid(lua_State *L) {
|
|
return LuaUnixGetid(L, geteuid);
|
|
}
|
|
|
|
// unix.getegid()
|
|
// └─→ gid:int
|
|
static int LuaUnixGetegid(lua_State *L) {
|
|
return LuaUnixGetid(L, getegid);
|
|
}
|
|
|
|
// unix.umask(newmask:int)
|
|
// └─→ oldmask:int
|
|
static int LuaUnixUmask(lua_State *L) {
|
|
return ReturnInteger(L, umask(luaL_checkinteger(L, 1)));
|
|
}
|
|
|
|
// unix.access(path:str, how:int[, flags:int[, dirfd:int]])
|
|
// ├─→ true
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixAccess(lua_State *L) {
|
|
int olderr = errno;
|
|
return SysretBool(
|
|
L, "access", olderr,
|
|
faccessat(luaL_optinteger(L, 3, AT_FDCWD), luaL_checkstring(L, 1),
|
|
luaL_checkinteger(L, 2), luaL_optinteger(L, 4, 0)));
|
|
}
|
|
|
|
// unix.mkdir(path:str[, mode:int[, dirfd:int]])
|
|
// ├─→ true
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixMkdir(lua_State *L) {
|
|
int olderr = errno;
|
|
return SysretBool(
|
|
L, "mkdir", olderr,
|
|
mkdirat(luaL_optinteger(L, 3, AT_FDCWD), luaL_checkstring(L, 1),
|
|
luaL_optinteger(L, 2, 0755)));
|
|
}
|
|
|
|
// unix.makedirs(path:str[, mode:int])
|
|
// ├─→ true
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixMakedirs(lua_State *L) {
|
|
int olderr = errno;
|
|
return SysretBool(
|
|
L, "makedirs", olderr,
|
|
makedirs(luaL_checkstring(L, 1), luaL_optinteger(L, 2, 0755)));
|
|
}
|
|
|
|
// unix.chdir(path:str)
|
|
// ├─→ true
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixChdir(lua_State *L) {
|
|
int olderr = errno;
|
|
return SysretBool(L, "chdir", olderr, chdir(luaL_checkstring(L, 1)));
|
|
}
|
|
|
|
// unix.unlink(path:str[, dirfd:int])
|
|
// ├─→ true
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixUnlink(lua_State *L) {
|
|
int olderr = errno;
|
|
return SysretBool(
|
|
L, "unlink", olderr,
|
|
unlinkat(luaL_optinteger(L, 2, AT_FDCWD), luaL_checkstring(L, 1), 0));
|
|
}
|
|
|
|
// unix.rmdir(path:str[, dirfd:int])
|
|
// ├─→ true
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixRmdir(lua_State *L) {
|
|
int olderr = errno;
|
|
return SysretBool(L, "rmdir", olderr,
|
|
unlinkat(luaL_optinteger(L, 2, AT_FDCWD),
|
|
luaL_checkstring(L, 1), AT_REMOVEDIR));
|
|
}
|
|
|
|
// unix.rename(oldpath:str, newpath:str[, olddirfd:int, newdirfd:int])
|
|
// ├─→ true
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixRename(lua_State *L) {
|
|
int olderr = errno;
|
|
return SysretBool(
|
|
L, "rename", olderr,
|
|
renameat(luaL_optinteger(L, 3, AT_FDCWD), luaL_checkstring(L, 1),
|
|
luaL_optinteger(L, 4, AT_FDCWD), luaL_checkstring(L, 2)));
|
|
}
|
|
|
|
// unix.link(existingpath:str, newpath:str[, flags:int[, olddirfd, newdirfd]])
|
|
// ├─→ true
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixLink(lua_State *L) {
|
|
int olderr = errno;
|
|
return SysretBool(
|
|
L, "link", olderr,
|
|
linkat(luaL_optinteger(L, 4, AT_FDCWD), luaL_checkstring(L, 1),
|
|
luaL_optinteger(L, 5, AT_FDCWD), luaL_checkstring(L, 2),
|
|
luaL_optinteger(L, 3, 0)));
|
|
}
|
|
|
|
// unix.symlink(target:str, linkpath:str[, newdirfd:int])
|
|
// ├─→ true
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixSymlink(lua_State *L) {
|
|
int olderr = errno;
|
|
return SysretBool(
|
|
L, "symlink", olderr,
|
|
symlinkat(luaL_checkstring(L, 1), luaL_optinteger(L, 3, AT_FDCWD),
|
|
luaL_checkstring(L, 2)));
|
|
}
|
|
|
|
// unix.chown(path:str, uid:int, gid:int[, flags:int[, dirfd:int]])
|
|
// ├─→ true
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixChown(lua_State *L) {
|
|
int olderr = errno;
|
|
return SysretBool(
|
|
L, "chown", olderr,
|
|
fchownat(luaL_optinteger(L, 5, AT_FDCWD), luaL_checkstring(L, 1),
|
|
luaL_checkinteger(L, 2), luaL_checkinteger(L, 3),
|
|
luaL_optinteger(L, 4, 0)));
|
|
}
|
|
|
|
// unix.chmod(path:str, mode:int[, flags:int[, dirfd:int]])
|
|
// ├─→ true
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixChmod(lua_State *L) {
|
|
int olderr = errno;
|
|
return SysretBool(
|
|
L, "chmod", olderr,
|
|
fchmodat(luaL_optinteger(L, 4, AT_FDCWD), luaL_checkstring(L, 1),
|
|
luaL_checkinteger(L, 2), luaL_optinteger(L, 3, 0)));
|
|
}
|
|
|
|
// unix.readlink(path:str[, dirfd:int])
|
|
// ├─→ content:str
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixReadlink(lua_State *L) {
|
|
char *buf;
|
|
ssize_t rc;
|
|
const char *path;
|
|
int dirfd, olderr = errno;
|
|
size_t got, bufsiz = 8192;
|
|
path = luaL_checkstring(L, 1);
|
|
dirfd = luaL_optinteger(L, 2, AT_FDCWD);
|
|
buf = LuaAllocOrDie(L, bufsiz);
|
|
if ((rc = readlinkat(dirfd, path, buf, bufsiz)) != -1) {
|
|
got = rc;
|
|
if (got < bufsiz) {
|
|
lua_pushlstring(L, buf, got);
|
|
free(buf);
|
|
return 1;
|
|
} else {
|
|
enametoolong();
|
|
}
|
|
}
|
|
free(buf);
|
|
return SysretErrno(L, "readlink", olderr);
|
|
}
|
|
|
|
// unix.getcwd()
|
|
// ├─→ path:str
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixGetcwd(lua_State *L) {
|
|
char *path;
|
|
int olderr = errno;
|
|
if ((path = getcwd(0, 0))) {
|
|
lua_pushstring(L, path);
|
|
free(path);
|
|
return 1;
|
|
} else {
|
|
return SysretErrno(L, "getcwd", olderr);
|
|
}
|
|
}
|
|
|
|
// unix.fork()
|
|
// ├─┬─→ 0
|
|
// │ └─→ childpid:int
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixFork(lua_State *L) {
|
|
int olderr = errno;
|
|
return SysretInteger(L, "fork", olderr, fork());
|
|
}
|
|
|
|
// unix.environ()
|
|
// └─→ {str,...}
|
|
static int LuaUnixEnviron(lua_State *L) {
|
|
int i;
|
|
char **e;
|
|
lua_newtable(L);
|
|
for (i = 0, e = environ; *e; ++e) {
|
|
lua_pushstring(L, *e);
|
|
lua_rawseti(L, -2, ++i);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
// unix.execve(prog:str[, args:List<*>, env:List<*>])
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixExecve(lua_State *L) {
|
|
int olderr;
|
|
const char *prog;
|
|
char **argv, **envp, **freeme1, **freeme2, *ezargs[2];
|
|
olderr = errno;
|
|
prog = luaL_checkstring(L, 1);
|
|
if (!lua_isnoneornil(L, 2)) {
|
|
if ((argv = ConvertLuaArrayToStringList(L, 2))) {
|
|
freeme1 = argv;
|
|
if (!lua_isnoneornil(L, 3)) {
|
|
if ((envp = ConvertLuaArrayToStringList(L, 3))) {
|
|
freeme2 = envp;
|
|
} else {
|
|
FreeStringList(argv);
|
|
return SysretErrno(L, "execve", olderr);
|
|
}
|
|
} else {
|
|
envp = environ;
|
|
freeme2 = 0;
|
|
}
|
|
} else {
|
|
return SysretErrno(L, "execve", olderr);
|
|
}
|
|
} else {
|
|
ezargs[0] = prog;
|
|
ezargs[1] = 0;
|
|
argv = ezargs;
|
|
envp = environ;
|
|
freeme1 = 0;
|
|
freeme2 = 0;
|
|
}
|
|
execve(prog, argv, envp);
|
|
FreeStringList(freeme1);
|
|
FreeStringList(freeme2);
|
|
return SysretErrno(L, "execve", olderr);
|
|
}
|
|
|
|
// unix.commandv(prog:str)
|
|
// ├─→ path:str
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixCommandv(lua_State *L) {
|
|
int olderr;
|
|
const char *prog;
|
|
char *pathbuf, *resolved;
|
|
olderr = errno;
|
|
prog = luaL_checkstring(L, 1);
|
|
pathbuf = LuaAllocOrDie(L, PATH_MAX);
|
|
if ((resolved = commandv(prog, pathbuf, PATH_MAX))) {
|
|
lua_pushstring(L, resolved);
|
|
free(pathbuf);
|
|
return 1;
|
|
} else {
|
|
free(pathbuf);
|
|
return SysretErrno(L, "commandv", olderr);
|
|
}
|
|
}
|
|
|
|
// unix.realpath(path:str)
|
|
// ├─→ path:str
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixRealpath(lua_State *L) {
|
|
char *resolved;
|
|
int 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 SysretErrno(L, "realpath", 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)
|
|
// ├─→ true
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixChroot(lua_State *L) {
|
|
int olderr = errno;
|
|
return SysretBool(L, "chroot", olderr, chroot(luaL_checkstring(L, 1)));
|
|
}
|
|
|
|
// unix.setrlimit(resource:int, soft:int[, hard:int])
|
|
// ├─→ true
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixSetrlimit(lua_State *L) {
|
|
int olderr = errno;
|
|
int64_t soft = luaL_checkinteger(L, 2);
|
|
return SysretBool(
|
|
L, "setrlimit", olderr,
|
|
setrlimit(luaL_checkinteger(L, 1),
|
|
&(struct rlimit){soft, luaL_optinteger(L, 3, soft)}));
|
|
}
|
|
|
|
// unix.getrlimit(resource:int)
|
|
// ├─→ soft:int, hard:int
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixGetrlimit(lua_State *L) {
|
|
int olderr = errno;
|
|
struct rlimit rlim;
|
|
if (!getrlimit(luaL_checkinteger(L, 1), &rlim)) {
|
|
lua_pushinteger(L, FixLimit(rlim.rlim_cur));
|
|
lua_pushinteger(L, FixLimit(rlim.rlim_max));
|
|
return 2;
|
|
} else {
|
|
return SysretErrno(L, "getrlimit", olderr);
|
|
}
|
|
}
|
|
|
|
// unix.getrusage([who:int])
|
|
// ├─→ unix.Rusage
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixGetrusage(lua_State *L) {
|
|
struct rusage ru;
|
|
int olderr = errno;
|
|
if (!getrusage(luaL_optinteger(L, 1, RUSAGE_SELF), &ru)) {
|
|
LuaPushRusage(L, &ru);
|
|
return 1;
|
|
} else {
|
|
return SysretErrno(L, "getrusage", olderr);
|
|
}
|
|
}
|
|
|
|
// unix.kill(pid:int, sig:int)
|
|
// ├─→ true
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixKill(lua_State *L) {
|
|
int olderr = errno;
|
|
return SysretBool(L, "kill", olderr,
|
|
kill(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2)));
|
|
}
|
|
|
|
// unix.raise(sig:int)
|
|
// ├─→ rc:int
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixRaise(lua_State *L) {
|
|
int olderr = errno;
|
|
return SysretInteger(L, "raise", olderr, raise(luaL_checkinteger(L, 1)));
|
|
}
|
|
|
|
// unix.wait([pid:int, options:int])
|
|
// ├─→ pid:int, wstatus:int, unix.Rusage
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixWait(lua_State *L) {
|
|
struct rusage ru;
|
|
int pid, wstatus, olderr = errno;
|
|
if ((pid = wait4(luaL_optinteger(L, 1, -1), &wstatus,
|
|
luaL_optinteger(L, 2, 0), &ru)) != -1) {
|
|
lua_pushinteger(L, pid);
|
|
lua_pushinteger(L, wstatus);
|
|
LuaPushRusage(L, &ru);
|
|
return 3;
|
|
} else {
|
|
return SysretErrno(L, "wait", olderr);
|
|
}
|
|
}
|
|
|
|
// unix.fcntl(fd:int, cmd:int[, arg:int])
|
|
// ├─→ true, ...
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixFcntl(lua_State *L) {
|
|
int olderr = errno;
|
|
return SysretBool(L, "fcntl", olderr,
|
|
fcntl(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2),
|
|
luaL_optinteger(L, 3, 0)));
|
|
}
|
|
|
|
// unix.dup(oldfd:int[, newfd:int[, flags:int]])
|
|
// ├─→ newfd:int
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixDup(lua_State *L) {
|
|
int rc, oldfd, newfd, flags, olderr;
|
|
olderr = errno;
|
|
oldfd = luaL_checkinteger(L, 1);
|
|
newfd = luaL_optinteger(L, 2, -1);
|
|
flags = luaL_optinteger(L, 3, 0);
|
|
if (newfd == -1) {
|
|
rc = dup(oldfd);
|
|
} else {
|
|
rc = dup3(oldfd, newfd, flags);
|
|
}
|
|
return SysretInteger(L, "dup", olderr, rc);
|
|
}
|
|
|
|
// unix.pipe([flags:int])
|
|
// ├─→ reader:int, writer:int
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixPipe(lua_State *L) {
|
|
int pipefd[2], olderr = errno;
|
|
if (!pipe2(pipefd, luaL_optinteger(L, 1, 0))) {
|
|
lua_pushinteger(L, pipefd[0]);
|
|
lua_pushinteger(L, pipefd[1]);
|
|
return 2;
|
|
} else {
|
|
return SysretErrno(L, "pipe", olderr);
|
|
}
|
|
}
|
|
|
|
// unix.getsid(pid:int)
|
|
// ├─→ sid:int
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixGetsid(lua_State *L) {
|
|
int olderr = errno;
|
|
return SysretInteger(L, "getsid", olderr, getsid(luaL_checkinteger(L, 1)));
|
|
}
|
|
|
|
static dontinline int LuaUnixRc0(lua_State *L, const char *call, int f(void)) {
|
|
int olderr = errno;
|
|
return SysretInteger(L, call, olderr, f());
|
|
}
|
|
|
|
// unix.getpgrp()
|
|
// ├─→ pgid:int
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixGetpgrp(lua_State *L) {
|
|
return LuaUnixRc0(L, "getpgrp", getpgrp);
|
|
}
|
|
|
|
// unix.setpgrp()
|
|
// ├─→ pgid:int
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixSetpgrp(lua_State *L) {
|
|
return LuaUnixRc0(L, "setpgrp", setpgrp);
|
|
}
|
|
|
|
// unix.setsid()
|
|
// ├─→ sid:int
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixSetsid(lua_State *L) {
|
|
return LuaUnixRc0(L, "setsid", setsid);
|
|
}
|
|
|
|
// unix.getpgid(pid:int)
|
|
// ├─→ pgid:int
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixGetpgid(lua_State *L) {
|
|
int olderr = errno;
|
|
return SysretInteger(L, "getpgid", olderr, getpgid(luaL_checkinteger(L, 1)));
|
|
}
|
|
|
|
// unix.setpgid(pid:int, pgid:int)
|
|
// ├─→ true
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixSetpgid(lua_State *L) {
|
|
int olderr = errno;
|
|
return SysretBool(L, "setpgid", olderr,
|
|
setpgid(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2)));
|
|
}
|
|
|
|
static dontinline int LuaUnixSetid(lua_State *L, const char *call, int f(int)) {
|
|
int olderr = errno;
|
|
return SysretBool(L, call, olderr, f(luaL_checkinteger(L, 1)));
|
|
}
|
|
|
|
// unix.setuid(uid:int)
|
|
// ├─→ true
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixSetuid(lua_State *L) {
|
|
return LuaUnixSetid(L, "setuid", setuid);
|
|
}
|
|
|
|
// unix.setgid(gid:int)
|
|
// ├─→ true
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixSetgid(lua_State *L) {
|
|
return LuaUnixSetid(L, "setgid", setgid);
|
|
}
|
|
|
|
static dontinline int LuaUnixSetresid(lua_State *L, const char *call,
|
|
int f(uint32_t, uint32_t, uint32_t)) {
|
|
int olderr = errno;
|
|
return SysretBool(L, call, olderr,
|
|
f(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2),
|
|
luaL_checkinteger(L, 3)));
|
|
}
|
|
|
|
// unix.setresuid(real:int, effective:int, saved:int)
|
|
// ├─→ true
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixSetresuid(lua_State *L) {
|
|
return LuaUnixSetresid(L, "setresuid", setresuid);
|
|
}
|
|
|
|
// unix.setresgid(real:int, effective:int, saved:int)
|
|
// ├─→ true
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixSetresgid(lua_State *L) {
|
|
return LuaUnixSetresid(L, "setresgid", setresgid);
|
|
}
|
|
|
|
// unix.clock_gettime([clock:int])
|
|
// ├─→ seconds:int, nanos:int
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixGettime(lua_State *L) {
|
|
struct timespec ts;
|
|
int rc, olderr = errno;
|
|
if (!clock_gettime(luaL_optinteger(L, 1, CLOCK_REALTIME), &ts)) {
|
|
lua_pushinteger(L, ts.tv_sec);
|
|
lua_pushinteger(L, ts.tv_nsec);
|
|
return 2;
|
|
} else {
|
|
return SysretErrno(L, "clock_gettime", olderr);
|
|
}
|
|
}
|
|
|
|
// unix.nanosleep(seconds:int, nanos:int)
|
|
// ├─→ remseconds:int, remnanos:int
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixNanosleep(lua_State *L) {
|
|
int olderr = errno;
|
|
struct timespec req, rem;
|
|
req.tv_sec = luaL_checkinteger(L, 1);
|
|
req.tv_nsec = luaL_optinteger(L, 2, 0);
|
|
if (!nanosleep(&req, &rem)) {
|
|
lua_pushinteger(L, rem.tv_sec);
|
|
lua_pushinteger(L, rem.tv_nsec);
|
|
return 2;
|
|
} else {
|
|
return SysretErrno(L, "nanosleep", olderr);
|
|
}
|
|
}
|
|
|
|
// unix.sync()
|
|
static int LuaUnixSync(lua_State *L) {
|
|
sync();
|
|
return 0;
|
|
}
|
|
|
|
// unix.fsync(fd:int)
|
|
// ├─→ true
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixFsync(lua_State *L) {
|
|
int olderr = errno;
|
|
return SysretBool(L, "fsync", olderr, fsync(luaL_checkinteger(L, 1)));
|
|
}
|
|
|
|
// unix.fdatasync(fd:int)
|
|
// ├─→ true
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixFdatasync(lua_State *L) {
|
|
int olderr = errno;
|
|
return SysretBool(L, "fdatasync", olderr, fdatasync(luaL_checkinteger(L, 1)));
|
|
}
|
|
|
|
// unix.open(path:str[, flags:int[, mode:int[, dirfd:int]]])
|
|
// ├─→ fd:int
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixOpen(lua_State *L) {
|
|
int olderr = errno;
|
|
return SysretInteger(
|
|
L, "open", olderr,
|
|
openat(luaL_optinteger(L, 4, AT_FDCWD), luaL_checkstring(L, 1),
|
|
luaL_optinteger(L, 2, O_RDONLY), luaL_optinteger(L, 3, 0)));
|
|
}
|
|
|
|
// unix.close(fd:int)
|
|
// ├─→ true
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixClose(lua_State *L) {
|
|
int olderr = errno;
|
|
return SysretBool(L, "close", olderr, close(luaL_checkinteger(L, 1)));
|
|
}
|
|
|
|
// unix.lseek(fd:int, offset:int[, whence:int])
|
|
// ├─→ newposbytes:int
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixLseek(lua_State *L) {
|
|
int olderr = errno;
|
|
return SysretInteger(L, "lseek", olderr,
|
|
lseek(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2),
|
|
luaL_optinteger(L, 3, SEEK_SET)));
|
|
}
|
|
|
|
// unix.truncate(path:str[, length:int])
|
|
// ├─→ true
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixTruncate(lua_State *L) {
|
|
int olderr = errno;
|
|
return SysretBool(L, "truncate", olderr,
|
|
truncate(luaL_checkstring(L, 1), luaL_optinteger(L, 2, 0)));
|
|
}
|
|
|
|
// unix.ftruncate(fd:int[, length:int])
|
|
// ├─→ true
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixFtruncate(lua_State *L) {
|
|
int olderr = errno;
|
|
return SysretBool(
|
|
L, "ftruncate", olderr,
|
|
ftruncate(luaL_checkinteger(L, 1), luaL_optinteger(L, 2, 0)));
|
|
}
|
|
|
|
// unix.read(fd:int[, bufsiz:str[, offset:int]])
|
|
// ├─→ data:str
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixRead(lua_State *L) {
|
|
char *buf;
|
|
size_t got;
|
|
ssize_t rc;
|
|
int fd, olderr;
|
|
lua_Integer bufsiz, offset;
|
|
olderr = errno;
|
|
fd = luaL_checkinteger(L, 1);
|
|
bufsiz = luaL_optinteger(L, 2, BUFSIZ);
|
|
offset = luaL_optinteger(L, 3, -1);
|
|
bufsiz = MIN(bufsiz, 0x7ffff000);
|
|
buf = LuaAllocOrDie(L, bufsiz);
|
|
if (offset == -1) {
|
|
rc = read(fd, buf, bufsiz);
|
|
} else {
|
|
rc = pread(fd, buf, bufsiz, offset);
|
|
}
|
|
if (rc != -1) {
|
|
got = rc;
|
|
lua_pushlstring(L, buf, got);
|
|
free(buf);
|
|
return 1;
|
|
} else {
|
|
free(buf);
|
|
return SysretErrno(L, "read", olderr);
|
|
}
|
|
}
|
|
|
|
// unix.write(fd:int, data:str[, offset:int])
|
|
// ├─→ wrotebytes:int
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixWrite(lua_State *L) {
|
|
ssize_t rc;
|
|
size_t size;
|
|
int fd, olderr;
|
|
const char *data;
|
|
lua_Integer offset;
|
|
olderr = errno;
|
|
fd = luaL_checkinteger(L, 1);
|
|
data = luaL_checklstring(L, 2, &size);
|
|
offset = luaL_optinteger(L, 3, -1);
|
|
if (offset == -1) {
|
|
rc = write(fd, data, size);
|
|
} else {
|
|
rc = pwrite(fd, data, size, offset);
|
|
}
|
|
return SysretInteger(L, "write", olderr, rc);
|
|
}
|
|
|
|
// unix.stat(path:str[, flags:int[, dirfd:int]])
|
|
// ├─→ unix.Stat
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixStat(lua_State *L) {
|
|
struct stat st;
|
|
int olderr = errno;
|
|
if (!fstatat(luaL_optinteger(L, 3, AT_FDCWD), luaL_checkstring(L, 1), &st,
|
|
luaL_optinteger(L, 2, 0))) {
|
|
LuaPushStat(L, &st);
|
|
return 1;
|
|
} else {
|
|
return SysretErrno(L, "stat", olderr);
|
|
}
|
|
}
|
|
|
|
// unix.fstat(fd:int)
|
|
// ├─→ unix.Stat
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixFstat(lua_State *L) {
|
|
struct stat st;
|
|
int olderr = errno;
|
|
if (!fstat(luaL_checkinteger(L, 1), &st)) {
|
|
LuaPushStat(L, &st);
|
|
return 1;
|
|
} else {
|
|
return SysretErrno(L, "fstat", olderr);
|
|
}
|
|
}
|
|
|
|
static bool IsSockoptBool(int l, int x) {
|
|
if (l == SOL_SOCKET) {
|
|
return x == SO_TYPE || //
|
|
x == SO_DEBUG || //
|
|
x == SO_ERROR || //
|
|
x == SO_BROADCAST || //
|
|
x == SO_REUSEADDR || //
|
|
x == SO_REUSEPORT || //
|
|
x == SO_KEEPALIVE || //
|
|
x == SO_ACCEPTCONN || //
|
|
x == SO_DONTROUTE; //
|
|
} else if (l = SOL_TCP) {
|
|
return x == TCP_NODELAY || //
|
|
x == TCP_CORK || //
|
|
x == TCP_QUICKACK || //
|
|
x == TCP_FASTOPEN_CONNECT || //
|
|
x == TCP_DEFER_ACCEPT; //
|
|
} else if (l = SOL_IP) {
|
|
return x == IP_HDRINCL; //
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool IsSockoptInt(int l, int x) {
|
|
if (l == SOL_SOCKET) {
|
|
return x == SO_SNDBUF || //
|
|
x == SO_RCVBUF || //
|
|
x == SO_RCVLOWAT || //
|
|
x == SO_SNDLOWAT; //
|
|
} else if (l = SOL_TCP) {
|
|
return x == TCP_FASTOPEN || //
|
|
x == TCP_KEEPCNT || //
|
|
x == TCP_MAXSEG || //
|
|
x == TCP_SYNCNT || //
|
|
x == TCP_NOTSENT_LOWAT || //
|
|
x == TCP_WINDOW_CLAMP || //
|
|
x == TCP_KEEPIDLE || //
|
|
x == TCP_KEEPINTVL; //
|
|
} else if (l = SOL_IP) {
|
|
return x == IP_TOS || //
|
|
x == IP_MTU || //
|
|
x == IP_TTL; //
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool IsSockoptTimeval(int l, int x) {
|
|
if (l == SOL_SOCKET) {
|
|
return x == SO_RCVTIMEO || //
|
|
x == SO_SNDTIMEO; //
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static int LuaUnixSetsockopt(lua_State *L) {
|
|
void *optval;
|
|
struct linger l;
|
|
uint32_t optsize;
|
|
struct timeval tv;
|
|
int rc, fd, level, optname, optint, olderr = errno;
|
|
fd = luaL_checkinteger(L, 1);
|
|
level = luaL_checkinteger(L, 2);
|
|
optname = luaL_checkinteger(L, 3);
|
|
if (IsSockoptBool(level, optname)) {
|
|
// unix.setsockopt(fd:int, level:int, optname:int, value:bool)
|
|
// ├─→ true
|
|
// └─→ nil, unix.Errno
|
|
optint = lua_toboolean(L, 4);
|
|
optval = &optint;
|
|
optsize = sizeof(optint);
|
|
} else if (IsSockoptInt(level, optname)) {
|
|
// unix.setsockopt(fd:int, level:int, optname:int, value:int)
|
|
// ├─→ true
|
|
// └─→ nil, unix.Errno
|
|
optint = luaL_checkinteger(L, 4);
|
|
optval = &optint;
|
|
optsize = sizeof(optint);
|
|
} else if (IsSockoptTimeval(level, optname)) {
|
|
// unix.setsockopt(fd:int, level:int, optname:int, secs:int[, nanos:int])
|
|
// ├─→ true
|
|
// └─→ nil, unix.Errno
|
|
tv.tv_sec = luaL_checkinteger(L, 4);
|
|
optval = &tv;
|
|
optsize = sizeof(tv);
|
|
} else if (level == SOL_SOCKET && optname == SO_LINGER) {
|
|
// unix.setsockopt(fd:int, level:int, optname:int, secs:int, enabled:bool)
|
|
// ├─→ true
|
|
// └─→ nil, unix.Errno
|
|
l.l_linger = luaL_checkinteger(L, 4);
|
|
l.l_onoff = lua_toboolean(L, 5);
|
|
optval = &l;
|
|
optsize = sizeof(l);
|
|
} else {
|
|
einval();
|
|
return SysretErrno(L, "setsockopt", olderr);
|
|
}
|
|
return SysretBool(L, "setsockopt", olderr,
|
|
setsockopt(fd, level, optname, optval, optsize));
|
|
}
|
|
|
|
static int LuaUnixGetsockopt(lua_State *L) {
|
|
uint32_t size;
|
|
struct linger l;
|
|
struct timeval tv;
|
|
int rc, fd, level, optname, optval, olderr = errno;
|
|
fd = luaL_checkinteger(L, 1);
|
|
level = luaL_checkinteger(L, 2);
|
|
optname = luaL_checkinteger(L, 3);
|
|
if (IsSockoptBool(level, optname) || IsSockoptInt(level, optname)) {
|
|
// unix.getsockopt(fd:int, level:int, optname:int)
|
|
// ├─→ value:int
|
|
// └─→ nil, unix.Errno
|
|
size = sizeof(optval);
|
|
if (getsockopt(fd, level, optname, &optval, &size) != -1) {
|
|
CheckOptvalsize(L, sizeof(optval), size);
|
|
lua_pushinteger(L, optval);
|
|
return 1;
|
|
}
|
|
} else if (IsSockoptTimeval(level, optname)) {
|
|
// unix.getsockopt(fd:int, level:int, optname:int)
|
|
// ├─→ secs:int, nsecs:int
|
|
// └─→ nil, unix.Errno
|
|
size = sizeof(tv);
|
|
if (getsockopt(fd, level, optname, &tv, &size) != -1) {
|
|
CheckOptvalsize(L, sizeof(tv), size);
|
|
lua_pushinteger(L, tv.tv_sec);
|
|
lua_pushinteger(L, tv.tv_usec * 1000);
|
|
return 2;
|
|
}
|
|
} else if (level == SOL_SOCKET && optname == SO_LINGER) {
|
|
// unix.getsockopt(fd:int, unix.SOL_SOCKET, unix.SO_LINGER)
|
|
// ├─→ seconds:int, enabled:bool
|
|
// └─→ nil, unix.Errno
|
|
size = sizeof(l);
|
|
if (getsockopt(fd, level, optname, &l, &size) != -1) {
|
|
CheckOptvalsize(L, sizeof(l), size);
|
|
lua_pushinteger(L, l.l_linger);
|
|
lua_pushboolean(L, !!l.l_onoff);
|
|
return 1;
|
|
}
|
|
} else {
|
|
einval();
|
|
}
|
|
return SysretErrno(L, "getsockopt", olderr);
|
|
}
|
|
|
|
// unix.socket([family:int[, type:int[, protocol:int]]])
|
|
// ├─→ fd:int
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixSocket(lua_State *L) {
|
|
int olderr = errno;
|
|
return SysretInteger(
|
|
L, "socket", olderr,
|
|
socket(luaL_optinteger(L, 1, AF_INET), luaL_optinteger(L, 2, SOCK_STREAM),
|
|
luaL_optinteger(L, 3, IPPROTO_TCP)));
|
|
}
|
|
|
|
// unix.socketpair([family:int[, type:int[, protocol:int]]])
|
|
// ├─→ fd1:int, fd2:int
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixSocketpair(lua_State *L) {
|
|
int sv[2], olderr = errno;
|
|
if (!socketpair(luaL_optinteger(L, 1, AF_UNIX),
|
|
luaL_optinteger(L, 2, SOCK_STREAM), luaL_optinteger(L, 3, 0),
|
|
sv)) {
|
|
lua_pushinteger(L, sv[0]);
|
|
lua_pushinteger(L, sv[1]);
|
|
return 2;
|
|
} else {
|
|
return SysretErrno(L, "socketpair", olderr);
|
|
}
|
|
}
|
|
|
|
// unix.bind(fd:int[, ip:uint32, port:uint16])
|
|
// ├─→ true
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixBind(lua_State *L) {
|
|
int olderr = errno;
|
|
return SysretBool(L, "bind", olderr,
|
|
bind(luaL_checkinteger(L, 1),
|
|
&(struct sockaddr_in){
|
|
.sin_family = AF_INET,
|
|
.sin_addr.s_addr = htonl(luaL_optinteger(L, 2, 0)),
|
|
.sin_port = htons(luaL_optinteger(L, 3, 0)),
|
|
},
|
|
sizeof(struct sockaddr_in)));
|
|
}
|
|
|
|
// unix.connect(fd:int, ip:uint32, port:uint16)
|
|
// ├─→ true
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixConnect(lua_State *L) {
|
|
int olderr = errno;
|
|
return SysretBool(
|
|
L, "connect", olderr,
|
|
connect(luaL_checkinteger(L, 1),
|
|
&(struct sockaddr_in){
|
|
.sin_addr.s_addr = htonl(luaL_checkinteger(L, 2)),
|
|
.sin_port = htons(luaL_checkinteger(L, 3)),
|
|
},
|
|
sizeof(struct sockaddr_in)));
|
|
}
|
|
|
|
// unix.listen(fd:int[, backlog:int])
|
|
// ├─→ true
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixListen(lua_State *L) {
|
|
int olderr = errno;
|
|
return SysretBool(L, "listen", olderr,
|
|
listen(luaL_checkinteger(L, 1), luaL_optinteger(L, 2, 10)));
|
|
}
|
|
|
|
// unix.getsockname(fd:int)
|
|
// ├─→ ip:uint32, port:uint16
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixGetsockname(lua_State *L) {
|
|
int fd, olderr;
|
|
uint32_t addrsize;
|
|
struct sockaddr_in sa;
|
|
olderr = errno;
|
|
addrsize = sizeof(sa);
|
|
fd = luaL_checkinteger(L, 1);
|
|
if (!getsockname(fd, &sa, &addrsize)) {
|
|
lua_pushinteger(L, ntohl(sa.sin_addr.s_addr));
|
|
lua_pushinteger(L, ntohs(sa.sin_port));
|
|
return 2;
|
|
} else {
|
|
return SysretErrno(L, "getsockname", olderr);
|
|
}
|
|
}
|
|
|
|
// unix.getpeername(fd:int)
|
|
// ├─→ ip:uint32, port:uint16
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixGetpeername(lua_State *L) {
|
|
int fd, olderr;
|
|
uint32_t addrsize;
|
|
struct sockaddr_in sa;
|
|
olderr = errno;
|
|
addrsize = sizeof(sa);
|
|
fd = luaL_checkinteger(L, 1);
|
|
if (!getpeername(fd, &sa, &addrsize)) {
|
|
lua_pushinteger(L, ntohl(sa.sin_addr.s_addr));
|
|
lua_pushinteger(L, ntohs(sa.sin_port));
|
|
return 2;
|
|
} else {
|
|
return SysretErrno(L, "getpeername", olderr);
|
|
}
|
|
}
|
|
|
|
// unix.siocgifconf()
|
|
// ├─→ {{name:str,ip:uint32,netmask:uint32}, ...}
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixSiocgifconf(lua_State *L) {
|
|
size_t n;
|
|
char *data;
|
|
int i, fd, olderr;
|
|
struct ifreq *ifr;
|
|
struct ifconf conf;
|
|
olderr = errno;
|
|
data = LuaAllocOrDie(L, (n = 4096));
|
|
if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)) == -1) {
|
|
free(data);
|
|
return SysretErrno(L, "siocgifconf", olderr);
|
|
}
|
|
conf.ifc_buf = data;
|
|
conf.ifc_len = n;
|
|
if (ioctl(fd, SIOCGIFCONF, &conf) == -1) {
|
|
close(fd);
|
|
free(data);
|
|
return SysretErrno(L, "siocgifconf", olderr);
|
|
}
|
|
lua_newtable(L);
|
|
i = 0;
|
|
for (ifr = (struct ifreq *)data; (char *)ifr < data + conf.ifc_len; ++ifr) {
|
|
if (ifr->ifr_addr.sa_family != AF_INET) continue;
|
|
lua_createtable(L, 0, 3);
|
|
lua_pushliteral(L, "name");
|
|
lua_pushstring(L, ifr->ifr_name);
|
|
lua_settable(L, -3);
|
|
lua_pushliteral(L, "ip");
|
|
lua_pushinteger(
|
|
L, ntohl(((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr.s_addr));
|
|
lua_settable(L, -3);
|
|
if (ioctl(fd, SIOCGIFNETMASK, ifr) != -1) {
|
|
lua_pushliteral(L, "netmask");
|
|
lua_pushinteger(
|
|
L, ntohl(((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr.s_addr));
|
|
lua_settable(L, -3);
|
|
}
|
|
lua_rawseti(L, -2, ++i);
|
|
}
|
|
close(fd);
|
|
free(data);
|
|
return 1;
|
|
}
|
|
|
|
// sandbox.pledge([promises:str])
|
|
// ├─→ true
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixPledge(lua_State *L) {
|
|
int olderr = errno;
|
|
return SysretBool(L, "pledge", olderr, pledge(luaL_checkstring(L, 1), 0));
|
|
}
|
|
|
|
// unix.gethostname()
|
|
// ├─→ host:str
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixGethostname(lua_State *L) {
|
|
int rc, olderr;
|
|
char buf[DNS_NAME_MAX + 1];
|
|
olderr = errno;
|
|
if ((rc = gethostname(buf, sizeof(buf))) != -1) {
|
|
if (strnlen(buf, sizeof(buf)) < sizeof(buf)) {
|
|
lua_pushstring(L, buf);
|
|
return 1;
|
|
} else {
|
|
enomem();
|
|
}
|
|
}
|
|
return SysretErrno(L, "gethostname", olderr);
|
|
}
|
|
|
|
// unix.accept(serverfd:int[, flags:int])
|
|
// ├─→ clientfd:int, ip:uint32, port:uint16
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixAccept(lua_State *L) {
|
|
uint32_t addrsize;
|
|
struct sockaddr_in sa;
|
|
int clientfd, serverfd, olderr, flags;
|
|
olderr = errno;
|
|
addrsize = sizeof(sa);
|
|
serverfd = luaL_checkinteger(L, 1);
|
|
flags = luaL_optinteger(L, 2, 0);
|
|
clientfd = accept4(serverfd, &sa, &addrsize, flags);
|
|
if (clientfd != -1) {
|
|
lua_pushinteger(L, clientfd);
|
|
lua_pushinteger(L, ntohl(sa.sin_addr.s_addr));
|
|
lua_pushinteger(L, ntohs(sa.sin_port));
|
|
return 3;
|
|
} else {
|
|
return SysretErrno(L, "accept", olderr);
|
|
}
|
|
}
|
|
|
|
// unix.poll({[fd:int]=events:int, ...}[, timeoutms:int])
|
|
// ├─→ {[fd:int]=revents:int, ...}
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixPoll(lua_State *L) {
|
|
size_t nfds;
|
|
struct pollfd *fds, *fds2;
|
|
int i, fd, events, timeoutms, olderr = errno;
|
|
timeoutms = luaL_optinteger(L, 2, -1);
|
|
luaL_checktype(L, 1, LUA_TTABLE);
|
|
lua_pushnil(L);
|
|
for (fds = 0, nfds = 0; lua_next(L, 1);) {
|
|
if (lua_isinteger(L, -2)) {
|
|
if ((fds2 = LuaRealloc(L, fds, (nfds + 1) * sizeof(*fds)))) {
|
|
fds2[nfds].fd = lua_tointeger(L, -2);
|
|
fds2[nfds].events = lua_tointeger(L, -1);
|
|
fds = fds2;
|
|
++nfds;
|
|
} else {
|
|
free(fds);
|
|
return SysretErrno(L, "poll", olderr);
|
|
}
|
|
} else {
|
|
// ignore non-integer key
|
|
}
|
|
lua_pop(L, 1);
|
|
}
|
|
olderr = errno;
|
|
if ((events = poll(fds, nfds, timeoutms)) != -1) {
|
|
lua_createtable(L, events, 0);
|
|
for (i = 0; i < nfds; ++i) {
|
|
if (fds[i].revents && fds[i].fd >= 0) {
|
|
lua_pushinteger(L, fds[i].revents);
|
|
lua_rawseti(L, -2, fds[i].fd);
|
|
}
|
|
}
|
|
free(fds);
|
|
return 1;
|
|
} else {
|
|
free(fds);
|
|
return SysretErrno(L, "poll", olderr);
|
|
}
|
|
}
|
|
|
|
// unix.recvfrom(fd:int[, bufsiz:int[, flags:int]])
|
|
// ├─→ data:str, ip:uint32, port:uint16
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixRecvfrom(lua_State *L) {
|
|
char *buf;
|
|
size_t got;
|
|
ssize_t rc;
|
|
uint32_t addrsize;
|
|
lua_Integer bufsiz;
|
|
struct sockaddr_in sa;
|
|
int fd, flags, olderr = errno;
|
|
addrsize = sizeof(sa);
|
|
fd = luaL_checkinteger(L, 1);
|
|
bufsiz = luaL_optinteger(L, 2, 1500);
|
|
bufsiz = MIN(bufsiz, 0x7ffff000);
|
|
flags = luaL_optinteger(L, 3, 0);
|
|
buf = LuaAllocOrDie(L, bufsiz);
|
|
rc = recvfrom(fd, buf, bufsiz, flags, &sa, &addrsize);
|
|
if (rc != -1) {
|
|
got = rc;
|
|
lua_pushlstring(L, buf, got);
|
|
lua_pushinteger(L, ntohl(sa.sin_addr.s_addr));
|
|
lua_pushinteger(L, ntohs(sa.sin_port));
|
|
free(buf);
|
|
return 3;
|
|
} else {
|
|
free(buf);
|
|
return SysretErrno(L, "recvfrom", olderr);
|
|
}
|
|
}
|
|
|
|
// unix.recv(fd:int[, bufsiz:int[, flags:int]])
|
|
// ├─→ data:str
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixRecv(lua_State *L) {
|
|
char *buf;
|
|
size_t got;
|
|
ssize_t rc;
|
|
lua_Integer bufsiz;
|
|
int fd, flags, pushed, olderr = errno;
|
|
fd = luaL_checkinteger(L, 1);
|
|
bufsiz = luaL_optinteger(L, 2, 1500);
|
|
bufsiz = MIN(bufsiz, 0x7ffff000);
|
|
flags = luaL_optinteger(L, 3, 0);
|
|
buf = LuaAllocOrDie(L, bufsiz);
|
|
rc = recv(fd, buf, bufsiz, flags);
|
|
if (rc != -1) {
|
|
got = rc;
|
|
lua_pushlstring(L, buf, got);
|
|
free(buf);
|
|
return 1;
|
|
} else {
|
|
free(buf);
|
|
return SysretErrno(L, "recv", olderr);
|
|
}
|
|
}
|
|
|
|
// unix.send(fd:int, data:str[, flags:int])
|
|
// ├─→ sent:int
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixSend(lua_State *L) {
|
|
char *data;
|
|
ssize_t rc;
|
|
size_t sent, size;
|
|
int fd, flags, olderr = errno;
|
|
fd = luaL_checkinteger(L, 1);
|
|
data = luaL_checklstring(L, 2, &size);
|
|
flags = luaL_optinteger(L, 3, 0);
|
|
return SysretInteger(L, "send", olderr, send(fd, data, size, flags));
|
|
}
|
|
|
|
// unix.sendto(fd:int, data:str, ip:uint32, port:uint16[, flags:int])
|
|
// ├─→ sent:int
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixSendto(lua_State *L) {
|
|
char *data;
|
|
size_t sent, size;
|
|
struct sockaddr_in sa;
|
|
int fd, flags, olderr = errno;
|
|
fd = luaL_checkinteger(L, 1);
|
|
data = luaL_checklstring(L, 2, &size);
|
|
bzero(&sa, sizeof(sa));
|
|
sa.sin_addr.s_addr = htonl(luaL_checkinteger(L, 3));
|
|
sa.sin_port = htons(luaL_checkinteger(L, 4));
|
|
flags = luaL_optinteger(L, 5, 0);
|
|
return SysretInteger(L, "sendto", olderr,
|
|
sendto(fd, data, size, flags, &sa, sizeof(sa)));
|
|
}
|
|
|
|
// unix.shutdown(fd:int, how:int)
|
|
// ├─→ true
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixShutdown(lua_State *L) {
|
|
int olderr = errno;
|
|
return SysretBool(L, "shutdown", olderr,
|
|
shutdown(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2)));
|
|
}
|
|
|
|
// unix.sigprocmask(how:int, newmask:unix.Sigset)
|
|
// ├─→ oldmask:unix.Sigset
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixSigprocmask(lua_State *L) {
|
|
uint64_t imask;
|
|
int olderr = errno;
|
|
struct sigset oldmask;
|
|
if (!sigprocmask(luaL_checkinteger(L, 1),
|
|
luaL_checkudata(L, 2, "unix.Sigset"), &oldmask)) {
|
|
LuaPushSigset(L, oldmask);
|
|
return 1;
|
|
} else {
|
|
return SysretErrno(L, "sigprocmask", olderr);
|
|
}
|
|
}
|
|
|
|
static void LuaUnixOnSignal(int sig, siginfo_t *si, ucontext_t *ctx) {
|
|
int type;
|
|
lua_State *L = GL;
|
|
struct sigset ss, os;
|
|
STRACE("LuaUnixOnSignal(%G)", sig);
|
|
lua_getglobal(L, "__signal_handlers");
|
|
type = lua_rawgeti(L, -1, sig);
|
|
lua_remove(L, -2); // pop __signal_handlers
|
|
if (type == LUA_TFUNCTION) {
|
|
lua_pushinteger(L, sig);
|
|
if (lua_pcall(L, 1, 0, 0) != LUA_OK) {
|
|
sigfillset(&ss);
|
|
sigprocmask(SIG_BLOCK, &ss, &os);
|
|
ERRORF("(lua) %s failed: %s", strsignal(sig), lua_tostring(L, -1));
|
|
sigprocmask(SIG_SETMASK, &os, 0);
|
|
lua_pop(L, 1); // pop error
|
|
}
|
|
} else {
|
|
lua_pop(L, 1); // pop handler
|
|
}
|
|
}
|
|
|
|
// unix.sigaction(sig:int[, handler:func|int[, flags:int[, mask:unix.Sigset]]])
|
|
// ├─→ oldhandler:func|int, flags:int, mask:unix.Sigset
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixSigaction(lua_State *L) {
|
|
struct sigset *mask;
|
|
int i, n, sig, olderr = errno;
|
|
struct sigaction sa, oldsa, *saptr = &sa;
|
|
sigemptyset(&sa.sa_mask);
|
|
sig = luaL_checkinteger(L, 1);
|
|
if (!(1 <= sig && sig <= NSIG)) {
|
|
luaL_argerror(L, 1, "signal number invalid");
|
|
unreachable;
|
|
}
|
|
if (lua_isnoneornil(L, 2)) {
|
|
// if handler/flags/mask aren't passed,
|
|
// then we're quering the current state
|
|
saptr = 0;
|
|
} else if (lua_isinteger(L, 2)) {
|
|
// bypass handling signals using lua code if possible
|
|
sa.sa_sigaction = (void *)luaL_checkinteger(L, 2);
|
|
} else if (lua_isfunction(L, 2)) {
|
|
sa.sa_sigaction = LuaUnixOnSignal;
|
|
// lua probably isn't async signal safe, so...
|
|
// let's mask all the lua handlers during handling
|
|
sigaddset(&sa.sa_mask, sig);
|
|
lua_getglobal(L, "__signal_handlers");
|
|
luaL_checktype(L, -1, LUA_TTABLE);
|
|
lua_len(L, -1);
|
|
n = lua_tointeger(L, -1);
|
|
lua_pop(L, 1);
|
|
for (i = 1; i <= MIN(n, NSIG); ++i) {
|
|
if (lua_geti(L, -1, i) == LUA_TFUNCTION) {
|
|
sigaddset(&sa.sa_mask, i);
|
|
}
|
|
lua_pop(L, 1);
|
|
}
|
|
lua_pop(L, 1);
|
|
} else {
|
|
luaL_argerror(L, 2, "sigaction handler not integer or function");
|
|
unreachable;
|
|
}
|
|
sa.sa_flags = luaL_optinteger(L, 3, 0);
|
|
if (!lua_isnoneornil(L, 4)) {
|
|
mask = luaL_checkudata(L, 4, "unix.Sigset");
|
|
sa.sa_mask.__bits[0] |= mask->__bits[0];
|
|
sa.sa_mask.__bits[1] |= mask->__bits[1];
|
|
}
|
|
if (!sigaction(sig, saptr, &oldsa)) {
|
|
lua_getglobal(L, "__signal_handlers");
|
|
// push the old handler result to stack. if the global lua handler
|
|
// table has a real function, then we prefer to return that. if it's
|
|
// absent or a raw integer value, then we're better off returning
|
|
// what the kernel gave us in &oldsa.
|
|
if (lua_rawgeti(L, -1, sig) != LUA_TFUNCTION) {
|
|
lua_pop(L, 1);
|
|
lua_pushinteger(L, (intptr_t)oldsa.sa_handler);
|
|
}
|
|
if (saptr) {
|
|
// update the global lua table
|
|
if (sa.sa_sigaction == LuaUnixOnSignal) {
|
|
lua_pushvalue(L, -3);
|
|
} else {
|
|
lua_pushnil(L);
|
|
}
|
|
lua_rawseti(L, -3, sig);
|
|
}
|
|
// remove the signal handler table from stack
|
|
lua_remove(L, -2);
|
|
// finish pushing the last 2/3 results
|
|
lua_pushinteger(L, oldsa.sa_flags);
|
|
LuaPushSigset(L, oldsa.sa_mask);
|
|
return 3;
|
|
} else {
|
|
return SysretErrno(L, "sigaction", olderr);
|
|
}
|
|
}
|
|
|
|
// unix.sigsuspend([mask:Sigmask])
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixSigsuspend(lua_State *L) {
|
|
int olderr = errno;
|
|
sigsuspend(!lua_isnoneornil(L, 1) ? luaL_checkudata(L, 1, "unix.Sigset") : 0);
|
|
return SysretErrno(L, "sigsuspend", olderr);
|
|
}
|
|
|
|
// unix.setitimer(which[, intervalsec, intns, valuesec, valuens])
|
|
// ├─→ intervalsec:int, intervalns:int, valuesec:int, valuens:int
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixSetitimer(lua_State *L) {
|
|
int which, olderr = errno;
|
|
struct itimerval it, oldit, *itptr;
|
|
which = luaL_checkinteger(L, 1);
|
|
if (!lua_isnoneornil(L, 2)) {
|
|
itptr = ⁢
|
|
it.it_interval.tv_sec = luaL_optinteger(L, 2, 0);
|
|
it.it_interval.tv_usec = luaL_optinteger(L, 3, 0) / 1000;
|
|
it.it_value.tv_sec = luaL_optinteger(L, 4, 0);
|
|
it.it_value.tv_usec = luaL_optinteger(L, 5, 0) / 1000;
|
|
} else {
|
|
itptr = 0;
|
|
}
|
|
if (!setitimer(which, itptr, &oldit)) {
|
|
lua_pushinteger(L, oldit.it_interval.tv_sec);
|
|
lua_pushinteger(L, oldit.it_interval.tv_usec * 1000);
|
|
lua_pushinteger(L, oldit.it_value.tv_sec);
|
|
lua_pushinteger(L, oldit.it_value.tv_usec * 1000);
|
|
return 4;
|
|
} else {
|
|
return SysretErrno(L, "setitimer", olderr);
|
|
}
|
|
}
|
|
|
|
static int LuaUnixStr(lua_State *L, char *f(int)) {
|
|
return ReturnString(L, f(luaL_checkinteger(L, 1)));
|
|
}
|
|
|
|
// unix.strsignal(sig:int)
|
|
// └─→ symbol:str
|
|
static int LuaUnixStrsignal(lua_State *L) {
|
|
return LuaUnixStr(L, strsignal);
|
|
}
|
|
|
|
// unix.WIFEXITED(wstatus)
|
|
// └─→ bool
|
|
static int LuaUnixWifexited(lua_State *L) {
|
|
return ReturnBoolean(L, WIFEXITED(luaL_checkinteger(L, 1)));
|
|
}
|
|
|
|
// unix.WEXITSTATUS(wstatus)
|
|
// └─→ exitcode:uint8
|
|
static int LuaUnixWexitstatus(lua_State *L) {
|
|
return ReturnInteger(L, WEXITSTATUS(luaL_checkinteger(L, 1)));
|
|
}
|
|
|
|
// unix.WIFSIGNALED(wstatus)
|
|
// └─→ bool
|
|
static int LuaUnixWifsignaled(lua_State *L) {
|
|
return ReturnBoolean(L, WIFSIGNALED(luaL_checkinteger(L, 1)));
|
|
}
|
|
|
|
// unix.WTERMSIG(wstatus)
|
|
// └─→ sig:uint8
|
|
static int LuaUnixWtermsig(lua_State *L) {
|
|
return ReturnInteger(L, WTERMSIG(luaL_checkinteger(L, 1)));
|
|
}
|
|
|
|
static dontinline int LuaUnixTime(lua_State *L, const char *call,
|
|
struct tm *f(const time_t *, struct tm *)) {
|
|
int64_t ts;
|
|
struct tm tm;
|
|
int olderr = errno;
|
|
ts = luaL_checkinteger(L, 1);
|
|
if (f(&ts, &tm)) {
|
|
lua_pushinteger(L, tm.tm_year + 1900);
|
|
lua_pushinteger(L, tm.tm_mon + 1); // 1 ≤ mon ≤ 12
|
|
lua_pushinteger(L, tm.tm_mday); // 1 ≤ mday ≤ 31
|
|
lua_pushinteger(L, tm.tm_hour); // 0 ≤ hour ≤ 23
|
|
lua_pushinteger(L, tm.tm_min); // 0 ≤ min ≤ 59
|
|
lua_pushinteger(L, tm.tm_sec); // 0 ≤ sec ≤ 60
|
|
lua_pushinteger(L, tm.tm_gmtoff); // ±93600 seconds
|
|
lua_pushinteger(L, tm.tm_wday); // 0 ≤ wday ≤ 6
|
|
lua_pushinteger(L, tm.tm_yday); // 0 ≤ yday ≤ 365
|
|
lua_pushinteger(L, tm.tm_isdst); // daylight savings
|
|
lua_pushstring(L, tm.tm_zone);
|
|
return 11;
|
|
} else {
|
|
return SysretErrno(L, call, olderr);
|
|
}
|
|
}
|
|
|
|
// unix.gmtime(unixsecs:int)
|
|
// ├─→ year,mon,mday,hour,min,sec,gmtoffsec,wday,yday,dst:int,zone:str
|
|
// └─→ nil,unix.Errno
|
|
static int LuaUnixGmtime(lua_State *L) {
|
|
return LuaUnixTime(L, "gmtime", gmtime_r);
|
|
}
|
|
|
|
// unix.localtime(unixts:int)
|
|
// ├─→ year,mon,mday,hour,min,sec,gmtoffsec,wday,yday,dst:int,zone:str
|
|
// └─→ nil,unix.Errno
|
|
static int LuaUnixLocaltime(lua_State *L) {
|
|
return LuaUnixTime(L, "localtime", localtime_r);
|
|
}
|
|
|
|
// unix.major(rdev:int)
|
|
// └─→ major:int
|
|
static int LuaUnixMajor(lua_State *L) {
|
|
return ReturnInteger(L, major(luaL_checkinteger(L, 1)));
|
|
}
|
|
|
|
// unix.minor(rdev:int)
|
|
// └─→ minor:int
|
|
static int LuaUnixMinor(lua_State *L) {
|
|
return ReturnInteger(L, minor(luaL_checkinteger(L, 1)));
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// unix.Stat object
|
|
|
|
static struct stat *GetUnixStat(lua_State *L) {
|
|
return luaL_checkudata(L, 1, "unix.Stat");
|
|
}
|
|
|
|
// unix.Stat:size()
|
|
// └─→ bytes:int
|
|
static int LuaUnixStatSize(lua_State *L) {
|
|
return ReturnInteger(L, GetUnixStat(L)->st_size);
|
|
}
|
|
|
|
// unix.Stat:mode()
|
|
// └─→ mode:int
|
|
static int LuaUnixStatMode(lua_State *L) {
|
|
return ReturnInteger(L, GetUnixStat(L)->st_mode);
|
|
}
|
|
|
|
// unix.Stat:dev()
|
|
// └─→ dev:int
|
|
static int LuaUnixStatDev(lua_State *L) {
|
|
return ReturnInteger(L, GetUnixStat(L)->st_dev);
|
|
}
|
|
|
|
// unix.Stat:ino()
|
|
// └─→ inodeint
|
|
static int LuaUnixStatIno(lua_State *L) {
|
|
return ReturnInteger(L, GetUnixStat(L)->st_ino);
|
|
}
|
|
|
|
// unix.Stat:nlink()
|
|
// └─→ count:int
|
|
static int LuaUnixStatNlink(lua_State *L) {
|
|
return ReturnInteger(L, GetUnixStat(L)->st_nlink);
|
|
}
|
|
|
|
// unix.Stat:rdev()
|
|
// └─→ rdev:int
|
|
static int LuaUnixStatRdev(lua_State *L) {
|
|
return ReturnInteger(L, GetUnixStat(L)->st_rdev);
|
|
}
|
|
|
|
// unix.Stat:uid()
|
|
// └─→ uid:int
|
|
static int LuaUnixStatUid(lua_State *L) {
|
|
return ReturnInteger(L, GetUnixStat(L)->st_uid);
|
|
}
|
|
|
|
// unix.Stat:gid()
|
|
// └─→ gid:int
|
|
static int LuaUnixStatGid(lua_State *L) {
|
|
return ReturnInteger(L, GetUnixStat(L)->st_gid);
|
|
}
|
|
|
|
// unix.Stat:blocks()
|
|
// └─→ count:int
|
|
static int LuaUnixStatBlocks(lua_State *L) {
|
|
return ReturnInteger(L, GetUnixStat(L)->st_blocks);
|
|
}
|
|
|
|
// unix.Stat:blksize()
|
|
// └─→ bytes:int
|
|
static int LuaUnixStatBlksize(lua_State *L) {
|
|
return ReturnInteger(L, GetUnixStat(L)->st_blksize);
|
|
}
|
|
|
|
static dontinline int ReturnTimespec(lua_State *L, struct timespec *ts) {
|
|
lua_pushinteger(L, ts->tv_sec);
|
|
lua_pushinteger(L, ts->tv_nsec);
|
|
return 2;
|
|
}
|
|
|
|
// unix.Stat:atim()
|
|
// └─→ unixts:int, nanos:int
|
|
static int LuaUnixStatAtim(lua_State *L) {
|
|
return ReturnTimespec(L, &GetUnixStat(L)->st_atim);
|
|
}
|
|
|
|
// unix.Stat:mtim()
|
|
// └─→ unixts:int, nanos:int
|
|
static int LuaUnixStatMtim(lua_State *L) {
|
|
return ReturnTimespec(L, &GetUnixStat(L)->st_mtim);
|
|
}
|
|
|
|
// unix.Stat:ctim()
|
|
// └─→ unixts:int, nanos:int
|
|
static int LuaUnixStatCtim(lua_State *L) {
|
|
return ReturnTimespec(L, &GetUnixStat(L)->st_ctim);
|
|
}
|
|
|
|
// unix.Stat:birthtim()
|
|
// └─→ unixts:int, nanos:int
|
|
static int LuaUnixStatBirthtim(lua_State *L) {
|
|
return ReturnTimespec(L, &GetUnixStat(L)->st_birthtim);
|
|
}
|
|
|
|
// unix.Stat:gen()
|
|
// └─→ gen:int [xnu/bsd]
|
|
static int LuaUnixStatGen(lua_State *L) {
|
|
return ReturnInteger(L, GetUnixStat(L)->st_gen);
|
|
}
|
|
|
|
// unix.Stat:flags()
|
|
// └─→ flags:int [xnu/bsd]
|
|
static int LuaUnixStatFlags(lua_State *L) {
|
|
return ReturnInteger(L, GetUnixStat(L)->st_flags);
|
|
}
|
|
|
|
static int LuaUnixStatToString(lua_State *L) {
|
|
struct stat *st = GetUnixStat(L);
|
|
lua_pushstring(L, "unix.Stat()");
|
|
return 1;
|
|
}
|
|
|
|
static const luaL_Reg kLuaUnixStatMeth[] = {
|
|
{"atim", LuaUnixStatAtim}, //
|
|
{"birthtim", LuaUnixStatBirthtim}, //
|
|
{"blksize", LuaUnixStatBlksize}, //
|
|
{"blocks", LuaUnixStatBlocks}, //
|
|
{"ctim", LuaUnixStatCtim}, //
|
|
{"dev", LuaUnixStatDev}, //
|
|
{"gid", LuaUnixStatGid}, //
|
|
{"ino", LuaUnixStatIno}, //
|
|
{"mode", LuaUnixStatMode}, //
|
|
{"mtim", LuaUnixStatMtim}, //
|
|
{"nlink", LuaUnixStatNlink}, //
|
|
{"rdev", LuaUnixStatRdev}, //
|
|
{"size", LuaUnixStatSize}, //
|
|
{"uid", LuaUnixStatUid}, //
|
|
{"flags", LuaUnixStatFlags}, //
|
|
{"gen", LuaUnixStatGen}, //
|
|
{0}, //
|
|
};
|
|
|
|
static const luaL_Reg kLuaUnixStatMeta[] = {
|
|
{"__tostring", LuaUnixStatToString}, //
|
|
{0}, //
|
|
};
|
|
|
|
static void LuaUnixStatObj(lua_State *L) {
|
|
luaL_newmetatable(L, "unix.Stat");
|
|
luaL_setfuncs(L, kLuaUnixStatMeta, 0);
|
|
luaL_newlibtable(L, kLuaUnixStatMeth);
|
|
luaL_setfuncs(L, kLuaUnixStatMeth, 0);
|
|
lua_setfield(L, -2, "__index");
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// unix.Rusage object
|
|
|
|
static struct rusage *GetUnixRusage(lua_State *L) {
|
|
return luaL_checkudata(L, 1, "unix.Rusage");
|
|
}
|
|
|
|
static dontinline int ReturnTimeval(lua_State *L, struct timeval *tv) {
|
|
lua_pushinteger(L, tv->tv_sec);
|
|
lua_pushinteger(L, tv->tv_usec * 1000);
|
|
return 2;
|
|
}
|
|
|
|
// unix.Rusage:utime()
|
|
// └─→ unixts:int, nanos:int
|
|
static int LuaUnixRusageUtime(lua_State *L) {
|
|
return ReturnTimeval(L, &GetUnixRusage(L)->ru_utime);
|
|
}
|
|
|
|
// unix.Rusage:stime()
|
|
// └─→ unixts:int, nanos:int
|
|
static int LuaUnixRusageStime(lua_State *L) {
|
|
return ReturnTimeval(L, &GetUnixRusage(L)->ru_stime);
|
|
}
|
|
|
|
// unix.Rusage:maxrss()
|
|
// └─→ kilobytes:int
|
|
static int LuaUnixRusageMaxrss(lua_State *L) {
|
|
return ReturnInteger(L, GetUnixRusage(L)->ru_maxrss);
|
|
}
|
|
|
|
// unix.Rusage:ixrss()
|
|
// └─→ integralkilobytes:int
|
|
static int LuaUnixRusageIxrss(lua_State *L) {
|
|
return ReturnInteger(L, GetUnixRusage(L)->ru_ixrss);
|
|
}
|
|
|
|
// unid.Rusage:idrss()
|
|
// └─→ integralkilobytes:int
|
|
static int LuaUnixRusageIdrss(lua_State *L) {
|
|
return ReturnInteger(L, GetUnixRusage(L)->ru_idrss);
|
|
}
|
|
|
|
// unis.Rusage:isrss()
|
|
// └─→ integralkilobytes:int
|
|
static int LuaUnixRusageIsrss(lua_State *L) {
|
|
return ReturnInteger(L, GetUnixRusage(L)->ru_isrss);
|
|
}
|
|
|
|
// unix.Rusage:minflt()
|
|
// └─→ count:int
|
|
static int LuaUnixRusageMinflt(lua_State *L) {
|
|
return ReturnInteger(L, GetUnixRusage(L)->ru_minflt);
|
|
}
|
|
|
|
// unix.Rusage:majflt()
|
|
// └─→ count:int
|
|
static int LuaUnixRusageMajflt(lua_State *L) {
|
|
return ReturnInteger(L, GetUnixRusage(L)->ru_majflt);
|
|
}
|
|
|
|
// unix.Rusage:nswap()
|
|
// └─→ count:int
|
|
static int LuaUnixRusageNswap(lua_State *L) {
|
|
return ReturnInteger(L, GetUnixRusage(L)->ru_nswap);
|
|
}
|
|
|
|
// unix.Rusage:inblock()
|
|
// └─→ count:int
|
|
static int LuaUnixRusageInblock(lua_State *L) {
|
|
return ReturnInteger(L, GetUnixRusage(L)->ru_inblock);
|
|
}
|
|
|
|
// unix.Rusage:oublock()
|
|
// └─→ count:int
|
|
static int LuaUnixRusageOublock(lua_State *L) {
|
|
return ReturnInteger(L, GetUnixRusage(L)->ru_oublock);
|
|
}
|
|
|
|
// unix.Rusage:msgsnd()
|
|
// └─→ count:int
|
|
static int LuaUnixRusageMsgsnd(lua_State *L) {
|
|
return ReturnInteger(L, GetUnixRusage(L)->ru_msgsnd);
|
|
}
|
|
|
|
// unix.Rusage:msgrcv()
|
|
// └─→ count:int
|
|
static int LuaUnixRusageMsgrcv(lua_State *L) {
|
|
return ReturnInteger(L, GetUnixRusage(L)->ru_msgrcv);
|
|
}
|
|
|
|
// unix.Rusage:nsignals()
|
|
// └─→ count:int
|
|
static int LuaUnixRusageNsignals(lua_State *L) {
|
|
return ReturnInteger(L, GetUnixRusage(L)->ru_nsignals);
|
|
}
|
|
|
|
// unix.Rusage:nvcsw()
|
|
// └─→ count:int
|
|
static int LuaUnixRusageNvcsw(lua_State *L) {
|
|
return ReturnInteger(L, GetUnixRusage(L)->ru_nvcsw);
|
|
}
|
|
|
|
// unix.Rusage:nivcsw()
|
|
// └─→ count:int
|
|
static int LuaUnixRusageNivcsw(lua_State *L) {
|
|
return ReturnInteger(L, GetUnixRusage(L)->ru_nivcsw);
|
|
}
|
|
|
|
static int LuaUnixRusageToString(lua_State *L) {
|
|
char *b = 0;
|
|
struct rusage *ru = GetUnixRusage(L);
|
|
appends(&b, "{");
|
|
appendf(&b, "%s={%ld, %ld}", "utime", ru->ru_utime.tv_sec,
|
|
ru->ru_utime.tv_usec * 1000);
|
|
if (ru->ru_stime.tv_sec || ru->ru_stime.tv_usec) {
|
|
appendw(&b, READ16LE(", "));
|
|
appendf(&b, "%s={%ld, %ld}", "stime", ru->ru_stime.tv_sec,
|
|
ru->ru_stime.tv_usec * 1000);
|
|
}
|
|
if (ru->ru_maxrss) appendf(&b, ", %s=%ld", "maxrss", ru->ru_maxrss);
|
|
if (ru->ru_ixrss) appendf(&b, ", %s=%ld", "ixrss", ru->ru_ixrss);
|
|
if (ru->ru_idrss) appendf(&b, ", %s=%ld", "idrss", ru->ru_idrss);
|
|
if (ru->ru_isrss) appendf(&b, ", %s=%ld", "isrss", ru->ru_isrss);
|
|
if (ru->ru_minflt) appendf(&b, ", %s=%ld", "minflt", ru->ru_minflt);
|
|
if (ru->ru_majflt) appendf(&b, ", %s=%ld", "majflt", ru->ru_majflt);
|
|
if (ru->ru_nswap) appendf(&b, ", %s=%ld", "nswap", ru->ru_nswap);
|
|
if (ru->ru_inblock) appendf(&b, ", %s=%ld", "inblock", ru->ru_inblock);
|
|
if (ru->ru_oublock) appendf(&b, ", %s=%ld", "oublock", ru->ru_oublock);
|
|
if (ru->ru_msgsnd) appendf(&b, ", %s=%ld", "msgsnd", ru->ru_msgsnd);
|
|
if (ru->ru_msgrcv) appendf(&b, ", %s=%ld", "msgrcv", ru->ru_msgrcv);
|
|
if (ru->ru_nsignals) appendf(&b, ", %s=%ld", "nsignals", ru->ru_nsignals);
|
|
if (ru->ru_nvcsw) appendf(&b, ", %s=%ld", "nvcsw", ru->ru_nvcsw);
|
|
if (ru->ru_nivcsw) appendf(&b, ", %s=%ld", "nivcsw", ru->ru_nivcsw);
|
|
appendw(&b, '}');
|
|
lua_pushlstring(L, b, appendz(b).i);
|
|
return 1;
|
|
}
|
|
|
|
static const luaL_Reg kLuaUnixRusageMeth[] = {
|
|
{"utime", LuaUnixRusageUtime}, //
|
|
{"stime", LuaUnixRusageStime}, //
|
|
{"maxrss", LuaUnixRusageMaxrss}, //
|
|
{"ixrss", LuaUnixRusageIxrss}, //
|
|
{"idrss", LuaUnixRusageIdrss}, //
|
|
{"isrss", LuaUnixRusageIsrss}, //
|
|
{"minflt", LuaUnixRusageMinflt}, //
|
|
{"majflt", LuaUnixRusageMajflt}, //
|
|
{"nswap", LuaUnixRusageNswap}, //
|
|
{"inblock", LuaUnixRusageInblock}, //
|
|
{"oublock", LuaUnixRusageOublock}, //
|
|
{"msgsnd", LuaUnixRusageMsgsnd}, //
|
|
{"msgrcv", LuaUnixRusageMsgrcv}, //
|
|
{"nsignals", LuaUnixRusageNsignals}, //
|
|
{"nvcsw", LuaUnixRusageNvcsw}, //
|
|
{"nivcsw", LuaUnixRusageNivcsw}, //
|
|
{0}, //
|
|
};
|
|
|
|
static const luaL_Reg kLuaUnixRusageMeta[] = {
|
|
{"__repr", LuaUnixRusageToString}, //
|
|
{"__tostring", LuaUnixRusageToString}, //
|
|
{0}, //
|
|
};
|
|
|
|
static void LuaUnixRusageObj(lua_State *L) {
|
|
luaL_newmetatable(L, "unix.Rusage");
|
|
luaL_setfuncs(L, kLuaUnixRusageMeta, 0);
|
|
luaL_newlibtable(L, kLuaUnixRusageMeth);
|
|
luaL_setfuncs(L, kLuaUnixRusageMeth, 0);
|
|
lua_setfield(L, -2, "__index");
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// unix.Errno object
|
|
|
|
static struct UnixErrno *GetUnixErrno(lua_State *L) {
|
|
return luaL_checkudata(L, 1, "unix.Errno");
|
|
}
|
|
|
|
// unix.Errno:errno()
|
|
// └─→ errno:int
|
|
static int LuaUnixErrnoErrno(lua_State *L) {
|
|
return ReturnInteger(L, GetUnixErrno(L)->errno_);
|
|
}
|
|
|
|
static int LuaUnixErrnoWinerr(lua_State *L) {
|
|
return ReturnInteger(L, GetUnixErrno(L)->winerr);
|
|
}
|
|
|
|
static int LuaUnixErrnoName(lua_State *L) {
|
|
return ReturnString(L, strerrno(GetUnixErrno(L)->errno_));
|
|
}
|
|
|
|
static int LuaUnixErrnoDoc(lua_State *L) {
|
|
return ReturnString(L, strerdoc(GetUnixErrno(L)->errno_));
|
|
}
|
|
|
|
static int LuaUnixErrnoCall(lua_State *L) {
|
|
return ReturnString(L, GetUnixErrno(L)->call);
|
|
}
|
|
|
|
static int LuaUnixErrnoToString(lua_State *L) {
|
|
char msg[256];
|
|
struct UnixErrno *e;
|
|
e = GetUnixErrno(L);
|
|
if (e->call) {
|
|
strerror_wr(e->errno_, e->winerr, msg, sizeof(msg));
|
|
lua_pushfstring(L, "%s() failed: %s", e->call, msg);
|
|
} else {
|
|
lua_pushstring(L, strerrno(e->errno_));
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static const luaL_Reg kLuaUnixErrnoMeth[] = {
|
|
{"errno", LuaUnixErrnoErrno}, //
|
|
{"winerr", LuaUnixErrnoWinerr}, //
|
|
{"name", LuaUnixErrnoName}, //
|
|
{"call", LuaUnixErrnoCall}, //
|
|
{"doc", LuaUnixErrnoDoc}, //
|
|
{0}, //
|
|
};
|
|
|
|
static const luaL_Reg kLuaUnixErrnoMeta[] = {
|
|
{"__tostring", LuaUnixErrnoToString}, //
|
|
{0}, //
|
|
};
|
|
|
|
static void LuaUnixErrnoObj(lua_State *L) {
|
|
luaL_newmetatable(L, "unix.Errno");
|
|
luaL_setfuncs(L, kLuaUnixErrnoMeta, 0);
|
|
luaL_newlibtable(L, kLuaUnixErrnoMeth);
|
|
luaL_setfuncs(L, kLuaUnixErrnoMeth, 0);
|
|
lua_setfield(L, -2, "__index");
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// unix.Sigset object
|
|
|
|
// unix.Sigset(sig:int, ...)
|
|
// └─→ unix.Sigset
|
|
static int LuaUnixSigset(lua_State *L) {
|
|
int i, n;
|
|
lua_Integer sig;
|
|
struct sigset set;
|
|
sigemptyset(&set);
|
|
n = lua_gettop(L);
|
|
for (i = 1; i <= n; ++i) {
|
|
sig = luaL_checkinteger(L, i);
|
|
if (1 <= sig && sig <= NSIG) {
|
|
set.__bits[(sig - 1) >> 6] |= 1ull << ((sig - 1) & 63);
|
|
}
|
|
}
|
|
LuaPushSigset(L, set);
|
|
return 1;
|
|
}
|
|
|
|
// unix.Sigset:add(sig:int)
|
|
static int LuaUnixSigsetAdd(lua_State *L) {
|
|
lua_Integer sig;
|
|
struct sigset *set;
|
|
set = luaL_checkudata(L, 1, "unix.Sigset");
|
|
sig = luaL_checkinteger(L, 2);
|
|
if (1 <= sig && sig <= NSIG) {
|
|
set->__bits[(sig - 1) >> 6] |= 1ull << ((sig - 1) & 63);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// unix.Sigset:remove(sig:int)
|
|
static int LuaUnixSigsetRemove(lua_State *L) {
|
|
lua_Integer sig;
|
|
struct sigset *set;
|
|
set = luaL_checkudata(L, 1, "unix.Sigset");
|
|
sig = luaL_checkinteger(L, 2);
|
|
if (1 <= sig && sig <= NSIG) {
|
|
set->__bits[(sig - 1) >> 6] &= ~(1ull << ((sig - 1) & 63));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// unix.Sigset:fill()
|
|
static int LuaUnixSigsetFill(lua_State *L) {
|
|
struct sigset *set;
|
|
set = luaL_checkudata(L, 1, "unix.Sigset");
|
|
memset(set, -1, sizeof(*set));
|
|
return 0;
|
|
}
|
|
|
|
// unix.Sigset:clear()
|
|
static int LuaUnixSigsetClear(lua_State *L) {
|
|
struct sigset *set;
|
|
set = luaL_checkudata(L, 1, "unix.Sigset");
|
|
bzero(set, sizeof(*set));
|
|
return 0;
|
|
}
|
|
|
|
// unix.Sigset:contains(sig:int)
|
|
// └─→ bool
|
|
static int LuaUnixSigsetContains(lua_State *L) {
|
|
lua_Integer sig;
|
|
struct sigset *set;
|
|
set = luaL_checkudata(L, 1, "unix.Sigset");
|
|
sig = luaL_checkinteger(L, 2);
|
|
return ReturnBoolean(
|
|
L, (1 <= sig && sig <= NSIG)
|
|
? !!(set->__bits[(sig - 1) >> 6] & (1ull << ((sig - 1) & 63)))
|
|
: false);
|
|
}
|
|
|
|
static int LuaUnixSigsetTostring(lua_State *L) {
|
|
char *b = 0;
|
|
int sig, first;
|
|
struct sigset *ss;
|
|
ss = luaL_checkudata(L, 1, "unix.Sigset");
|
|
appends(&b, "unix.Sigset");
|
|
appendw(&b, '(');
|
|
for (sig = first = 1; sig <= NSIG; ++sig) {
|
|
if (sigismember(ss, sig) == 1) {
|
|
if (!first) {
|
|
appendw(&b, READ16LE(", "));
|
|
} else {
|
|
first = 0;
|
|
}
|
|
appendw(&b, READ64LE("unix.\0\0"));
|
|
appends(&b, strsignal(sig));
|
|
}
|
|
}
|
|
appendw(&b, ')');
|
|
lua_pushlstring(L, b, appendz(b).i);
|
|
free(b);
|
|
return 1;
|
|
}
|
|
|
|
static const luaL_Reg kLuaUnixSigsetMeth[] = {
|
|
{"add", LuaUnixSigsetAdd}, //
|
|
{"fill", LuaUnixSigsetFill}, //
|
|
{"clear", LuaUnixSigsetClear}, //
|
|
{"remove", LuaUnixSigsetRemove}, //
|
|
{"contains", LuaUnixSigsetContains}, //
|
|
{0}, //
|
|
};
|
|
|
|
static const luaL_Reg kLuaUnixSigsetMeta[] = {
|
|
{"__tostring", LuaUnixSigsetTostring}, //
|
|
{"__repr", LuaUnixSigsetTostring}, //
|
|
{0}, //
|
|
};
|
|
|
|
static void LuaUnixSigsetObj(lua_State *L) {
|
|
luaL_newmetatable(L, "unix.Sigset");
|
|
luaL_setfuncs(L, kLuaUnixSigsetMeta, 0);
|
|
luaL_newlibtable(L, kLuaUnixSigsetMeth);
|
|
luaL_setfuncs(L, kLuaUnixSigsetMeth, 0);
|
|
lua_setfield(L, -2, "__index");
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// unix.Dir object
|
|
|
|
static DIR **GetUnixDirSelf(lua_State *L) {
|
|
return luaL_checkudata(L, 1, "unix.Dir");
|
|
}
|
|
|
|
static DIR *GetDirOrDie(lua_State *L) {
|
|
DIR **dirp;
|
|
dirp = GetUnixDirSelf(L);
|
|
if (*dirp) return *dirp;
|
|
luaL_argerror(L, 1, "unix.UnixDir is closed");
|
|
unreachable;
|
|
}
|
|
|
|
// unix.Dir:close()
|
|
// ├─→ true
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixDirClose(lua_State *L) {
|
|
DIR **dirp;
|
|
int rc, olderr;
|
|
dirp = GetUnixDirSelf(L);
|
|
if (*dirp) {
|
|
olderr = errno;
|
|
rc = closedir(*dirp);
|
|
*dirp = 0;
|
|
return SysretBool(L, "closedir", olderr, rc);
|
|
} else {
|
|
lua_pushboolean(L, true);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
// unix.Dir:read()
|
|
// ├─→ name:str, kind:int, ino:int, off:int
|
|
// └─→ nil
|
|
static int LuaUnixDirRead(lua_State *L) {
|
|
struct dirent *ent;
|
|
if ((ent = readdir(GetDirOrDie(L)))) {
|
|
lua_pushlstring(L, ent->d_name, strnlen(ent->d_name, sizeof(ent->d_name)));
|
|
lua_pushinteger(L, ent->d_type);
|
|
lua_pushinteger(L, ent->d_ino);
|
|
lua_pushinteger(L, ent->d_off);
|
|
return 4;
|
|
} else {
|
|
// end of directory stream condition
|
|
// we make the assumption getdents() won't fail
|
|
lua_pushnil(L);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
// unix.Dir:fd()
|
|
// ├─→ fd:int
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixDirFd(lua_State *L) {
|
|
int fd, olderr = errno;
|
|
fd = dirfd(GetDirOrDie(L));
|
|
if (fd != -1) {
|
|
lua_pushinteger(L, fd);
|
|
return 1;
|
|
} else {
|
|
return SysretErrno(L, "dirfd", olderr);
|
|
}
|
|
}
|
|
|
|
// unix.Dir:tell()
|
|
// ├─→ off:int
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixDirTell(lua_State *L) {
|
|
int olderr = errno;
|
|
return SysretInteger(L, "telldir", olderr, telldir(GetDirOrDie(L)));
|
|
}
|
|
|
|
// unix.Dir:rewind()
|
|
static int LuaUnixDirRewind(lua_State *L) {
|
|
rewinddir(GetDirOrDie(L));
|
|
return 0;
|
|
}
|
|
|
|
static int ReturnDir(lua_State *L, DIR *dir) {
|
|
DIR **dirp;
|
|
dirp = lua_newuserdatauv(L, sizeof(*dirp), 1);
|
|
luaL_setmetatable(L, "unix.Dir");
|
|
*dirp = dir;
|
|
return 1;
|
|
}
|
|
|
|
// unix.opendir(path:str)
|
|
// ├─→ state:unix.Dir
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixOpendir(lua_State *L) {
|
|
DIR *dir;
|
|
int olderr = errno;
|
|
if ((dir = opendir(luaL_checkstring(L, 1)))) {
|
|
return ReturnDir(L, dir);
|
|
} else {
|
|
return SysretErrno(L, "opendir", olderr);
|
|
}
|
|
}
|
|
|
|
// unix.fdopendir(fd:int)
|
|
// ├─→ next:function, state:unix.Dir
|
|
// └─→ nil, unix.Errno
|
|
static int LuaUnixFdopendir(lua_State *L) {
|
|
DIR *dir;
|
|
int olderr = errno;
|
|
if ((dir = fdopendir(luaL_checkinteger(L, 1)))) {
|
|
return ReturnDir(L, dir);
|
|
} else {
|
|
return SysretErrno(L, "fdopendir", olderr);
|
|
}
|
|
}
|
|
|
|
static const luaL_Reg kLuaUnixDirMeth[] = {
|
|
{"close", LuaUnixDirClose}, //
|
|
{"read", LuaUnixDirRead}, //
|
|
{"fd", LuaUnixDirFd}, //
|
|
{"tell", LuaUnixDirTell}, //
|
|
{"rewind", LuaUnixDirRewind}, //
|
|
{0}, //
|
|
};
|
|
|
|
static const luaL_Reg kLuaUnixDirMeta[] = {
|
|
{"__call", LuaUnixDirRead}, //
|
|
{"__gc", LuaUnixDirClose}, //
|
|
{0}, //
|
|
};
|
|
|
|
static void LuaUnixDirObj(lua_State *L) {
|
|
luaL_newmetatable(L, "unix.Dir");
|
|
luaL_setfuncs(L, kLuaUnixDirMeta, 0);
|
|
luaL_newlibtable(L, kLuaUnixDirMeth);
|
|
luaL_setfuncs(L, kLuaUnixDirMeth, 0);
|
|
lua_setfield(L, -2, "__index");
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// UNIX module
|
|
|
|
static const luaL_Reg kLuaUnix[] = {
|
|
{"Sigset", LuaUnixSigset}, // creates signal bitmask
|
|
{"exit", LuaUnixExit}, // exit w/o atexit
|
|
{"stat", LuaUnixStat}, // get file info from path
|
|
{"fstat", LuaUnixFstat}, // get file info from fd
|
|
{"open", LuaUnixOpen}, // open file fd at lowest slot
|
|
{"close", LuaUnixClose}, // close file or socket
|
|
{"lseek", LuaUnixLseek}, // seek in file
|
|
{"read", LuaUnixRead}, // read from file or socket
|
|
{"write", LuaUnixWrite}, // write to file or socket
|
|
{"access", LuaUnixAccess}, // check my file authorization
|
|
{"fcntl", LuaUnixFcntl}, // manipulate file descriptor
|
|
{"chdir", LuaUnixChdir}, // change directory
|
|
{"chown", LuaUnixChown}, // change owner of file
|
|
{"chmod", LuaUnixChmod}, // change mode of file
|
|
{"readlink", LuaUnixReadlink}, // reads symbolic link
|
|
{"getcwd", LuaUnixGetcwd}, // get current directory
|
|
{"fork", LuaUnixFork}, // make child process via mitosis
|
|
{"execve", LuaUnixExecve}, // replace process with program
|
|
{"environ", LuaUnixEnviron}, // get environment variables
|
|
{"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
|
|
{"fdopendir", LuaUnixFdopendir}, // read directory entry list
|
|
{"rename", LuaUnixRename}, // rename file or directory
|
|
{"link", LuaUnixLink}, // create hard link
|
|
{"unlink", LuaUnixUnlink}, // remove file
|
|
{"symlink", LuaUnixSymlink}, // create symbolic link
|
|
{"sync", LuaUnixSync}, // flushes files and disks
|
|
{"fsync", LuaUnixFsync}, // flush open file
|
|
{"fdatasync", LuaUnixFdatasync}, // flush open file w/o metadata
|
|
{"truncate", LuaUnixTruncate}, // shrink or extend file medium
|
|
{"ftruncate", LuaUnixFtruncate}, // shrink or extend file medium
|
|
{"umask", LuaUnixUmask}, // set default file mask
|
|
{"chroot", LuaUnixChroot}, // change root directory
|
|
{"setrlimit", LuaUnixSetrlimit}, // prevent cpu memory bombs
|
|
{"getrlimit", LuaUnixGetrlimit}, // query resource limits
|
|
{"getrusage", LuaUnixGetrusage}, // query resource usages
|
|
{"getppid", LuaUnixGetppid}, // get parent process id
|
|
{"getpgrp", LuaUnixGetpgrp}, // get process group id
|
|
{"getpgid", LuaUnixGetpgid}, // get process group id of pid
|
|
{"setpgid", LuaUnixSetpgid}, // set process group id for pid
|
|
{"setpgrp", LuaUnixSetpgrp}, // sets process group id
|
|
{"getsid", LuaUnixGetsid}, // get session id of pid
|
|
{"setsid", LuaUnixSetsid}, // create a new session id
|
|
{"getpid", LuaUnixGetpid}, // get id of this process
|
|
{"getuid", LuaUnixGetuid}, // get real user id of process
|
|
{"geteuid", LuaUnixGeteuid}, // get effective user id of process
|
|
{"setuid", LuaUnixSetuid}, // set real user id of process
|
|
{"setresuid", LuaUnixSetresuid}, // sets real/effective/saved uids
|
|
{"getgid", LuaUnixGetgid}, // get real group id of process
|
|
{"getegid", LuaUnixGetegid}, // get effective group id of process
|
|
{"setgid", LuaUnixSetgid}, // set real group id of process
|
|
{"setresgid", LuaUnixSetresgid}, // sets real/effective/saved gids
|
|
{"gethostname", LuaUnixGethostname}, // get hostname of this machine
|
|
{"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
|
|
{"setsockopt", LuaUnixSetsockopt}, // tune socket options
|
|
{"getsockopt", LuaUnixGetsockopt}, // get socket tunings
|
|
{"poll", LuaUnixPoll}, // waits for file descriptor events
|
|
{"bind", LuaUnixBind}, // reserve network interface address
|
|
{"listen", LuaUnixListen}, // begin listening for clients
|
|
{"accept", LuaUnixAccept}, // create client fd for client
|
|
{"connect", LuaUnixConnect}, // connect to remote address
|
|
{"recv", LuaUnixRecv}, // receive tcp from some address
|
|
{"recvfrom", LuaUnixRecvfrom}, // receive udp from some address
|
|
{"send", LuaUnixSend}, // send tcp to some address
|
|
{"sendto", LuaUnixSendto}, // send udp to some address
|
|
{"shutdown", LuaUnixShutdown}, // make socket half empty or full
|
|
{"getpeername", LuaUnixGetpeername}, // get address of remote end
|
|
{"getsockname", LuaUnixGetsockname}, // get address of local end
|
|
{"siocgifconf", LuaUnixSiocgifconf}, // get list of network interfaces
|
|
{"sigaction", LuaUnixSigaction}, // install signal handler
|
|
{"sigprocmask", LuaUnixSigprocmask}, // change signal mask
|
|
{"sigsuspend", LuaUnixSigsuspend}, // wait for signal
|
|
{"setitimer", LuaUnixSetitimer}, // set alarm clock
|
|
{"gmtime", LuaUnixGmtime}, // destructure unix timestamp
|
|
{"pledge", LuaUnixPledge}, // enables syscall sandbox
|
|
{"localtime", LuaUnixLocaltime}, // localize unix timestamp
|
|
{"major", LuaUnixMajor}, // extract device info
|
|
{"minor", LuaUnixMinor}, // extract device info
|
|
{"strsignal", LuaUnixStrsignal}, // turn signal into string
|
|
{"WIFEXITED", LuaUnixWifexited}, // gets exit code from wait status
|
|
{"WEXITSTATUS", LuaUnixWexitstatus}, // gets exit status from wait status
|
|
{"WIFSIGNALED", LuaUnixWifsignaled}, // determines if died due to signal
|
|
{"WTERMSIG", LuaUnixWtermsig}, // gets the signal code
|
|
{0}, //
|
|
};
|
|
|
|
static void LoadMagnums(lua_State *L, struct MagnumStr *ms, const char *pfx) {
|
|
int i;
|
|
char b[64], *p;
|
|
p = stpcpy(b, pfx);
|
|
for (i = 0; ms[i].x != MAGNUM_TERMINATOR; ++i) {
|
|
stpcpy(p, MAGNUM_STRING(ms, i));
|
|
LuaSetIntField(L, b, MAGNUM_NUMBER(ms, i));
|
|
}
|
|
}
|
|
|
|
int LuaUnix(lua_State *L) {
|
|
GL = L;
|
|
luaL_newlib(L, kLuaUnix);
|
|
LuaUnixSigsetObj(L);
|
|
LuaUnixRusageObj(L);
|
|
LuaUnixErrnoObj(L);
|
|
LuaUnixStatObj(L);
|
|
LuaUnixDirObj(L);
|
|
lua_newtable(L);
|
|
lua_setglobal(L, "__signal_handlers");
|
|
|
|
LoadMagnums(L, kErrnoNames, "");
|
|
LoadMagnums(L, kOpenFlags, "O_");
|
|
LoadMagnums(L, kSignalNames, "SIG");
|
|
LoadMagnums(L, kIpOptnames, "IP_");
|
|
LoadMagnums(L, kTcpOptnames, "TCP_");
|
|
LoadMagnums(L, kSockOptnames, "SO_");
|
|
LoadMagnums(L, kClockNames, "CLOCK_");
|
|
|
|
// seek() whence
|
|
LuaSetIntField(L, "SEEK_SET", SEEK_SET);
|
|
LuaSetIntField(L, "SEEK_CUR", SEEK_CUR);
|
|
LuaSetIntField(L, "SEEK_END", SEEK_END);
|
|
|
|
// fcntl() stuff
|
|
LuaSetIntField(L, "F_GETFD", F_GETFD);
|
|
LuaSetIntField(L, "F_SETFD", F_SETFD);
|
|
LuaSetIntField(L, "F_GETFL", F_GETFL);
|
|
LuaSetIntField(L, "F_SETFL", F_SETFL);
|
|
LuaSetIntField(L, "F_UNLCK", F_UNLCK);
|
|
LuaSetIntField(L, "F_RDLCK", F_RDLCK);
|
|
LuaSetIntField(L, "F_WRLCK", F_WRLCK);
|
|
LuaSetIntField(L, "F_SETLK", F_SETLK);
|
|
LuaSetIntField(L, "F_SETLKW", F_SETLKW);
|
|
LuaSetIntField(L, "FD_CLOEXEC", FD_CLOEXEC);
|
|
|
|
// access() mode
|
|
LuaSetIntField(L, "R_OK", R_OK);
|
|
LuaSetIntField(L, "W_OK", W_OK);
|
|
LuaSetIntField(L, "X_OK", X_OK);
|
|
LuaSetIntField(L, "F_OK", F_OK);
|
|
|
|
// rlimit() resources
|
|
LuaSetIntField(L, "RLIMIT_AS", RLIMIT_AS);
|
|
LuaSetIntField(L, "RLIMIT_RSS", RLIMIT_RSS);
|
|
LuaSetIntField(L, "RLIMIT_CPU", RLIMIT_CPU);
|
|
LuaSetIntField(L, "RLIMIT_FSIZE", RLIMIT_FSIZE);
|
|
LuaSetIntField(L, "RLIMIT_NPROC", RLIMIT_NPROC);
|
|
LuaSetIntField(L, "RLIMIT_NOFILE", RLIMIT_NOFILE);
|
|
|
|
// wait() options
|
|
LuaSetIntField(L, "WNOHANG", WNOHANG);
|
|
|
|
// socket() family
|
|
LuaSetIntField(L, "AF_UNSPEC", AF_UNSPEC);
|
|
LuaSetIntField(L, "AF_UNIX", AF_UNIX);
|
|
LuaSetIntField(L, "AF_INET", AF_INET);
|
|
|
|
// socket() type
|
|
LuaSetIntField(L, "SOCK_STREAM", SOCK_STREAM);
|
|
LuaSetIntField(L, "SOCK_DGRAM", SOCK_DGRAM);
|
|
LuaSetIntField(L, "SOCK_RAW", SOCK_RAW);
|
|
LuaSetIntField(L, "SOCK_RDM", SOCK_RDM);
|
|
LuaSetIntField(L, "SOCK_SEQPACKET", SOCK_SEQPACKET);
|
|
LuaSetIntField(L, "SOCK_CLOEXEC", SOCK_CLOEXEC);
|
|
LuaSetIntField(L, "SOCK_NONBLOCK", SOCK_NONBLOCK);
|
|
|
|
// socket() protocol
|
|
LuaSetIntField(L, "IPPROTO_IP", IPPROTO_IP);
|
|
LuaSetIntField(L, "IPPROTO_ICMP", IPPROTO_ICMP);
|
|
LuaSetIntField(L, "IPPROTO_TCP", IPPROTO_TCP);
|
|
LuaSetIntField(L, "IPPROTO_UDP", IPPROTO_UDP);
|
|
LuaSetIntField(L, "IPPROTO_RAW", IPPROTO_RAW);
|
|
|
|
// shutdown() how
|
|
LuaSetIntField(L, "SHUT_RD", SHUT_RD);
|
|
LuaSetIntField(L, "SHUT_WR", SHUT_WR);
|
|
LuaSetIntField(L, "SHUT_RDWR", SHUT_RDWR);
|
|
|
|
// recvfrom() / sendto() flags
|
|
LuaSetIntField(L, "MSG_WAITALL", MSG_WAITALL);
|
|
LuaSetIntField(L, "MSG_DONTROUTE", MSG_DONTROUTE);
|
|
LuaSetIntField(L, "MSG_PEEK", MSG_PEEK);
|
|
LuaSetIntField(L, "MSG_OOB", MSG_OOB);
|
|
LuaSetIntField(L, "MSG_NOSIGNAL", MSG_NOSIGNAL);
|
|
|
|
// readdir() type
|
|
LuaSetIntField(L, "DT_UNKNOWN", DT_UNKNOWN);
|
|
LuaSetIntField(L, "DT_REG", DT_REG);
|
|
LuaSetIntField(L, "DT_DIR", DT_DIR);
|
|
LuaSetIntField(L, "DT_BLK", DT_BLK);
|
|
LuaSetIntField(L, "DT_LNK", DT_LNK);
|
|
LuaSetIntField(L, "DT_CHR", DT_CHR);
|
|
LuaSetIntField(L, "DT_FIFO", DT_FIFO);
|
|
LuaSetIntField(L, "DT_SOCK", DT_SOCK);
|
|
|
|
// poll() flags
|
|
LuaSetIntField(L, "POLLIN", POLLIN);
|
|
LuaSetIntField(L, "POLLPRI", POLLPRI);
|
|
LuaSetIntField(L, "POLLOUT", POLLOUT);
|
|
LuaSetIntField(L, "POLLERR", POLLERR);
|
|
LuaSetIntField(L, "POLLHUP", POLLHUP);
|
|
LuaSetIntField(L, "POLLNVAL", POLLNVAL);
|
|
LuaSetIntField(L, "POLLRDBAND", POLLRDBAND);
|
|
LuaSetIntField(L, "POLLRDNORM", POLLRDNORM);
|
|
LuaSetIntField(L, "POLLWRBAND", POLLWRBAND);
|
|
LuaSetIntField(L, "POLLWRNORM", POLLWRNORM);
|
|
LuaSetIntField(L, "POLLRDHUP", POLLRDHUP);
|
|
|
|
// i/o options
|
|
LuaSetIntField(L, "AT_FDCWD", AT_FDCWD);
|
|
LuaSetIntField(L, "AT_EACCESS", AT_EACCESS);
|
|
LuaSetIntField(L, "AT_SYMLINK_NOFOLLOW", AT_SYMLINK_NOFOLLOW);
|
|
|
|
// sigprocmask() handlers
|
|
LuaSetIntField(L, "SIG_BLOCK", SIG_BLOCK);
|
|
LuaSetIntField(L, "SIG_UNBLOCK", SIG_UNBLOCK);
|
|
LuaSetIntField(L, "SIG_SETMASK", SIG_SETMASK);
|
|
|
|
// sigaction() handlers
|
|
LuaSetIntField(L, "SIG_DFL", (intptr_t)SIG_DFL);
|
|
LuaSetIntField(L, "SIG_IGN", (intptr_t)SIG_IGN);
|
|
|
|
// setitimer() which
|
|
LuaSetIntField(L, "ITIMER_REAL", ITIMER_REAL); // portable
|
|
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);
|
|
|
|
// setsockopt() level
|
|
LuaSetIntField(L, "SOL_IP", SOL_IP);
|
|
LuaSetIntField(L, "SOL_SOCKET", SOL_SOCKET);
|
|
LuaSetIntField(L, "SOL_TCP", SOL_TCP);
|
|
LuaSetIntField(L, "SOL_UDP", SOL_UDP);
|
|
|
|
// sigaction() flags
|
|
LuaSetIntField(L, "SA_RESTART", SA_RESTART);
|
|
LuaSetIntField(L, "SA_RESETHAND", SA_RESETHAND);
|
|
LuaSetIntField(L, "SA_NODEFER", SA_NODEFER);
|
|
LuaSetIntField(L, "SA_NOCLDWAIT", SA_NOCLDWAIT);
|
|
LuaSetIntField(L, "SA_NOCLDSTOP", SA_NOCLDSTOP);
|
|
|
|
// getrusage() who
|
|
LuaSetIntField(L, "RUSAGE_SELF", RUSAGE_SELF);
|
|
LuaSetIntField(L, "RUSAGE_THREAD", RUSAGE_THREAD);
|
|
LuaSetIntField(L, "RUSAGE_CHILDREN", RUSAGE_CHILDREN);
|
|
LuaSetIntField(L, "RUSAGE_BOTH", RUSAGE_BOTH);
|
|
|
|
LuaSetIntField(L, "ARG_MAX", __arg_max());
|
|
LuaSetIntField(L, "BUFSIZ", BUFSIZ);
|
|
LuaSetIntField(L, "CLK_TCK", CLK_TCK);
|
|
LuaSetIntField(L, "NAME_MAX", _NAME_MAX);
|
|
LuaSetIntField(L, "NSIG", _NSIG);
|
|
LuaSetIntField(L, "PATH_MAX", _PATH_MAX);
|
|
LuaSetIntField(L, "PIPE_BUF", PIPE_BUF);
|
|
|
|
return 1;
|
|
}
|