mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-07 15:03:34 +00:00
dc0ea6640e
This change makes further effort towards improving our poll() implementation on the New Technology. The stdin worker didn't work out so well for Python so it's not being used for now. System call tracing with the --strace flag should now be less noisy now on Windows unless you modify the strace.internal.h defines to turn on some optional ones that are most useful for debugging the system call wrappers.
1618 lines
46 KiB
C
1618 lines
46 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/calls/calls.h"
|
|
#include "libc/calls/sigbits.h"
|
|
#include "libc/calls/strace.internal.h"
|
|
#include "libc/calls/struct/dirent.h"
|
|
#include "libc/calls/struct/itimerval.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/ucontext.h"
|
|
#include "libc/errno.h"
|
|
#include "libc/fmt/fmt.h"
|
|
#include "libc/fmt/kerrornames.internal.h"
|
|
#include "libc/intrin/kprintf.h"
|
|
#include "libc/log/check.h"
|
|
#include "libc/log/log.h"
|
|
#include "libc/macros.internal.h"
|
|
#include "libc/mem/mem.h"
|
|
#include "libc/runtime/runtime.h"
|
|
#include "libc/sock/sock.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/ipproto.h"
|
|
#include "libc/sysv/consts/itimer.h"
|
|
#include "libc/sysv/consts/msg.h"
|
|
#include "libc/sysv/consts/o.h"
|
|
#include "libc/sysv/consts/ok.h"
|
|
#include "libc/sysv/consts/sa.h"
|
|
#include "libc/sysv/consts/shut.h"
|
|
#include "libc/sysv/consts/sig.h"
|
|
#include "libc/sysv/consts/sock.h"
|
|
#include "libc/sysv/consts/w.h"
|
|
#include "libc/time/time.h"
|
|
#include "libc/x/x.h"
|
|
#include "third_party/lua/lauxlib.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 UnixStat {
|
|
int refs;
|
|
struct stat st;
|
|
};
|
|
|
|
struct UnixDir {
|
|
int refs;
|
|
DIR *dir;
|
|
};
|
|
|
|
static lua_State *GL;
|
|
|
|
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 ReturnTimespec(lua_State *L, struct timespec *ts) {
|
|
lua_pushinteger(L, ts->tv_sec);
|
|
lua_pushinteger(L, ts->tv_nsec);
|
|
return 2;
|
|
}
|
|
|
|
static int ReturnRc(lua_State *L, int64_t rc, int olderr) {
|
|
lua_pushinteger(L, rc);
|
|
if (rc != -1) return 1;
|
|
lua_pushinteger(L, errno);
|
|
errno = olderr;
|
|
return 2;
|
|
}
|
|
|
|
static char **ConvertLuaArrayToStringList(lua_State *L, int i) {
|
|
int j, n;
|
|
char **p;
|
|
luaL_checktype(L, i, LUA_TTABLE);
|
|
lua_len(L, i);
|
|
n = lua_tointeger(L, -1);
|
|
lua_pop(L, 1);
|
|
p = xcalloc(n + 1, sizeof(*p));
|
|
for (j = 1; j <= n; ++j) {
|
|
lua_geti(L, i, j);
|
|
p[j - 1] = strdup(lua_tostring(L, -1));
|
|
lua_pop(L, 1);
|
|
}
|
|
return p;
|
|
}
|
|
|
|
static void FreeStringList(char **p) {
|
|
int i;
|
|
if (p) {
|
|
for (i = 0; p[i]; ++i) {
|
|
free(p[i]);
|
|
}
|
|
free(p);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// System Calls
|
|
|
|
// unix.exit([exitcode]) → ⊥
|
|
static int LuaUnixExit(lua_State *L) {
|
|
_Exit(luaL_optinteger(L, 1, 0));
|
|
unreachable;
|
|
}
|
|
|
|
// unix.access(path, mode) → rc, errno
|
|
// mode can be: R_OK, W_OK, X_OK, F_OK
|
|
static int LuaUnixAccess(lua_State *L) {
|
|
const char *file;
|
|
int rc, mode, olderr;
|
|
olderr = errno;
|
|
file = luaL_checklstring(L, 1, 0);
|
|
mode = luaL_checkinteger(L, 2);
|
|
rc = access(file, mode);
|
|
return ReturnRc(L, rc, olderr);
|
|
}
|
|
|
|
// unix.mkdir(path, mode) → rc, errno
|
|
// 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);
|
|
rc = mkdir(file, mode);
|
|
return ReturnRc(L, rc, olderr);
|
|
}
|
|
|
|
// unix.chdir(path) → rc, errno
|
|
static int LuaUnixChdir(lua_State *L) {
|
|
int rc, olderr;
|
|
const char *file;
|
|
olderr = errno;
|
|
file = luaL_checklstring(L, 1, 0);
|
|
rc = chdir(file);
|
|
return ReturnRc(L, rc, olderr);
|
|
}
|
|
|
|
// unix.unlink(path) → rc, errno
|
|
static int LuaUnixUnlink(lua_State *L) {
|
|
int rc, olderr;
|
|
const char *file;
|
|
olderr = errno;
|
|
file = luaL_checklstring(L, 1, 0);
|
|
rc = unlink(file);
|
|
return ReturnRc(L, rc, olderr);
|
|
}
|
|
|
|
// unix.rmdir(path) → rc, errno
|
|
static int LuaUnixRmdir(lua_State *L) {
|
|
const char *file;
|
|
int rc, olderr;
|
|
olderr = errno;
|
|
file = luaL_checklstring(L, 1, 0);
|
|
rc = rmdir(file);
|
|
return ReturnRc(L, rc, olderr);
|
|
}
|
|
|
|
// unix.rename(oldpath, newpath) → rc, errno
|
|
static int LuaUnixRename(lua_State *L) {
|
|
const char *oldpath, *newpath;
|
|
int rc, olderr;
|
|
olderr = errno;
|
|
oldpath = luaL_checklstring(L, 1, 0);
|
|
newpath = luaL_checklstring(L, 2, 0);
|
|
rc = rename(oldpath, newpath);
|
|
return ReturnRc(L, rc, olderr);
|
|
}
|
|
|
|
// unix.link(existingpath, newpath) → rc, errno
|
|
static int LuaUnixLink(lua_State *L) {
|
|
const char *existingpath, *newpath;
|
|
int rc, olderr;
|
|
olderr = errno;
|
|
existingpath = luaL_checklstring(L, 1, 0);
|
|
newpath = luaL_checklstring(L, 2, 0);
|
|
rc = link(existingpath, newpath);
|
|
return ReturnRc(L, rc, olderr);
|
|
}
|
|
|
|
// unix.symlink(target, linkpath) → rc, errno
|
|
static int LuaUnixSymlink(lua_State *L) {
|
|
const char *target, *linkpath;
|
|
int rc, olderr;
|
|
olderr = errno;
|
|
target = luaL_checklstring(L, 1, 0);
|
|
linkpath = luaL_checklstring(L, 2, 0);
|
|
rc = symlink(target, linkpath);
|
|
return ReturnRc(L, rc, olderr);
|
|
}
|
|
|
|
// unix.chown(path, uid, gid) → rc, errno
|
|
static int LuaUnixChown(lua_State *L) {
|
|
const char *file;
|
|
int rc, uid, gid, olderr;
|
|
olderr = errno;
|
|
file = luaL_checklstring(L, 1, 0);
|
|
uid = luaL_checkinteger(L, 2);
|
|
gid = luaL_checkinteger(L, 3);
|
|
rc = chown(file, uid, gid);
|
|
return ReturnRc(L, rc, olderr);
|
|
}
|
|
|
|
// unix.chmod(path, mode) → rc, errno
|
|
static int LuaUnixChmod(lua_State *L) {
|
|
const char *file;
|
|
int rc, mode, olderr;
|
|
olderr = errno;
|
|
file = luaL_checklstring(L, 1, 0);
|
|
mode = luaL_checkinteger(L, 2);
|
|
rc = chmod(file, mode);
|
|
return ReturnRc(L, rc, olderr);
|
|
}
|
|
|
|
// unix.getcwd(path, mode) → rc, errno
|
|
static int LuaUnixGetcwd(lua_State *L) {
|
|
char *path;
|
|
path = getcwd(0, 0);
|
|
assert(path);
|
|
lua_pushstring(L, path);
|
|
free(path);
|
|
return 1;
|
|
}
|
|
|
|
// unix.fork() → childpid|0, errno
|
|
static int LuaUnixFork(lua_State *L) {
|
|
int rc, olderr;
|
|
olderr = errno;
|
|
rc = fork();
|
|
return ReturnRc(L, rc, olderr);
|
|
}
|
|
|
|
// unix.execve(prog, argv[, envp]) → errno
|
|
// unix.exit(127)
|
|
//
|
|
// unix = require "unix"
|
|
// prog = unix.commandv("ls")
|
|
// unix.execve(prog, {prog, "-hal", "."})
|
|
// unix.exit(127)
|
|
//
|
|
// prog needs to be absolute, see commandv()
|
|
// envp defaults to environ
|
|
static int LuaUnixExecve(lua_State *L) {
|
|
int olderr;
|
|
const char *prog;
|
|
char **argv, **envp, **freeme;
|
|
olderr = errno;
|
|
prog = luaL_checkstring(L, 1);
|
|
argv = ConvertLuaArrayToStringList(L, 2);
|
|
if (!lua_isnoneornil(L, 3)) {
|
|
envp = ConvertLuaArrayToStringList(L, 3);
|
|
freeme = envp;
|
|
} else {
|
|
envp = environ;
|
|
freeme = 0;
|
|
}
|
|
execve(prog, argv, envp);
|
|
FreeStringList(freeme);
|
|
FreeStringList(argv);
|
|
lua_pushinteger(L, errno);
|
|
errno = olderr;
|
|
return 1;
|
|
}
|
|
|
|
// unix.commandv(prog) → path, errno
|
|
static int LuaUnixCommandv(lua_State *L) {
|
|
int olderr;
|
|
const char *prog;
|
|
char *pathbuf, *resolved;
|
|
olderr = errno;
|
|
pathbuf = xmalloc(PATH_MAX);
|
|
prog = luaL_checkstring(L, 1);
|
|
if ((resolved = commandv(prog, pathbuf))) {
|
|
lua_pushstring(L, resolved);
|
|
lua_pushnil(L);
|
|
} else {
|
|
lua_pushnil(L);
|
|
lua_pushinteger(L, errno);
|
|
errno = olderr;
|
|
}
|
|
free(pathbuf);
|
|
return 2;
|
|
}
|
|
|
|
// unix.getpid() → pid
|
|
static int LuaUnixGetpid(lua_State *L) {
|
|
lua_pushinteger(L, getpid());
|
|
return 1;
|
|
}
|
|
|
|
// unix.getppid() → pid
|
|
static int LuaUnixGetppid(lua_State *L) {
|
|
lua_pushinteger(L, getppid());
|
|
return 1;
|
|
}
|
|
|
|
// unix.kill(pid, sig) → rc, errno
|
|
static int LuaUnixKill(lua_State *L) {
|
|
int rc, pid, sig, olderr;
|
|
olderr = errno;
|
|
pid = luaL_checkinteger(L, 1);
|
|
sig = luaL_checkinteger(L, 2);
|
|
rc = kill(pid, sig);
|
|
return ReturnRc(L, rc, olderr);
|
|
}
|
|
|
|
// unix.raise(sig) → rc, errno
|
|
static int LuaUnixRaise(lua_State *L) {
|
|
int rc, sig, olderr;
|
|
olderr = errno;
|
|
sig = luaL_checkinteger(L, 1);
|
|
rc = raise(sig);
|
|
return ReturnRc(L, rc, olderr);
|
|
}
|
|
|
|
// unix.wait(pid[, options]) → pid, wstatus, nil, errno
|
|
static int LuaUnixWait(lua_State *L) {
|
|
int rc, pid, olderr, options, wstatus;
|
|
olderr = errno;
|
|
pid = luaL_checkinteger(L, 1);
|
|
options = luaL_optinteger(L, 2, 0);
|
|
rc = wait4(pid, &wstatus, options, 0);
|
|
if (rc != -1) {
|
|
lua_pushinteger(L, rc);
|
|
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;
|
|
}
|
|
}
|
|
|
|
// unix.fcntl(fd, cmd[, arg]) → rc, errno
|
|
static int LuaUnixFcntl(lua_State *L) {
|
|
intptr_t arg;
|
|
int rc, fd, cmd, olderr;
|
|
olderr = errno;
|
|
fd = luaL_checkinteger(L, 1);
|
|
cmd = luaL_checkinteger(L, 2);
|
|
arg = luaL_optinteger(L, 3, 0);
|
|
rc = fcntl(fd, cmd, arg);
|
|
return ReturnRc(L, rc, olderr);
|
|
}
|
|
|
|
// unix.dup(oldfd[, newfd[, flags]]) → newfd, errno
|
|
// flags can have O_CLOEXEC
|
|
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 ReturnRc(L, rc, olderr);
|
|
}
|
|
|
|
// unix.pipe([flags]) → reader, writer, errno
|
|
// flags can have O_CLOEXEC
|
|
static int LuaUnixPipe(lua_State *L) {
|
|
int flags, olderr, pipefd[2];
|
|
olderr = errno;
|
|
flags = luaL_optinteger(L, 1, 0);
|
|
if (!pipe2(pipefd, flags)) {
|
|
lua_pushinteger(L, pipefd[0]);
|
|
lua_pushinteger(L, pipefd[1]);
|
|
return 2;
|
|
} else {
|
|
lua_pushnil(L);
|
|
lua_pushnil(L);
|
|
lua_pushinteger(L, errno);
|
|
errno = olderr;
|
|
return 3;
|
|
}
|
|
}
|
|
|
|
// unix.getsid(pid) → sid, errno
|
|
static int LuaUnixGetsid(lua_State *L) {
|
|
int rc, pid, olderr;
|
|
olderr = errno;
|
|
pid = luaL_checkinteger(L, 1);
|
|
rc = getsid(pid);
|
|
return ReturnRc(L, rc, olderr);
|
|
}
|
|
|
|
// unix.getpgrp() → pgid, errno
|
|
static int LuaUnixGetpgrp(lua_State *L) {
|
|
int rc, olderr;
|
|
olderr = errno;
|
|
rc = getpgrp();
|
|
return ReturnRc(L, rc, olderr);
|
|
}
|
|
|
|
// unix.getpgid(pid) → pgid, errno
|
|
static int LuaUnixGetpgid(lua_State *L) {
|
|
int rc, pid, olderr;
|
|
olderr = errno;
|
|
pid = luaL_checkinteger(L, 1);
|
|
rc = getpgid(pid);
|
|
return ReturnRc(L, rc, olderr);
|
|
}
|
|
|
|
// unix.umask(mask) → rc, errno
|
|
static int LuaUnixUmask(lua_State *L) {
|
|
int rc, mask, olderr;
|
|
olderr = errno;
|
|
mask = luaL_checkinteger(L, 1);
|
|
rc = umask(mask);
|
|
return ReturnRc(L, rc, olderr);
|
|
}
|
|
|
|
// unix.setpgid(pid, pgid) → pgid, errno
|
|
static int LuaUnixSetpgid(lua_State *L) {
|
|
int rc, pid, pgid, olderr;
|
|
olderr = errno;
|
|
pid = luaL_checkinteger(L, 1);
|
|
pgid = luaL_checkinteger(L, 2);
|
|
rc = setpgid(pid, pgid);
|
|
return ReturnRc(L, rc, olderr);
|
|
}
|
|
|
|
// unix.setsid() → sid, errno
|
|
static int LuaUnixSetsid(lua_State *L) {
|
|
int rc, olderr;
|
|
olderr = errno;
|
|
rc = setsid();
|
|
return ReturnRc(L, rc, olderr);
|
|
}
|
|
|
|
// unix.getuid() → uid, errno
|
|
static int LuaUnixGetuid(lua_State *L) {
|
|
int rc, olderr;
|
|
olderr = errno;
|
|
rc = getuid();
|
|
return ReturnRc(L, rc, olderr);
|
|
}
|
|
|
|
// unix.getgid() → gid, errno
|
|
static int LuaUnixGetgid(lua_State *L) {
|
|
int rc, olderr;
|
|
olderr = errno;
|
|
rc = getgid();
|
|
return ReturnRc(L, rc, olderr);
|
|
}
|
|
|
|
// unix.gettime([clock]) → seconds, nanos, errno
|
|
static int LuaUnixGettime(lua_State *L) {
|
|
struct timespec ts;
|
|
int rc, clock, olderr;
|
|
olderr = errno;
|
|
clock = luaL_optinteger(L, 1, CLOCK_REALTIME);
|
|
rc = clock_gettime(clock, &ts);
|
|
if (rc != -1) {
|
|
lua_pushinteger(L, ts.tv_sec);
|
|
lua_pushinteger(L, ts.tv_nsec);
|
|
return 2;
|
|
} else {
|
|
lua_pushnil(L);
|
|
lua_pushnil(L);
|
|
lua_pushinteger(L, errno);
|
|
errno = olderr;
|
|
return 3;
|
|
}
|
|
}
|
|
|
|
// unix.nanosleep(seconds, nanos) → remseconds, remnanos, errno
|
|
static int LuaUnixNanosleep(lua_State *L) {
|
|
int rc, olderr;
|
|
struct timespec req, rem;
|
|
olderr = errno;
|
|
req.tv_sec = luaL_checkinteger(L, 1);
|
|
req.tv_nsec = luaL_optinteger(L, 2, 0);
|
|
rc = nanosleep(&req, &rem);
|
|
if (rc != -1) {
|
|
lua_pushinteger(L, rem.tv_sec);
|
|
lua_pushinteger(L, rem.tv_nsec);
|
|
return 2;
|
|
} else {
|
|
lua_pushnil(L);
|
|
lua_pushnil(L);
|
|
lua_pushinteger(L, errno);
|
|
errno = olderr;
|
|
return 3;
|
|
}
|
|
}
|
|
|
|
// unix.sync(fd)
|
|
static int LuaUnixSync(lua_State *L) {
|
|
sync();
|
|
return 0;
|
|
}
|
|
|
|
// unix.fsync(fd) → rc, errno
|
|
static int LuaUnixFsync(lua_State *L) {
|
|
int rc, fd, olderr;
|
|
olderr = errno;
|
|
fd = luaL_checkinteger(L, 1);
|
|
rc = fsync(fd);
|
|
return ReturnRc(L, rc, olderr);
|
|
}
|
|
|
|
// unix.fdatasync(fd) → rc, errno
|
|
static int LuaUnixFdatasync(lua_State *L) {
|
|
int rc, fd, olderr;
|
|
olderr = errno;
|
|
fd = luaL_checkinteger(L, 1);
|
|
rc = fdatasync(fd);
|
|
return ReturnRc(L, rc, olderr);
|
|
}
|
|
|
|
// unix.open(path, flags[, mode]) → fd, errno
|
|
static int LuaUnixOpen(lua_State *L) {
|
|
const char *file;
|
|
int rc, flags, mode, olderr;
|
|
olderr = errno;
|
|
file = luaL_checklstring(L, 1, 0);
|
|
flags = luaL_checkinteger(L, 2);
|
|
mode = luaL_optinteger(L, 3, 0);
|
|
rc = open(file, flags, mode);
|
|
return ReturnRc(L, rc, olderr);
|
|
}
|
|
|
|
// unix.close(fd) → rc, errno
|
|
static int LuaUnixClose(lua_State *L) {
|
|
int rc, fd, olderr;
|
|
olderr = errno;
|
|
fd = luaL_checkinteger(L, 1);
|
|
rc = close(fd);
|
|
return ReturnRc(L, rc, olderr);
|
|
}
|
|
|
|
// unix.seek(fd, offset, whence) → newpos, errno
|
|
// where whence ∈ {SEEK_SET, SEEK_CUR, SEEK_END}
|
|
// whence defaults to SEEK_SET
|
|
static int LuaUnixSeek(lua_State *L) {
|
|
int64_t newpos, offset;
|
|
int fd, olderr, whence;
|
|
olderr = errno;
|
|
fd = luaL_checkinteger(L, 1);
|
|
offset = luaL_checkinteger(L, 2);
|
|
whence = luaL_optinteger(L, 3, SEEK_SET);
|
|
newpos = lseek(fd, offset, whence);
|
|
return ReturnRc(L, newpos, olderr);
|
|
}
|
|
|
|
// unix.truncate(path, length) → rc, errno
|
|
// unix.truncate(fd, length) → rc, errno
|
|
static int LuaUnixTruncate(lua_State *L) {
|
|
int64_t length;
|
|
const char *path;
|
|
int rc, fd, olderr, whence;
|
|
olderr = errno;
|
|
if (lua_isinteger(L, 1)) {
|
|
fd = luaL_checkinteger(L, 1);
|
|
length = luaL_checkinteger(L, 2);
|
|
rc = ftruncate(fd, length);
|
|
} else if (lua_isstring(L, 1)) {
|
|
path = luaL_checkstring(L, 1);
|
|
length = luaL_checkinteger(L, 2);
|
|
rc = truncate(path, length);
|
|
} else {
|
|
luaL_argerror(L, 1, "not integer or string");
|
|
unreachable;
|
|
}
|
|
return ReturnRc(L, rc, olderr);
|
|
}
|
|
|
|
// unix.read(fd[, bufsiz, offset]) → data, errno
|
|
static int LuaUnixRead(lua_State *L) {
|
|
char *buf;
|
|
size_t got;
|
|
int fd, olderr;
|
|
int64_t rc, 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 = xmalloc(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);
|
|
lua_pushnil(L);
|
|
} else {
|
|
lua_pushnil(L);
|
|
lua_pushinteger(L, errno);
|
|
errno = olderr;
|
|
}
|
|
free(buf);
|
|
return 2;
|
|
}
|
|
|
|
// unix.write(fd, data[, offset]) → rc, errno
|
|
static int LuaUnixWrite(lua_State *L) {
|
|
size_t size;
|
|
int fd, olderr;
|
|
const char *data;
|
|
int64_t rc, offset;
|
|
olderr = errno;
|
|
fd = luaL_checkinteger(L, 1);
|
|
data = luaL_checklstring(L, 2, &size);
|
|
offset = luaL_optinteger(L, 3, -1);
|
|
size = MIN(size, 0x7ffff000);
|
|
if (offset == -1) {
|
|
rc = write(fd, data, size);
|
|
} else {
|
|
rc = pwrite(fd, data, size, offset);
|
|
}
|
|
return ReturnRc(L, rc, olderr);
|
|
}
|
|
|
|
// unix.stat(path) → UnixStat*, errno
|
|
// unix.stat(fd) → UnixStat*, errno
|
|
static int LuaUnixStat(lua_State *L) {
|
|
const char *path;
|
|
int rc, fd, olderr;
|
|
struct UnixStat **ust, *st;
|
|
olderr = errno;
|
|
st = xmalloc(sizeof(struct UnixStat));
|
|
if (lua_isinteger(L, 1)) {
|
|
fd = luaL_checkinteger(L, 1);
|
|
rc = fstat(fd, &st->st);
|
|
} else if (lua_isstring(L, 1)) {
|
|
path = luaL_checkstring(L, 1);
|
|
rc = stat(path, &st->st);
|
|
} else {
|
|
luaL_argerror(L, 1, "not integer or string");
|
|
unreachable;
|
|
}
|
|
if (rc == -1) {
|
|
lua_pushnil(L);
|
|
lua_pushinteger(L, errno);
|
|
errno = olderr;
|
|
free(st);
|
|
return 2;
|
|
}
|
|
st->refs = 1;
|
|
ust = lua_newuserdatauv(L, sizeof(st), 1);
|
|
luaL_setmetatable(L, "UnixStat*");
|
|
*ust = st;
|
|
return 1;
|
|
}
|
|
|
|
// unix.opendir(path) → UnixDir*, errno
|
|
// unix.opendir(fd) → UnixDir*, errno
|
|
static int LuaUnixOpendir(lua_State *L) {
|
|
DIR *rc;
|
|
int fd, olderr;
|
|
const char *path;
|
|
struct UnixDir **udir, *dir;
|
|
olderr = errno;
|
|
dir = xcalloc(1, sizeof(struct UnixDir));
|
|
if (lua_isinteger(L, 1)) {
|
|
fd = luaL_checkinteger(L, 1);
|
|
rc = fdopendir(fd);
|
|
} else if (lua_isstring(L, 1)) {
|
|
path = luaL_checkstring(L, 1);
|
|
rc = opendir(path);
|
|
} else {
|
|
luaL_argerror(L, 1, "not integer or string");
|
|
unreachable;
|
|
}
|
|
if (!rc) {
|
|
lua_pushnil(L);
|
|
lua_pushinteger(L, errno);
|
|
errno = olderr;
|
|
free(dir);
|
|
return 2;
|
|
}
|
|
dir->refs = 1;
|
|
dir->dir = rc;
|
|
udir = lua_newuserdatauv(L, sizeof(dir), 1);
|
|
luaL_setmetatable(L, "UnixDir*");
|
|
*udir = dir;
|
|
return 1;
|
|
}
|
|
|
|
// unix.socket([family[, type[, protocol]]]) → fd, errno
|
|
// SOCK_CLOEXEC may be or'd into type
|
|
// family defaults to AF_INET
|
|
// type defaults to SOCK_STREAM
|
|
// protocol defaults to IPPROTO_TCP
|
|
static int LuaUnixSocket(lua_State *L) {
|
|
const char *file;
|
|
int rc, olderr, family, type, protocol;
|
|
olderr = errno;
|
|
family = luaL_optinteger(L, 1, AF_INET);
|
|
type = luaL_optinteger(L, 2, SOCK_STREAM);
|
|
protocol = luaL_optinteger(L, 3, IPPROTO_TCP);
|
|
rc = socket(family, type, protocol);
|
|
return ReturnRc(L, rc, olderr);
|
|
}
|
|
|
|
// unix.socketpair([family[, type[, protocol]]]) → fd1, fd2, errno
|
|
// SOCK_CLOEXEC may be or'd into type
|
|
// family defaults to AF_INET
|
|
// type defaults to SOCK_STREAM
|
|
// protocol defaults to IPPROTO_TCP
|
|
static int LuaUnixSocketpair(lua_State *L) {
|
|
int olderr, family, type, protocol, sv[2];
|
|
olderr = errno;
|
|
family = luaL_optinteger(L, 1, AF_INET);
|
|
type = luaL_optinteger(L, 2, SOCK_STREAM);
|
|
protocol = luaL_optinteger(L, 3, IPPROTO_TCP);
|
|
if (!socketpair(family, type, protocol, sv)) {
|
|
lua_pushinteger(L, sv[0]);
|
|
lua_pushinteger(L, sv[1]);
|
|
return 2;
|
|
} else {
|
|
lua_pushnil(L);
|
|
lua_pushnil(L);
|
|
lua_pushinteger(L, errno);
|
|
errno = olderr;
|
|
return 3;
|
|
}
|
|
}
|
|
|
|
// unix.bind(fd, ip, port) → rc, errno
|
|
// SOCK_CLOEXEC may be or'd into type
|
|
// family defaults to AF_INET
|
|
// type defaults to SOCK_STREAM
|
|
// protocol defaults to IPPROTO_TCP
|
|
static int LuaUnixBind(lua_State *L) {
|
|
int rc, olderr, fd;
|
|
struct sockaddr_in sa;
|
|
bzero(&sa, sizeof(sa));
|
|
olderr = errno;
|
|
fd = luaL_checkinteger(L, 1);
|
|
sa.sin_addr.s_addr = htonl(luaL_checkinteger(L, 2));
|
|
sa.sin_port = htons(luaL_checkinteger(L, 3));
|
|
rc = bind(fd, &sa, sizeof(sa));
|
|
return ReturnRc(L, rc, olderr);
|
|
}
|
|
|
|
// unix.connect(fd, ip, port) → rc, errno
|
|
// SOCK_CLOEXEC may be or'd into type
|
|
// family defaults to AF_INET
|
|
// type defaults to SOCK_STREAM
|
|
// protocol defaults to IPPROTO_TCP
|
|
static int LuaUnixConnect(lua_State *L) {
|
|
int rc, olderr, fd;
|
|
struct sockaddr_in sa;
|
|
bzero(&sa, sizeof(sa));
|
|
olderr = errno;
|
|
fd = luaL_checkinteger(L, 1);
|
|
sa.sin_addr.s_addr = htonl(luaL_checkinteger(L, 2));
|
|
sa.sin_port = htons(luaL_checkinteger(L, 3));
|
|
rc = connect(fd, &sa, sizeof(sa));
|
|
return ReturnRc(L, rc, olderr);
|
|
}
|
|
|
|
// unix.listen(fd[, backlog]) → rc, errno
|
|
static int LuaUnixListen(lua_State *L) {
|
|
int rc, fd, olderr, backlog;
|
|
olderr = errno;
|
|
fd = luaL_checkinteger(L, 1);
|
|
backlog = luaL_optinteger(L, 2, 10);
|
|
rc = listen(fd, backlog);
|
|
return ReturnRc(L, rc, olderr);
|
|
}
|
|
|
|
// unix.getsockname(fd) → ip, port, errno
|
|
static int LuaUnixGetsockname(lua_State *L) {
|
|
int fd, olderr;
|
|
uint32_t addrsize;
|
|
struct sockaddr_in sa;
|
|
olderr = errno;
|
|
bzero(&sa, sizeof(sa));
|
|
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 {
|
|
lua_pushnil(L);
|
|
lua_pushnil(L);
|
|
lua_pushinteger(L, errno);
|
|
errno = olderr;
|
|
return 3;
|
|
}
|
|
}
|
|
|
|
// unix.getpeername(fd) → ip, port, errno
|
|
static int LuaUnixGetpeername(lua_State *L) {
|
|
int fd, olderr;
|
|
uint32_t addrsize;
|
|
struct sockaddr_in sa;
|
|
olderr = errno;
|
|
bzero(&sa, sizeof(sa));
|
|
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 {
|
|
lua_pushnil(L);
|
|
lua_pushnil(L);
|
|
lua_pushinteger(L, errno);
|
|
errno = olderr;
|
|
return 3;
|
|
}
|
|
}
|
|
|
|
// unix.accept(serverfd) → clientfd, ip, port, errno
|
|
static int LuaUnixAccept(lua_State *L) {
|
|
uint32_t addrsize;
|
|
struct sockaddr_in sa;
|
|
int clientfd, serverfd, olderr;
|
|
olderr = errno;
|
|
bzero(&sa, sizeof(sa));
|
|
addrsize = sizeof(sa);
|
|
serverfd = luaL_checkinteger(L, 1);
|
|
clientfd = accept(serverfd, &sa, &addrsize);
|
|
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 {
|
|
lua_pushnil(L);
|
|
lua_pushnil(L);
|
|
lua_pushnil(L);
|
|
lua_pushinteger(L, errno);
|
|
errno = olderr;
|
|
return 4;
|
|
}
|
|
}
|
|
|
|
// unix.recvfrom(fd[, bufsiz[, flags]]) → data, ip, port, errno
|
|
// flags can have MSG_{WAITALL,DONTROUTE,PEEK,OOB}, etc.
|
|
static int LuaUnixRecvfrom(lua_State *L) {
|
|
char *buf;
|
|
size_t got;
|
|
ssize_t rc;
|
|
uint32_t addrsize;
|
|
struct sockaddr_in sa;
|
|
int fd, flags, bufsiz, olderr;
|
|
olderr = errno;
|
|
bzero(&sa, sizeof(sa));
|
|
addrsize = sizeof(sa);
|
|
fd = luaL_checkinteger(L, 1);
|
|
bufsiz = luaL_optinteger(L, 2, 1500);
|
|
flags = luaL_optinteger(L, 3, 0);
|
|
bufsiz = MIN(bufsiz, 0x7ffff000);
|
|
buf = xmalloc(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));
|
|
lua_pushnil(L);
|
|
} else {
|
|
lua_pushnil(L);
|
|
lua_pushnil(L);
|
|
lua_pushnil(L);
|
|
lua_pushinteger(L, errno);
|
|
errno = olderr;
|
|
}
|
|
free(buf);
|
|
return 4;
|
|
}
|
|
|
|
// unix.recv(fd[, bufsiz[, flags]]) → data, errno
|
|
static int LuaUnixRecv(lua_State *L) {
|
|
char *buf;
|
|
size_t got;
|
|
ssize_t rc;
|
|
int fd, flags, bufsiz, olderr;
|
|
olderr = errno;
|
|
fd = luaL_checkinteger(L, 1);
|
|
bufsiz = luaL_optinteger(L, 2, 1500);
|
|
flags = luaL_optinteger(L, 3, 0);
|
|
bufsiz = MIN(bufsiz, 0x7ffff000);
|
|
buf = xmalloc(bufsiz);
|
|
rc = recv(fd, buf, bufsiz, flags);
|
|
if (rc != -1) {
|
|
got = rc;
|
|
lua_pushlstring(L, buf, got);
|
|
lua_pushnil(L);
|
|
} else {
|
|
lua_pushnil(L);
|
|
lua_pushinteger(L, errno);
|
|
errno = olderr;
|
|
}
|
|
free(buf);
|
|
return 4;
|
|
}
|
|
|
|
// unix.send(fd, data[, flags]) → sent, errno
|
|
static int LuaUnixSend(lua_State *L) {
|
|
char *data;
|
|
ssize_t rc;
|
|
size_t sent, size;
|
|
int fd, flags, bufsiz, olderr;
|
|
olderr = errno;
|
|
fd = luaL_checkinteger(L, 1);
|
|
data = luaL_checklstring(L, 2, &size);
|
|
size = MIN(size, 0x7ffff000);
|
|
flags = luaL_optinteger(L, 5, 0);
|
|
rc = send(fd, data, size, flags);
|
|
return ReturnRc(L, rc, olderr);
|
|
}
|
|
|
|
// unix.sendto(fd, data, ip, port[, flags]) → sent, errno
|
|
// flags MSG_OOB, MSG_DONTROUTE, MSG_NOSIGNAL, etc.
|
|
static int LuaUnixSendto(lua_State *L) {
|
|
char *data;
|
|
ssize_t rc;
|
|
size_t sent, size;
|
|
struct sockaddr_in sa;
|
|
int fd, flags, bufsiz, olderr;
|
|
olderr = errno;
|
|
bzero(&sa, sizeof(sa));
|
|
fd = luaL_checkinteger(L, 1);
|
|
data = luaL_checklstring(L, 2, &size);
|
|
size = MIN(size, 0x7ffff000);
|
|
sa.sin_addr.s_addr = htonl(luaL_checkinteger(L, 3));
|
|
sa.sin_port = htons(luaL_checkinteger(L, 4));
|
|
flags = luaL_optinteger(L, 5, 0);
|
|
rc = sendto(fd, data, size, flags, &sa, sizeof(sa));
|
|
return ReturnRc(L, rc, olderr);
|
|
}
|
|
|
|
// unix.shutdown(fd, how) → rc, errno
|
|
// how can be SHUT_RD, SHUT_WR, or SHUT_RDWR
|
|
static int LuaUnixShutdown(lua_State *L) {
|
|
int rc, fd, how, olderr;
|
|
olderr = errno;
|
|
fd = luaL_checkinteger(L, 1);
|
|
how = luaL_checkinteger(L, 2);
|
|
rc = shutdown(fd, how);
|
|
return ReturnRc(L, rc, olderr);
|
|
}
|
|
|
|
// unix.sigprocmask(how[, mask]) → oldmask, errno
|
|
// how can be SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK
|
|
static int LuaUnixSigprocmask(lua_State *L) {
|
|
uint64_t imask;
|
|
int how, olderr;
|
|
sigset_t mask, oldmask, *maskptr;
|
|
olderr = errno;
|
|
how = luaL_checkinteger(L, 1);
|
|
if (lua_isnoneornil(L, 2)) {
|
|
// if mask isn't passed then we're querying
|
|
maskptr = 0;
|
|
} else {
|
|
maskptr = &mask;
|
|
imask = luaL_checkinteger(L, 2);
|
|
bzero(&mask, sizeof(mask));
|
|
if (how == SIG_SETMASK) {
|
|
sigprocmask(how, 0, &mask);
|
|
}
|
|
mask.__bits[0] = imask;
|
|
}
|
|
if (!sigprocmask(how, maskptr, &oldmask)) {
|
|
lua_pushinteger(L, oldmask.__bits[0]);
|
|
return 1;
|
|
} else {
|
|
lua_pushnil(L);
|
|
lua_pushinteger(L, errno);
|
|
errno = olderr;
|
|
return 2;
|
|
}
|
|
}
|
|
|
|
static void LuaUnixOnSignal(int sig, siginfo_t *si, ucontext_t *ctx) {
|
|
STRACE("LuaUnixOnSignal(%G)", sig);
|
|
lua_getglobal(GL, "__signal_handlers");
|
|
CHECK_EQ(LUA_TFUNCTION, lua_geti(GL, -1, sig));
|
|
lua_remove(GL, -2);
|
|
lua_pushinteger(GL, sig);
|
|
if (lua_pcall(GL, 1, 0, 0) != LUA_OK) {
|
|
ERRORF("(lua) %s failed: %s", strsignal(sig), lua_tostring(GL, -1));
|
|
lua_pop(GL, 1); // pop error
|
|
}
|
|
}
|
|
|
|
// unix.sigaction(sig[, handler[, flags[, mask]]]) → handler, flags, mask, errno
|
|
//
|
|
// unix = require "unix"
|
|
// unix.sigaction(unix.SIGUSR1, function(sig)
|
|
// print(string.format("got %s", unix.strsignal(sig)))
|
|
// end)
|
|
// unix.sigprocmask(unix.SIG_SETMASK, -1)
|
|
// unix.raise(unix.SIGUSR1)
|
|
// unix.sigsuspend()
|
|
//
|
|
// handler can be SIG_IGN, SIG_DFL, intptr_t, or a Lua function
|
|
// sig can be SIGINT, SIGQUIT, SIGTERM, SIGUSR1, etc.
|
|
static int LuaUnixSigaction(lua_State *L) {
|
|
int i, n, sig, olderr;
|
|
struct sigaction sa, oldsa, *saptr;
|
|
saptr = &sa;
|
|
olderr = errno;
|
|
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, SA_RESTART);
|
|
sa.sa_mask.__bits[0] |= luaL_optinteger(L, 4, 0);
|
|
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_geti(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_seti(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);
|
|
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 2;
|
|
}
|
|
}
|
|
|
|
// unix.sigsuspend([mask]) → errno
|
|
static int LuaUnixSigsuspend(lua_State *L) {
|
|
int olderr;
|
|
sigset_t mask;
|
|
olderr = errno;
|
|
mask.__bits[0] = luaL_optinteger(L, 1, 0);
|
|
mask.__bits[1] = 0;
|
|
sigsuspend(&mask);
|
|
lua_pushinteger(L, errno);
|
|
errno = olderr;
|
|
return 1;
|
|
}
|
|
|
|
// unix.setitimer(which[, intsec, intmicros, valsec, valmicros])
|
|
// → intsec, intns, valsec, valns, errno
|
|
//
|
|
// ticks = 0
|
|
// unix.sigaction(unix.SIGALRM, function(sig)
|
|
// print(string.format("tick no. %d", ticks))
|
|
// ticks = ticks + 1
|
|
// end)
|
|
// unix.setitimer(unix.ITIMER_REAL, 0, 400000, 0, 400000)
|
|
// while true do
|
|
// unix.sigsuspend()
|
|
// end
|
|
//
|
|
// which should be ITIMER_REAL
|
|
static int LuaUnixSetitimer(lua_State *L) {
|
|
int which, olderr;
|
|
struct itimerval it, oldit, *itptr;
|
|
olderr = errno;
|
|
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);
|
|
it.it_value.tv_sec = luaL_optinteger(L, 4, 0);
|
|
it.it_value.tv_usec = luaL_optinteger(L, 5, 0);
|
|
} else {
|
|
itptr = 0;
|
|
}
|
|
if (!setitimer(which, itptr, &oldit)) {
|
|
lua_pushinteger(L, oldit.it_interval.tv_sec);
|
|
lua_pushinteger(L, oldit.it_interval.tv_usec);
|
|
lua_pushinteger(L, oldit.it_value.tv_sec);
|
|
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;
|
|
}
|
|
}
|
|
|
|
// unix.strerror(errno) → str
|
|
static int LuaUnixStrerror(lua_State *L) {
|
|
lua_pushstring(L, strerror(luaL_checkinteger(L, 1)));
|
|
return 1;
|
|
}
|
|
|
|
// unix.strsignal(sig) → str
|
|
static int LuaUnixStrsignal(lua_State *L) {
|
|
lua_pushstring(L, strsignal(luaL_checkinteger(L, 1)));
|
|
return 1;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// UnixStat* object
|
|
|
|
static dontinline struct stat *GetUnixStat(lua_State *L) {
|
|
struct UnixStat **ust;
|
|
ust = luaL_checkudata(L, 1, "UnixStat*");
|
|
return &(*ust)->st;
|
|
}
|
|
|
|
static int LuaUnixStatSize(lua_State *L) {
|
|
return ReturnInteger(L, GetUnixStat(L)->st_size);
|
|
}
|
|
|
|
static int LuaUnixStatMode(lua_State *L) {
|
|
return ReturnInteger(L, GetUnixStat(L)->st_mode);
|
|
}
|
|
|
|
static int LuaUnixStatDev(lua_State *L) {
|
|
return ReturnInteger(L, GetUnixStat(L)->st_dev);
|
|
}
|
|
|
|
static int LuaUnixStatIno(lua_State *L) {
|
|
return ReturnInteger(L, GetUnixStat(L)->st_ino);
|
|
}
|
|
|
|
static int LuaUnixStatNlink(lua_State *L) {
|
|
return ReturnInteger(L, GetUnixStat(L)->st_nlink);
|
|
}
|
|
|
|
static int LuaUnixStatRdev(lua_State *L) {
|
|
return ReturnInteger(L, GetUnixStat(L)->st_rdev);
|
|
}
|
|
|
|
static int LuaUnixStatUid(lua_State *L) {
|
|
return ReturnInteger(L, GetUnixStat(L)->st_uid);
|
|
}
|
|
|
|
static int LuaUnixStatGid(lua_State *L) {
|
|
return ReturnInteger(L, GetUnixStat(L)->st_gid);
|
|
}
|
|
|
|
static int LuaUnixStatBlocks(lua_State *L) {
|
|
return ReturnInteger(L, GetUnixStat(L)->st_blocks);
|
|
}
|
|
|
|
static int LuaUnixStatBlksize(lua_State *L) {
|
|
return ReturnInteger(L, GetUnixStat(L)->st_blksize);
|
|
}
|
|
|
|
static int LuaUnixStatAtim(lua_State *L) {
|
|
return ReturnTimespec(L, &GetUnixStat(L)->st_atim);
|
|
}
|
|
|
|
static int LuaUnixStatMtim(lua_State *L) {
|
|
return ReturnTimespec(L, &GetUnixStat(L)->st_mtim);
|
|
}
|
|
|
|
static int LuaUnixStatCtim(lua_State *L) {
|
|
return ReturnTimespec(L, &GetUnixStat(L)->st_ctim);
|
|
}
|
|
|
|
static void FreeUnixStat(struct UnixStat *stat) {
|
|
if (!--stat->refs) {
|
|
free(stat);
|
|
}
|
|
}
|
|
|
|
static int LuaUnixStatGc(lua_State *L) {
|
|
struct UnixStat **ust;
|
|
ust = luaL_checkudata(L, 1, "UnixStat*");
|
|
if (*ust) {
|
|
FreeUnixStat(*ust);
|
|
*ust = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static const luaL_Reg kLuaUnixStatMeth[] = {
|
|
{"size", LuaUnixStatSize}, //
|
|
{"mode", LuaUnixStatMode}, //
|
|
{"dev", LuaUnixStatDev}, //
|
|
{"ino", LuaUnixStatIno}, //
|
|
{"nlink", LuaUnixStatNlink}, //
|
|
{"rdev", LuaUnixStatRdev}, //
|
|
{"uid", LuaUnixStatUid}, //
|
|
{"gid", LuaUnixStatGid}, //
|
|
{"atim", LuaUnixStatAtim}, //
|
|
{"mtim", LuaUnixStatMtim}, //
|
|
{"ctim", LuaUnixStatCtim}, //
|
|
{"blocks", LuaUnixStatBlocks}, //
|
|
{"blksize", LuaUnixStatBlksize}, //
|
|
{0}, //
|
|
};
|
|
|
|
static const luaL_Reg kLuaUnixStatMeta[] = {
|
|
{"__gc", LuaUnixStatGc}, //
|
|
{0}, //
|
|
};
|
|
|
|
static void LuaUnixStatObj(lua_State *L) {
|
|
luaL_newmetatable(L, "UnixStat*");
|
|
luaL_setfuncs(L, kLuaUnixStatMeta, 0);
|
|
luaL_newlibtable(L, kLuaUnixStatMeth);
|
|
luaL_setfuncs(L, kLuaUnixStatMeth, 0);
|
|
lua_setfield(L, -2, "__index");
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// UnixDir* object
|
|
|
|
static struct UnixDir **GetUnixDirSelf(lua_State *L) {
|
|
return luaL_checkudata(L, 1, "UnixDir*");
|
|
}
|
|
|
|
static DIR *GetDirOrDie(lua_State *L) {
|
|
struct UnixDir **udir;
|
|
udir = GetUnixDirSelf(L);
|
|
assert((*udir)->dir);
|
|
if (*udir) return (*udir)->dir;
|
|
luaL_argerror(L, 1, "UnixDir* is closed");
|
|
unreachable;
|
|
}
|
|
|
|
static void FreeUnixDir(struct UnixDir *dir) {
|
|
if (!--dir->refs) {
|
|
closedir(dir->dir);
|
|
}
|
|
}
|
|
|
|
// UnixDir:close()
|
|
// may be called multiple times
|
|
// called by the garbage collector too
|
|
static int LuaUnixDirClose(lua_State *L) {
|
|
struct UnixDir **udir;
|
|
udir = GetUnixDirSelf(L);
|
|
if (*udir) {
|
|
FreeUnixDir(*udir);
|
|
*udir = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// UnixDir:read() → name, kind, ino, off
|
|
// returns nil if no more entries
|
|
// kind can be DT_UNKNOWN/REG/DIR/BLK/LNK/CHR/FIFO/SOCK
|
|
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 {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// UnixDir:fd() → fd, errno
|
|
// EOPNOTSUPP if using /zip/
|
|
// EOPNOTSUPP if IsWindows()
|
|
static int LuaUnixDirFd(lua_State *L) {
|
|
int fd, olderr;
|
|
olderr = errno;
|
|
fd = dirfd(GetDirOrDie(L));
|
|
if (fd != -1) {
|
|
lua_pushinteger(L, fd);
|
|
return 1;
|
|
} else {
|
|
lua_pushnil(L);
|
|
lua_pushinteger(L, errno);
|
|
errno = olderr;
|
|
return 2;
|
|
}
|
|
}
|
|
|
|
// UnixDir:tell() → off
|
|
static int LuaUnixDirTell(lua_State *L) {
|
|
long off;
|
|
off = telldir(GetDirOrDie(L));
|
|
lua_pushinteger(L, off);
|
|
return 1;
|
|
}
|
|
|
|
// UnixDir:rewind()
|
|
static int LuaUnixDirRewind(lua_State *L) {
|
|
rewinddir(GetDirOrDie(L));
|
|
return 0;
|
|
}
|
|
|
|
static const luaL_Reg kLuaUnixDirMeth[] = {
|
|
{"close", LuaUnixDirClose}, //
|
|
{"read", LuaUnixDirRead}, //
|
|
{"fd", LuaUnixDirFd}, //
|
|
{"tell", LuaUnixDirTell}, //
|
|
{"rewind", LuaUnixDirRewind}, //
|
|
{0}, //
|
|
};
|
|
|
|
static const luaL_Reg kLuaUnixDirMeta[] = {
|
|
{"__gc", LuaUnixDirClose}, //
|
|
{0}, //
|
|
};
|
|
|
|
static void LuaUnixDirObj(lua_State *L) {
|
|
luaL_newmetatable(L, "UnixDir*");
|
|
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[] = {
|
|
{"exit", LuaUnixExit}, // exit w/o atexit
|
|
{"stat", LuaUnixStat}, // get file info
|
|
{"open", LuaUnixOpen}, // open file fd at lowest slot
|
|
{"close", LuaUnixClose}, // close file or socket
|
|
{"seek", LuaUnixSeek}, // 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
|
|
{"getcwd", LuaUnixGetcwd}, // get current directory
|
|
{"fork", LuaUnixFork}, // make child process via mitosis
|
|
{"execve", LuaUnixExecve}, // replace process with program
|
|
{"commandv", LuaUnixCommandv}, // resolve program on $PATH
|
|
{"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
|
|
{"rmdir", LuaUnixRmdir}, // remove empty directory
|
|
{"opendir", LuaUnixOpendir}, // 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
|
|
{"umask", LuaUnixUmask}, // set file mode creation mask
|
|
{"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
|
|
{"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
|
|
{"getgid", LuaUnixGetgid}, // get real group id of process
|
|
{"gettime", LuaUnixGettime}, // get timestamp w/ nano precision
|
|
{"nanosleep", LuaUnixNanosleep}, // sleep w/ nano precision
|
|
{"socket", LuaUnixSocket}, // create network communication fd
|
|
{"socketpair", LuaUnixSocketpair}, // create bidirectional pipe
|
|
{"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
|
|
{"sigaction", LuaUnixSigaction}, // install signal handler
|
|
{"sigprocmask", LuaUnixSigprocmask}, // change signal mask
|
|
{"sigsuspend", LuaUnixSigsuspend}, // wait for signal
|
|
{"setitimer", LuaUnixSetitimer}, // set alarm clock
|
|
{"strerror", LuaUnixStrerror}, // turn errno into string
|
|
{"strsignal", LuaUnixStrsignal}, // turn signal into string
|
|
{0}, //
|
|
};
|
|
|
|
int LuaUnix(lua_State *L) {
|
|
int i;
|
|
char sigbuf[12];
|
|
|
|
GL = L;
|
|
luaL_newlib(L, kLuaUnix);
|
|
LuaUnixStatObj(L);
|
|
LuaUnixDirObj(L);
|
|
lua_newtable(L);
|
|
lua_setglobal(L, "__signal_handlers");
|
|
|
|
// errnos
|
|
for (i = 0; kErrorNames[i].x; ++i) {
|
|
LuaSetIntField(L, (const char *)((uintptr_t)kErrorNames + kErrorNames[i].s),
|
|
*(const int *)((uintptr_t)kErrorNames + kErrorNames[i].x));
|
|
}
|
|
|
|
// signals
|
|
strcpy(sigbuf, "SIG");
|
|
for (i = 0; kStrSignal[i].x; ++i) {
|
|
strcpy(sigbuf + 3, (const char *)((uintptr_t)kStrSignal + kStrSignal[i].s));
|
|
LuaSetIntField(L, sigbuf,
|
|
*(const int *)((uintptr_t)kStrSignal + kStrSignal[i].x));
|
|
}
|
|
|
|
// open() flags
|
|
LuaSetIntField(L, "O_RDONLY", O_RDONLY); //
|
|
LuaSetIntField(L, "O_WRONLY", O_WRONLY); //
|
|
LuaSetIntField(L, "O_RDWR", O_RDWR); //
|
|
LuaSetIntField(L, "O_ACCMODE", O_ACCMODE); // mask of prev three
|
|
LuaSetIntField(L, "O_CREAT", O_CREAT); //
|
|
LuaSetIntField(L, "O_EXCL", O_EXCL); //
|
|
LuaSetIntField(L, "O_TRUNC", O_TRUNC); //
|
|
LuaSetIntField(L, "O_CLOEXEC", O_CLOEXEC); //
|
|
LuaSetIntField(L, "O_APPEND", O_APPEND); // weird on nt
|
|
LuaSetIntField(L, "O_TMPFILE", O_TMPFILE); // linux, windows
|
|
LuaSetIntField(L, "O_NOFOLLOW", O_NOFOLLOW); // unix
|
|
LuaSetIntField(L, "O_SYNC", O_SYNC); // unix
|
|
LuaSetIntField(L, "O_ASYNC", O_ASYNC); // unix
|
|
LuaSetIntField(L, "O_NOCTTY", O_NOCTTY); // unix
|
|
LuaSetIntField(L, "O_NOATIME", O_NOATIME); // linux
|
|
LuaSetIntField(L, "O_EXEC", O_EXEC); // free/openbsd
|
|
LuaSetIntField(L, "O_SEARCH", O_SEARCH); // free/netbsd
|
|
LuaSetIntField(L, "O_DSYNC", O_DSYNC); // linux/xnu/open/netbsd
|
|
LuaSetIntField(L, "O_RSYNC", O_RSYNC); // linux/open/netbsd
|
|
LuaSetIntField(L, "O_PATH", O_PATH); // linux
|
|
LuaSetIntField(L, "O_VERIFY", O_VERIFY); // freebsd
|
|
LuaSetIntField(L, "O_SHLOCK", O_SHLOCK); // bsd
|
|
LuaSetIntField(L, "O_EXLOCK", O_EXLOCK); // bsd
|
|
LuaSetIntField(L, "O_RANDOM", O_RANDOM); // windows
|
|
LuaSetIntField(L, "O_SEQUENTIAL", O_SEQUENTIAL); // windows
|
|
LuaSetIntField(L, "O_COMPRESSED", O_COMPRESSED); // windows
|
|
LuaSetIntField(L, "O_INDEXED", O_INDEXED); // windows
|
|
|
|
// 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);
|
|
|
|
// wait() options
|
|
LuaSetIntField(L, "WNOHANG", WNOHANG);
|
|
|
|
// gettime() clocks
|
|
LuaSetIntField(L, "CLOCK_REALTIME", CLOCK_REALTIME); // portable
|
|
LuaSetIntField(L, "CLOCK_MONOTONIC", CLOCK_MONOTONIC); // portable
|
|
LuaSetIntField(L, "CLOCK_MONOTONIC_RAW", CLOCK_MONOTONIC_RAW); // portable
|
|
LuaSetIntField(L, "CLOCK_REALTIME_COARSE", CLOCK_REALTIME_COARSE);
|
|
LuaSetIntField(L, "CLOCK_MONOTONIC_COARSE", CLOCK_MONOTONIC_COARSE);
|
|
LuaSetIntField(L, "CLOCK_PROCESS_CPUTIME_ID", CLOCK_PROCESS_CPUTIME_ID);
|
|
LuaSetIntField(L, "CLOCK_TAI", CLOCK_TAI);
|
|
LuaSetIntField(L, "CLOCK_PROF", CLOCK_PROF);
|
|
LuaSetIntField(L, "CLOCK_BOOTTIME", CLOCK_BOOTTIME);
|
|
LuaSetIntField(L, "CLOCK_REALTIME_ALARM", CLOCK_REALTIME_ALARM);
|
|
LuaSetIntField(L, "CLOCK_BOOTTIME_ALARM", CLOCK_BOOTTIME_ALARM);
|
|
|
|
// 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_CLOEXEC", SOCK_CLOEXEC);
|
|
|
|
// socket() protocol
|
|
LuaSetIntField(L, "IPPROTO_TCP", IPPROTO_TCP);
|
|
LuaSetIntField(L, "IPPROTO_UDP", IPPROTO_UDP);
|
|
|
|
// 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);
|
|
|
|
// i/o options
|
|
LuaSetIntField(L, "AT_FDCWD", AT_FDCWD);
|
|
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);
|
|
|
|
return 1;
|
|
}
|