Add more raw system calls to redbean

We now have execve, setitimer, sigaction, sigsuspend, and sigprocmask.
This commit is contained in:
Justine Tunney 2022-04-13 14:43:42 -07:00
parent 281a0f2730
commit fb7e8ef1e6
8 changed files with 329 additions and 73 deletions

View file

@ -45,7 +45,7 @@ static textwindows inline bool HasWorkingConsole(void) {
*/
int raise(int sig) {
int rc, event;
STRACE("raise(%G) → [...]", sig);
STRACE("raise(%G) → ...", sig);
if (sig == SIGTRAP) {
DebugBreak();
rc = 0;
@ -75,6 +75,6 @@ int raise(int sig) {
rc = __sig_raise(sig, SI_USER);
}
}
STRACE("[...] raise(%G) → %d% m", sig, rc);
STRACE("...raise(%G) → %d% m", sig, rc);
return rc;
}

View file

@ -62,8 +62,6 @@ int sigprocmask(int how, const sigset_t *opt_set, sigset_t *opt_out_oldset) {
char buf[2][41];
int res, rc, arg1;
const sigset_t *arg2;
STRACE("sigprocmask(%s, %s, [...]", DescribeHow(howbuf, how),
__strace_sigset(buf[0], sizeof(buf[0]), 0, opt_set));
sigemptyset(&old);
if (IsAsan() &&
((opt_set && !__asan_is_valid(opt_set, sizeof(*opt_set))) ||
@ -94,7 +92,7 @@ int sigprocmask(int how, const sigset_t *opt_set, sigset_t *opt_out_oldset) {
if (rc != -1 && opt_out_oldset) {
*opt_out_oldset = old;
}
STRACE("[...] sigprocmask(%s, %s, [%s]) → %d% m", DescribeHow(howbuf, how),
STRACE("sigprocmask(%s, %s, [%s]) → %d% m", DescribeHow(howbuf, how),
__strace_sigset(buf[0], sizeof(buf[0]), 0, opt_set),
__strace_sigset(buf[1], sizeof(buf[1]), rc, opt_out_oldset), rc);
return rc;

View file

@ -32,6 +32,9 @@
/**
* Blocks until SIG MASK is delivered to process.
*
* This temporarily replaces the signal mask until a signal that it
* doesn't contain is delivered.
*
* @param ignore is a bitset of signals to block temporarily, which if
* NULL is equivalent to passing an empty signal set
* @return -1 w/ EINTR (or possibly EFAULT)
@ -43,8 +46,7 @@ int sigsuspend(const sigset_t *ignore) {
char buf[41];
long ms, totoms;
sigset_t save, mask, *arg;
STRACE("sigsuspend(%s) → [...]",
__strace_sigset(buf, sizeof(buf), 0, ignore));
STRACE("sigsuspend(%s) → ...", __strace_sigset(buf, sizeof(buf), 0, ignore));
if (IsAsan() && ignore && !__asan_is_valid(ignore, sizeof(*ignore))) {
rc = efault();
} else if (IsXnu() || IsOpenbsd()) {
@ -79,7 +81,7 @@ int sigsuspend(const sigset_t *ignore) {
ms += __SIG_POLLING_INTERVAL_MS;
if (ms >= __SIG_LOGGING_INTERVAL_MS) {
totoms += ms, ms = 0;
STRACE("[...] sigsuspending for %'lums...", totoms);
STRACE("... sigsuspending for %'lums...", totoms);
}
#endif
} while (1);
@ -89,6 +91,6 @@ int sigsuspend(const sigset_t *ignore) {
// TODO(jart): sigsuspend metal support
rc = enosys();
}
STRACE("[...] sigsuspend → %d% m", rc);
STRACE("...sigsuspend → %d% m", rc);
return rc;
}

View file

@ -171,11 +171,7 @@ LUA_API lua_Number lua_version (lua_State *L) {
/**
* lua_absindex
*
* [-0, +0, ]
*
* int lua_absindex (lua_State *L, int idx);
* lua_absindex [-0, +0, ]
*
* Converts the acceptable index idx into an equivalent absolute index (that
* is, one that does not depend on the stack size).
@ -187,11 +183,7 @@ LUA_API int lua_absindex (lua_State *L, int idx) {
}
/**
* lua_gettop
*
* [-0, +0, ]
*
* int lua_gettop (lua_State *L);
* lua_gettop [-0, +0, ]
*
* Returns the index of the top element in the stack. Because indices start
* at 1, this result is equal to the number of elements in the stack; in
@ -202,11 +194,7 @@ LUA_API int lua_gettop (lua_State *L) {
}
/**
* lua_settop
*
* [-?, +?, e]
*
* void lua_settop (lua_State *L, int index);
* lua_settop [-?, +?, e]
*
* Accepts any index, or 0, and sets the stack top to this index. If the new
* top is greater than the old one, then the new elements are filled with

View file

@ -34,7 +34,8 @@ FLAGS
-b log message bodies
-a log resource usage
-g log handler latency
-e run specified Lua command
-e eval Lua code in arg
-F eval Lua code in file
-E show crash reports to public ips
-j enable ssl client verify
-k disable ssl fetch verify
@ -1259,8 +1260,12 @@ UNIX MODULE
There's also a /unix.lua file in redbean-demo.com that provides a
glimpse of how these powerful APIs can be used. Here's a synopsis:
unix.fork() → childpid|0, errno
unix.exit([exitcode]) → ⊥
unix.fork() → childpid|0, errno
unix.commandv(prog) → path, errno
unix.execve(prog, argv[, envp]) → errno
prog needs to be absolute, see commandv()
envp defaults to environ
unix.access(path, mode) → rc, errno
mode can be: R_OK, W_OK, X_OK, F_OK
unix.mkdir(path, mode) → rc, errno
@ -1335,13 +1340,17 @@ UNIX MODULE
flags MSG_OOB, MSG_DONTROUTE, MSG_NOSIGNAL, etc.
unix.shutdown(fd, how) → rc, errno
how can be SHUT_RD, SHUT_WR, or SHUT_RDWR
unix.sigprocmask(how, mask) → oldmask, errno
unix.sigprocmask(how[, mask]) → oldmask, errno
how can be SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK
unix.sigaction(sig, handler[, flags[, mask]]) → rc, errno
unix.sigaction(sig[, handler[, flags[, mask]]]) → handler, flags, mask, errno
handler can be SIG_IGN, SIG_DFL, intptr_t, or a Lua function
sig can be SIGINT, SIGQUIT, SIGTERM, SIGUSR1, etc.
handler can be SIG_IGN or SIG_DFL for time being
note: this api will be changed in the future
unix.sigsuspend([mask]) → errno
unix.setitimer(which[, intsec, intmicros, valsec, valmicros])
→ intsec, intns, valsec, valns, errno
which should be ITIMER_REAL
unix.strerror(errno) → str
unix.strsignal(sig) → str
Here's your UnixStat* object.
@ -1354,9 +1363,9 @@ UNIX MODULE
UnixStat:rdev() → int
UnixStat:uid() → int
UnixStat:gid() → int
UnixStat:atim() → secs:int, nanosint
UnixStat:mtim() → secs:int, nanosint
UnixStat:ctim() → secs:int, nanosint
UnixStat:atim() → secs:int, nanos:int
UnixStat:mtim() → secs:int, nanos:int
UnixStat:ctim() → secs:int, nanos:int
UnixStat:blocks() → int
UnixStat:blksize() → int

22
tool/net/luacheck.h Normal file
View file

@ -0,0 +1,22 @@
#ifndef COSMOPOLITAN_TOOL_NET_LUACHECK_H_
#define COSMOPOLITAN_TOOL_NET_LUACHECK_H_
#include "libc/log/log.h"
#include "libc/mem/mem.h"
#include "third_party/lua/cosmo.h"
#include "third_party/lua/lua.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
#define AssertLuaStackIsEmpty(L) \
do { \
if (lua_gettop(L)) { \
char *s = LuaFormatStack(L); \
WARNF("lua stack should be empty!\n%s", s); \
free(s); \
lua_settop(L, 0); \
} \
} while (0)
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_TOOL_NET_LUACHECK_H_ */

View file

@ -18,16 +18,24 @@
*/
#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"
@ -37,6 +45,7 @@
#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"
@ -50,6 +59,7 @@
#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
@ -66,6 +76,8 @@ struct UnixDir {
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);
@ -90,6 +102,32 @@ static int ReturnRc(lua_State *L, int64_t rc, int 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
@ -227,6 +265,58 @@ static int LuaUnixFork(lua_State *L) {
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());
@ -843,21 +933,27 @@ static int LuaUnixShutdown(lua_State *L) {
return ReturnRc(L, rc, olderr);
}
// unix.sigprocmask(how, mask) → oldmask, errno
// 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;
sigset_t mask, oldmask, *maskptr;
olderr = errno;
how = luaL_checkinteger(L, 1);
imask = luaL_checkinteger(L, 2);
bzero(&mask, sizeof(mask));
if (how == SIG_SETMASK) {
sigprocmask(how, 0, &mask);
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;
}
mask.__bits[0] = imask;
if (!sigprocmask(how, &mask, &oldmask)) {
if (!sigprocmask(how, maskptr, &oldmask)) {
lua_pushinteger(L, oldmask.__bits[0]);
return 1;
} else {
@ -868,32 +964,99 @@ static int LuaUnixSigprocmask(lua_State *L) {
}
}
// unix.sigaction(sig, handler[, flags[, mask]]) → rc, errno
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.
// handler can be SIG_IGN or SIG_DFL for time being
// note: this api will be changed in the future
static int LuaUnixSigaction(lua_State *L) {
void *handler;
int sig, olderr;
struct sigaction sa;
uint64_t flags, imask;
int i, n, sig, olderr;
struct sigaction sa, oldsa, *saptr;
saptr = &sa;
olderr = errno;
sigemptyset(&sa.sa_mask);
sig = luaL_checkinteger(L, 1);
handler = (void *)luaL_checkinteger(L, 2);
flags = luaL_optinteger(L, 3, SA_RESTART);
imask = luaL_optinteger(L, 4, 0);
if (handler != SIG_DFL && handler != SIG_IGN) {
luaL_argerror(L, 2, "handler not SIG_DFL or SIG_IGN");
if (!(1 <= sig && sig <= NSIG)) {
luaL_argerror(L, 1, "signal number invalid");
unreachable;
}
sa.sa_handler = handler;
sa.sa_flags = flags;
sa.sa_mask.__bits[0] = imask;
sa.sa_mask.__bits[1] = 0;
if (!sigaction(sig, &sa, 0)) {
lua_pushinteger(L, 0);
return 1;
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;
@ -901,12 +1064,76 @@ static int LuaUnixSigaction(lua_State *L) {
}
}
// 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.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
@ -1139,6 +1366,8 @@ static const luaL_Reg kLuaUnix[] = {
{"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
@ -1179,7 +1408,10 @@ static const luaL_Reg kLuaUnix[] = {
{"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}, //
};
@ -1187,9 +1419,12 @@ 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) {
@ -1322,5 +1557,10 @@ int LuaUnix(lua_State *L) {
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;
}

View file

@ -132,6 +132,7 @@
#include "third_party/zlib/zlib.h"
#include "tool/build/lib/case.h"
#include "tool/build/lib/psk.h"
#include "tool/net/luacheck.h"
STATIC_STACK_SIZE(0x40000);
STATIC_YOINK("zip_uri_support");
@ -173,16 +174,6 @@ STATIC_YOINK("zip_uri_support");
#define HeaderEqualCase(H, S) \
SlicesEqualCase(S, strlen(S), HeaderData(H), HeaderLength(H))
#define AssertLuaStackIsEmpty(L) \
do { \
if (lua_gettop(L)) { \
char *s = LuaFormatStack(L); \
WARNF("lua stack should be empty!\n%s", s); \
free(s); \
lua_settop(L, 0); \
} \
} while (0)
static const uint8_t kGzipHeader[] = {
0x1F, // MAGNUM
0x8B, // MAGNUM
@ -397,7 +388,8 @@ static uint32_t clientaddrsize;
static size_t zsize;
static char *outbuf;
static lua_State *GL, *YL;
static lua_State *GL;
static lua_State *YL;
static char *content;
static uint8_t *zmap;
static uint8_t *zbase;
@ -1041,7 +1033,7 @@ static void LogLuaError(char *hook, char *err) {
ERRORF("(lua) failed to run %s: %s", hook, err);
}
static bool LuaRunCode(const char *code) {
static bool LuaEvalCode(const char *code) {
lua_State *L = GL;
int status = luaL_loadstring(L, code);
if (status != LUA_OK || LuaCallWithTrace(L, 0, 0, NULL) != LUA_OK) {
@ -1053,6 +1045,10 @@ static bool LuaRunCode(const char *code) {
return true;
}
static bool LuaEvalFile(const char *path) {
return LuaEvalCode(gc(xslurp(path, 0)));
}
static bool LuaOnClientConnection(void) {
#ifndef STATIC
bool dropit;
@ -6979,7 +6975,8 @@ static void GetOpts(int argc, char *argv[]) {
CASE('h', PrintUsage(stdout, EXIT_SUCCESS));
CASE('M', ProgramMaxPayloadSize(ParseInt(optarg)));
#ifndef STATIC
CASE('e', LuaRunCode(optarg));
CASE('e', LuaEvalCode(optarg));
CASE('F', LuaEvalFile(optarg));
CASE('E', leakcrashreports = true);
CASE('A', storeasset = true; StorePath(optarg));
#endif