diff --git a/libc/calls/calls.mk b/libc/calls/calls.mk index 28e882b67..72322331c 100644 --- a/libc/calls/calls.mk +++ b/libc/calls/calls.mk @@ -65,6 +65,7 @@ $(LIBC_CALLS_A).pkg: \ $(LIBC_CALLS_A_OBJS) \ $(foreach x,$(LIBC_CALLS_A_DIRECTDEPS),$($(x)_A).pkg) +o/$(MODE)/libc/calls/vdsofunc.greg.o \ o/$(MODE)/libc/calls/directmap.o \ o/$(MODE)/libc/calls/directmap-nt.o \ o/$(MODE)/libc/calls/raise.o: \ diff --git a/libc/calls/clock_gettime.c b/libc/calls/clock_gettime.c index 4d206301c..f091d6719 100644 --- a/libc/calls/clock_gettime.c +++ b/libc/calls/clock_gettime.c @@ -26,6 +26,8 @@ #include "libc/nt/synchronization.h" #include "libc/sysv/errfuns.h" +static typeof(sys_clock_gettime) *__clock_gettime = sys_clock_gettime; + /** * Returns nanosecond time. * @@ -52,7 +54,7 @@ noinstrument int clock_gettime(int clockid, struct timespec *ts) { rc = einval(); } else if (!IsWindows()) { e = errno; - if ((rc = sys_clock_gettime(clockid, ts))) { + if ((rc = __clock_gettime(clockid, ts))) { errno = e; ad = sys_gettimeofday((struct timeval *)ts, NULL, NULL); assert(ad.ax != -1); @@ -72,3 +74,23 @@ noinstrument int clock_gettime(int clockid, struct timespec *ts) { } return rc; } + +/** + * Returns fast system clock_gettime() if it exists. + */ +void *__get_clock_gettime(void) { + void *vdso; + static bool once; + static void *result; + if (!once) { + if ((vdso = __vdsofunc("__vdso_clock_gettime"))) { + __clock_gettime = result = vdso; + } + once = true; + } + return result; +} + +const void *const __clock_gettime_ctor[] initarray = { + __get_clock_gettime, +}; diff --git a/libc/calls/faccessat.c b/libc/calls/faccessat.c index 6b2c7ac3b..cadba32f0 100644 --- a/libc/calls/faccessat.c +++ b/libc/calls/faccessat.c @@ -33,7 +33,7 @@ * file is a relative path, then file is opened relative to dirfd * @param path is a filename or directory * @param mode can be R_OK, W_OK, X_OK, F_OK - * @param flags should be 0 + * @param flags can have AT_EACCESS, AT_SYMLINK_NOFOLLOW * @return 0 if ok, or -1 and sets errno * @asyncsignalsafe */ diff --git a/libc/calls/fstat-nt.c b/libc/calls/fstat-nt.c index 01ffa70c5..125ef8e09 100644 --- a/libc/calls/fstat-nt.c +++ b/libc/calls/fstat-nt.c @@ -106,7 +106,7 @@ textwindows int sys_fstat_nt(int64_t handle, struct stat *st) { st->st_size = (uint64_t)wst.nFileSizeHigh << 32 | wst.nFileSizeLow; st->st_blksize = PAGESIZE; st->st_dev = wst.dwVolumeSerialNumber; - st->st_rdev = wst.dwVolumeSerialNumber; + st->st_rdev = 0; st->st_ino = (uint64_t)wst.nFileIndexHigh << 32 | wst.nFileIndexLow; st->st_nlink = wst.nNumberOfLinks; if (S_ISLNK(st->st_mode)) { diff --git a/libc/calls/getexecutablename.c b/libc/calls/getexecutablename.c new file mode 100644 index 000000000..a191df534 --- /dev/null +++ b/libc/calls/getexecutablename.c @@ -0,0 +1,121 @@ +/*-*- 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/calls/calls.h" +#include "libc/calls/internal.h" +#include "libc/dce.h" +#include "libc/errno.h" +#include "libc/macros.internal.h" +#include "libc/nt/runtime.h" +#include "libc/runtime/runtime.h" +#include "libc/sysv/consts/at.h" +#include "libc/sysv/consts/ok.h" + +#define SIZE 1024 +#define CTL_KERN 1 +#define KERN_PROC 14 +#define KERN_PROC_PATHNAME_FREEBSD 12 +#define KERN_PROC_PATHNAME_NETBSD 5 + +char program_executable_name[PATH_MAX + 1]; + +static inline void GetProgramExecutableNameImpl(char *p, char *e) { + char *q; + ssize_t rc; + size_t i, n; + union { + int cmd[4]; + char16_t path16[PATH_MAX + 1]; + } u; + + if (IsWindows()) { + n = GetModuleFileName(0, u.path16, ARRAYLEN(u.path16)); + for (i = 0; i < n; ++i) { + if (u.path16[i] == '\\') { + u.path16[i] = '/'; + } + } + if (isalpha(u.path16[0]) && u.path16[1] == ':' && u.path16[2] == '/') { + p[0] = '/'; + p[1] = '/'; + p[2] = '?'; + p[3] = '/'; + p += 4; + } + tprecode16to8(p, e - p, u.path16); + return; + } + + if (__argc && (q = __argv[0]) && !sys_faccessat(AT_FDCWD, q, F_OK, 0)) { + if (*q != '/') { + if (q[0] == '.' && q[1] == '/') { + q += 2; + } + if (getcwd(p, e - p)) { + while (*p) ++p; + *p++ = '/'; + } + } + for (i = 0; *q && p + 1 < e; ++p, ++q) { + *p = *q; + } + *p = 0; + return; + } + + if ((rc = sys_readlinkat(AT_FDCWD, "/proc/self/exe", p, e - p - 1)) > 0 || + (rc = sys_readlinkat(AT_FDCWD, "/proc/curproc/file", p, e - p - 1)) > 0) { + p[rc] = 0; + return; + } + + if (IsFreebsd() || IsNetbsd()) { + u.cmd[0] = CTL_KERN; + u.cmd[1] = KERN_PROC; + if (IsFreebsd()) { + u.cmd[2] = KERN_PROC_PATHNAME_FREEBSD; + } else { + u.cmd[2] = KERN_PROC_PATHNAME_NETBSD; + } + u.cmd[3] = -1; // current process + n = e - p; + if (sysctl(u.cmd, ARRAYLEN(u.cmd), p, &n, 0, 0) != -1) { + return; + } + } +} + +/** + * Returns absolute path of executable. + */ +char *GetProgramExecutableName(void) { + int e; + static bool once; + if (!once) { + e = errno; + GetProgramExecutableNameImpl( + program_executable_name, + program_executable_name + sizeof(program_executable_name)); + errno = e; + } + return program_executable_name; +} + +const void *const GetProgramExecutableNameCtor[] initarray = { + GetProgramExecutableName, +}; diff --git a/libc/calls/gettimeofday.c b/libc/calls/gettimeofday.c index 425549707..927e785a4 100644 --- a/libc/calls/gettimeofday.c +++ b/libc/calls/gettimeofday.c @@ -25,6 +25,8 @@ #include "libc/time/struct/timezone.h" #include "libc/time/time.h" +static typeof(sys_gettimeofday) *__gettimeofday = sys_gettimeofday; + /** * Returns system wall time in microseconds. * @@ -40,7 +42,7 @@ int gettimeofday(struct timeval *tv, struct timezone *tz) { return efault(); } if (!IsWindows() && !IsMetal()) { - ad = sys_gettimeofday(tv, tz, NULL); + ad = __gettimeofday(tv, tz, NULL); assert(ad.ax != -1); if (SupportsXnu() && ad.ax && tv) { tv->tv_sec = ad.ax; @@ -53,3 +55,14 @@ int gettimeofday(struct timeval *tv, struct timezone *tz) { return sys_gettimeofday_nt(tv, tz); } } + +static textstartup void __gettimeofday_init(void) { + void *vdso; + if ((vdso = __vdsofunc("__vdso_gettimeofday"))) { + __gettimeofday = vdso; + } +} + +const void *const __gettimeofday_ctor[] initarray = { + __gettimeofday_init, +}; diff --git a/libc/calls/internal.h b/libc/calls/internal.h index 283b6f9fb..12aa6e8af 100644 --- a/libc/calls/internal.h +++ b/libc/calls/internal.h @@ -245,6 +245,8 @@ void sys_exit(int) hidden; ╚────────────────────────────────────────────────────────────────────────────│*/ void __onfork(void) hidden; +void *__vdsofunc(const char *) hidden; +void *__get_clock_gettime(void) hidden; i32 __fixupnewfd(i32, i32) hidden; void __restore_rt() hidden; int sys_utimensat_xnu(int, const char *, const struct timespec *, int) hidden; diff --git a/libc/calls/now.c b/libc/calls/now.c index 2e5cf754a..a7ec88b88 100644 --- a/libc/calls/now.c +++ b/libc/calls/now.c @@ -31,9 +31,9 @@ #include "libc/time/time.h" static struct Now { - bool once; uint64_t k0; long double r0, cpn; + typeof(sys_clock_gettime) *clock_gettime; } g_now; static long double GetTimeSample(void) { @@ -73,22 +73,39 @@ void RefreshTime(void) { now.cpn = MeasureNanosPerCycle(); now.r0 = dtime(CLOCK_REALTIME); now.k0 = rdtsc(); - now.once = true; memcpy(&g_now, &now, sizeof(now)); } -long double ConvertTicksToNanos(uint64_t ticks) { - if (!g_now.once) RefreshTime(); - return ticks * g_now.cpn; /* pico scale */ -} - -long double nowl_sys(void) { +static long double nowl_sys(void) { return dtime(CLOCK_REALTIME); } -long double nowl_art(void) { - uint64_t ticks; - if (!g_now.once) RefreshTime(); - ticks = unsignedsubtract(rdtsc(), g_now.k0); +static long double nowl_art(void) { + uint64_t ticks = rdtsc() - g_now.k0; return g_now.r0 + (1 / 1e9L * (ticks * g_now.cpn)); } + +static long double nowl_vdso(void) { + long double secs; + struct timespec tv; + g_now.clock_gettime(CLOCK_REALTIME, &tv); + secs = tv.tv_nsec; + secs *= 1 / 1e9L; + secs += tv.tv_sec; + return secs; +} + +long double nowl_setup(void) { + uint64_t ticks; + if ((g_now.clock_gettime = __get_clock_gettime())) { + nowl = nowl_vdso; + } else if (X86_HAVE(INVTSC)) { + RefreshTime(); + nowl = nowl_art; + } else { + nowl = nowl_sys; + } + return nowl(); +} + +long double (*nowl)(void) = nowl_setup; diff --git a/libc/calls/oldbench.c b/libc/calls/oldbench.c new file mode 100644 index 000000000..ee1ab4c41 --- /dev/null +++ b/libc/calls/oldbench.c @@ -0,0 +1,83 @@ +/*-*- 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 2020 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/bits/bits.h" +#include "libc/bits/initializer.internal.h" +#include "libc/bits/safemacros.internal.h" +#include "libc/calls/calls.h" +#include "libc/calls/internal.h" +#include "libc/calls/strace.internal.h" +#include "libc/dce.h" +#include "libc/macros.internal.h" +#include "libc/nexgen32e/rdtsc.h" +#include "libc/nexgen32e/x86feature.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/clock.h" +#include "libc/time/time.h" + +static struct Now { + bool once; + uint64_t k0; + long double r0, cpn; +} g_now; + +static long double GetTimeSample(void) { + uint64_t tick1, tick2; + long double time1, time2; + sched_yield(); + time1 = dtime(CLOCK_REALTIME); + tick1 = rdtsc(); + nanosleep(&(struct timespec){0, 1000000}, NULL); + time2 = dtime(CLOCK_REALTIME); + tick2 = rdtsc(); + return (time2 - time1) * 1e9 / MAX(1, tick2 - tick1); +} + +static long double MeasureNanosPerCycle(void) { + bool tc; + int i, n; + long double avg, samp; + tc = __time_critical; + __time_critical = true; + if (IsWindows()) { + n = 30; + } else { + n = 20; + } + for (avg = 1.0L, i = 1; i < n; ++i) { + samp = GetTimeSample(); + avg += (samp - avg) / i; + } + __time_critical = tc; + STRACE("MeasureNanosPerCycle cpn*1000=%d", (long)(avg * 1000)); + return avg; +} + +static void Refresh(void) { + struct Now now; + now.cpn = MeasureNanosPerCycle(); + now.r0 = dtime(CLOCK_REALTIME); + now.k0 = rdtsc(); + now.once = true; + memcpy(&g_now, &now, sizeof(now)); +} + +long double ConvertTicksToNanos(uint64_t ticks) { + if (!g_now.once) Refresh(); + return ticks * g_now.cpn; /* pico scale */ +} diff --git a/libc/calls/pipe-nt.c b/libc/calls/pipe-nt.c index 7728acdcd..0c0db0823 100644 --- a/libc/calls/pipe-nt.c +++ b/libc/calls/pipe-nt.c @@ -23,6 +23,7 @@ #include "libc/nt/enum/fileflagandattributes.h" #include "libc/nt/ipc.h" #include "libc/nt/runtime.h" +#include "libc/sysv/consts/limits.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/errfuns.h" @@ -42,9 +43,9 @@ textwindows int sys_pipe_nt(int pipefd[2], unsigned flags) { } else { mode = kNtPipeTypeMessage | kNtPipeReadmodeMessage; } - if ((hin = CreateNamedPipe(pipename, - kNtPipeAccessInbound | kNtFileFlagOverlapped, mode, - 1, 512, 512, 0, &kNtIsInheritable)) != -1) { + if ((hin = CreateNamedPipe( + pipename, kNtPipeAccessInbound | kNtFileFlagOverlapped, mode, 1, + PIPE_BUF, PIPE_BUF, 0, &kNtIsInheritable)) != -1) { if ((hout = CreateFile(pipename, kNtGenericWrite, 0, &kNtIsInheritable, kNtOpenExisting, kNtFileFlagOverlapped, 0)) != -1) { g_fds.p[reader].kind = kFdFile; diff --git a/libc/calls/program_executable_name.c b/libc/calls/program_executable_name.c deleted file mode 100644 index 316147b28..000000000 --- a/libc/calls/program_executable_name.c +++ /dev/null @@ -1,148 +0,0 @@ -/*-*- 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/internal.h" -#include "libc/calls/strace.internal.h" -#include "libc/dce.h" -#include "libc/errno.h" -#include "libc/intrin/kprintf.h" -#include "libc/intrin/spinlock.h" -#include "libc/log/libfatal.internal.h" -#include "libc/macros.internal.h" -#include "libc/mem/alloca.h" -#include "libc/nt/runtime.h" -#include "libc/runtime/runtime.h" -#include "libc/str/path.h" -#include "libc/str/str.h" -#include "libc/str/tpenc.h" -#include "libc/str/utf16.h" -#include "libc/sysv/consts/at.h" -#include "libc/sysv/consts/auxv.h" -#include "libc/sysv/consts/ok.h" -#include "libc/sysv/consts/prot.h" - -#define SIZE 1024 -#define CTL_KERN 1 -#define KERN_PROC 14 -#define KERN_PROC_PATHNAME_FREEBSD 12 -#define KERN_PROC_PATHNAME_NETBSD 5 - -char program_executable_name[SIZE]; - -static textwindows bool GetNtExePath(char exe[SIZE]) { - bool32 rc; - uint64_t w; - wint_t x, y; - uint32_t i, j; - char16_t p[PATH_MAX + 1]; - p[0] = 0; - rc = GetModuleFileName(0, p, ARRAYLEN(p)); - NTTRACE("GetModuleFileName(0, [%#hs]) → %hhhd", p, rc); - if (!rc) return false; - j = 0; - if (p[0] != '\\' || p[1] != '\\' || p[2] != '?' || p[3] != '\\') { - exe[j++] = '/'; - exe[j++] = '/'; - exe[j++] = '?'; - exe[j++] = '/'; - } - for (i = 0; (x = p[i++] & 0xffff);) { - if (!IsUcs2(x)) { - y = p[i++] & 0xffff; - x = MergeUtf16(x, y); - } - if (x == '\\') x = '/'; - w = tpenc(x); - do { - exe[j] = w; - if (++j == SIZE) { - return false; - } - } while ((w >>= 8)); - } - exe[j] = 0; - return true; -} - -static void ReadProgramExecutableName(char exe[SIZE], char *argv0, - uintptr_t *auxv) { - int e; - size_t m; - ssize_t n; - int cmd[4]; - char *p, *t; - e = errno; - if (!IsWindows() || !GetNtExePath(exe)) { - for (p = 0; *auxv; auxv += 2) { - if (*auxv == AT_EXECFN) { - p = (char *)auxv[1]; - break; - } - } - n = 0; - if (!p) p = argv0; - if (p) { - if (!_isabspath(p)) { - if (getcwd(exe, SIZE - 1)) { - n = strlen(exe); - exe[n++] = '/'; - } - } - for (; *p; ++p) { - if (n + 1 < SIZE) { - exe[n++] = *p; - } - } - } - exe[n] = 0; - } - errno = e; -} - -/** - * Returns absolute path of executable. - * - * This variable is initialized automatically at startup. The path is - * basically `argv[0]` except some extra vetting is done to provide - * stronger assurance that the path can be counted upon to exist. - * - * For example, if your program is executed as a relative path and then - * your program calls `chdir()`, then `argv[0]` will be incorrect; but - * `program_executable_name` will work, because it prefixed `getcwd()` - * early in the initialization phase. - * - * @see GetInterpreterExecutableName() - * @see program_invocation_short_name - * @see program_invocation_name - */ -char *GetProgramExecutableName(void) { - static bool once; - if (!once) { - ReadProgramExecutableName(program_executable_name, __argv[0], __auxv); - once = true; - } - return program_executable_name; -} - -// try our best to memoize it before a chdir() happens -const void *const program_executable_name_init_ctor[] initarray = { - GetProgramExecutableName, -}; diff --git a/libc/calls/sigaddset.c b/libc/calls/sigaddset.c index e730d90b7..260b80304 100644 --- a/libc/calls/sigaddset.c +++ b/libc/calls/sigaddset.c @@ -22,13 +22,14 @@ /** * Adds signal to set. * - * @return true, false, or -1 w/ errno + * @return 0 on success, or -1 w/ errno + * @raises EINVAL if `1 ≤ sig ≤ NSIG` isn't the case * @asyncsignalsafe */ int sigaddset(sigset_t *set, int sig) { - unsigned i = sig - 1; - if (i < sizeof(set->__bits) * 8) { - set->__bits[i >> 6] |= 1ull << (i & 63); + _Static_assert(sizeof(set->__bits[0]) * CHAR_BIT == 64, ""); + if (1 <= sig && sig <= NSIG) { + set->__bits[(sig - 1) >> 6] |= 1ull << ((sig - 1) & 63); return 0; } else { return einval(); diff --git a/libc/calls/sigdelset.c b/libc/calls/sigdelset.c index 856573808..84ca3129b 100644 --- a/libc/calls/sigdelset.c +++ b/libc/calls/sigdelset.c @@ -23,12 +23,13 @@ * Removes signal from set. * * @return 0 on success, or -1 w/ errno + * @raises EINVAL if `1 ≤ sig ≤ NSIG` isn't the case * @asyncsignalsafe */ int sigdelset(sigset_t *set, int sig) { - unsigned i = sig - 1; - if (i < sizeof(set->__bits) * 8) { - set->__bits[i >> 6] &= ~(1ull << (i & 63)); + _Static_assert(sizeof(set->__bits[0]) * CHAR_BIT == 64, ""); + if (1 <= sig && sig <= NSIG) { + set->__bits[(sig - 1) >> 6] &= ~(1ull << ((sig - 1) & 63)); return 0; } else { return einval(); diff --git a/libc/calls/sigismember.c b/libc/calls/sigismember.c index bbb57fe73..246ac55db 100644 --- a/libc/calls/sigismember.c +++ b/libc/calls/sigismember.c @@ -22,14 +22,15 @@ /** * Returns true if signal is member of set. * - * @return true, false, or -1 w/ errno + * @return 1 if set, 0 if not set, or -1 w/ errno + * @raises EINVAL if `1 ≤ sig ≤ NSIG` isn't the case * @asyncsignalsafe * @vforksafe */ int sigismember(const sigset_t *set, int sig) { - unsigned i = sig - 1; - if (i < sizeof(set->__bits) * 8) { - return (set->__bits[i >> 6] >> (i & 63)) & 1; + _Static_assert(sizeof(set->__bits[0]) * CHAR_BIT == 64, ""); + if (1 <= sig && sig <= NSIG) { + return !!(set->__bits[(sig - 1) >> 6] & (1ull << ((sig - 1) & 63))); } else { return einval(); } diff --git a/libc/calls/struct/stat.h b/libc/calls/struct/stat.h index fc39ec4a5..b744420cd 100644 --- a/libc/calls/struct/stat.h +++ b/libc/calls/struct/stat.h @@ -10,7 +10,7 @@ struct stat { /* cosmo abi */ uint32_t st_mode; /* 24: octal file mask thing */ uint32_t st_uid; /* 28: user id of owner */ uint32_t st_gid; /* group id of owning group */ - uint32_t st_flags; /* flags (bsd-only) */ + uint32_t st_flags; /* nt/xnu/bsd-only */ uint64_t st_rdev; /* id of device if a special file */ int64_t st_size; /* bytes in file */ int64_t st_blksize; /* preferred chunking for underlying filesystem */ @@ -19,7 +19,7 @@ struct stat { /* cosmo abi */ struct timespec st_mtim; /* modified time */ struct timespec st_ctim; /* complicated time */ struct timespec st_birthtim; - uint64_t st_gen; + uint64_t st_gen; /* xnu/bsd only */ }; #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/calls/vdsofunc.greg.c b/libc/calls/vdsofunc.greg.c new file mode 100644 index 000000000..843b45822 --- /dev/null +++ b/libc/calls/vdsofunc.greg.c @@ -0,0 +1,94 @@ +/*-*- 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 2022 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/bits/bits.h" +#include "libc/calls/calls.h" +#include "libc/calls/internal.h" +#include "libc/elf/scalar.h" +#include "libc/elf/struct/ehdr.h" +#include "libc/elf/struct/shdr.h" +#include "libc/elf/struct/sym.h" +#include "libc/log/libfatal.internal.h" +#include "libc/runtime/runtime.h" +#include "libc/sysv/consts/auxv.h" + +#define LAZY_RHEL7_RELOCATION 0xfffff + +#define GetStr(tab, rva) ((char *)(tab) + (rva)) +#define GetSection(e, s) ((void *)((intptr_t)(e) + (size_t)(s)->sh_offset)) +#define GetShstrtab(e) GetSection(e, GetShdr(e, (e)->e_shstrndx)) +#define GetSectionName(e, s) GetStr(GetShstrtab(e), (s)->sh_name) +#define GetPhdr(e, i) \ + ((Elf64_Phdr *)((intptr_t)(e) + (e)->e_phoff + \ + (size_t)(e)->e_phentsize * (i))) +#define GetShdr(e, i) \ + ((Elf64_Shdr *)((intptr_t)(e) + (e)->e_shoff + \ + (size_t)(e)->e_shentsize * (i))) + +static char *GetDynamicStringTable(Elf64_Ehdr *e, size_t *n) { + char *name; + Elf64_Half i; + Elf64_Shdr *shdr; + for (i = 0; i < e->e_shnum; ++i) { + shdr = GetShdr(e, i); + name = GetSectionName(e, GetShdr(e, i)); + if (shdr->sh_type == SHT_STRTAB) { + name = GetSectionName(e, GetShdr(e, i)); + if (name && READ64LE(name) == READ64LE(".dynstr")) { + if (n) *n = shdr->sh_size; + return GetSection(e, shdr); + } + } + } + return 0; +} + +static Elf64_Sym *GetDynamicSymbolTable(Elf64_Ehdr *e, Elf64_Xword *n) { + Elf64_Half i; + Elf64_Shdr *shdr; + for (i = e->e_shnum; i > 0; --i) { + shdr = GetShdr(e, i - 1); + if (shdr->sh_type == SHT_DYNSYM) { + if (shdr->sh_entsize != sizeof(Elf64_Sym)) continue; + if (n) *n = shdr->sh_size / shdr->sh_entsize; + return GetSection(e, shdr); + } + } + return 0; +} + +/** + * Returns Linux Kernel Virtual Dynamic Shared Object function address. + */ +void *__vdsofunc(const char *name) { + size_t m; + char *names; + Elf64_Ehdr *ehdr; + Elf64_Xword i, n; + Elf64_Sym *symtab, *sym; + if ((ehdr = (Elf64_Ehdr *)getauxval(AT_SYSINFO_EHDR)) && + (names = GetDynamicStringTable(ehdr, &m)) && + (symtab = GetDynamicSymbolTable(ehdr, &n))) { + for (i = 0; i < n; ++i) { + if (!__strcmp(names + symtab[i].st_name, name)) { + return (char *)ehdr + (symtab[i].st_value & LAZY_RHEL7_RELOCATION); + } + } + } + return 0; +} diff --git a/libc/elf/elf.h b/libc/elf/elf.h index ed9abee8f..9df8827ac 100644 --- a/libc/elf/elf.h +++ b/libc/elf/elf.h @@ -26,6 +26,8 @@ void GetElfVirtualAddressRange(const Elf64_Ehdr *, size_t, intptr_t *, intptr_t *); char *GetElfString(const Elf64_Ehdr *, size_t, const char *, Elf64_Word); const char *GetElfSectionName(const Elf64_Ehdr *, size_t, Elf64_Shdr *); +Elf64_Sym *GetElfDynSymbolTable(const Elf64_Ehdr *, size_t, Elf64_Xword *); +char *GetElfDynStringTable(const Elf64_Ehdr *, size_t); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/elf/getelfdynstringtable.c b/libc/elf/getelfdynstringtable.c new file mode 100644 index 000000000..6ed29f0a9 --- /dev/null +++ b/libc/elf/getelfdynstringtable.c @@ -0,0 +1,40 @@ +/*-*- 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 2020 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/elf/def.h" +#include "libc/elf/elf.h" +#include "libc/str/str.h" + +char *GetElfDynStringTable(const Elf64_Ehdr *elf, size_t mapsize) { + char *name; + Elf64_Half i; + Elf64_Shdr *shdr; + if (elf->e_shentsize) { + for (i = 0; i < elf->e_shnum; ++i) { + shdr = GetElfSectionHeaderAddress(elf, mapsize, i); + if (shdr->sh_type == SHT_STRTAB) { + name = GetElfSectionName(elf, mapsize, + GetElfSectionHeaderAddress(elf, mapsize, i)); + if (name && !strcmp(name, ".dynstr")) { + return GetElfSectionAddress(elf, mapsize, shdr); + } + } + } + } + return NULL; +} diff --git a/libc/elf/getelfdynsymboltable.c b/libc/elf/getelfdynsymboltable.c new file mode 100644 index 000000000..09256fda6 --- /dev/null +++ b/libc/elf/getelfdynsymboltable.c @@ -0,0 +1,37 @@ +/*-*- 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 2020 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/elf/def.h" +#include "libc/elf/elf.h" + +Elf64_Sym *GetElfDynSymbolTable(const Elf64_Ehdr *elf, size_t mapsize, + Elf64_Xword *out_count) { + Elf64_Half i; + Elf64_Shdr *shdr; + if (elf->e_shentsize) { + for (i = elf->e_shnum; i > 0; --i) { + shdr = GetElfSectionHeaderAddress(elf, mapsize, i - 1); + if (shdr->sh_type == SHT_DYNSYM) { + if (shdr->sh_entsize != sizeof(Elf64_Sym)) continue; + if (out_count) *out_count = shdr->sh_size / shdr->sh_entsize; + return GetElfSectionAddress(elf, mapsize, shdr); + } + } + } + return NULL; +} diff --git a/libc/fmt/strerror_wr.greg.c b/libc/fmt/strerror_wr.greg.c index dc3d2fc77..93b8616c4 100644 --- a/libc/fmt/strerror_wr.greg.c +++ b/libc/fmt/strerror_wr.greg.c @@ -16,7 +16,6 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#define ShouldUseMsabiAttribute() 1 #include "libc/bits/safemacros.internal.h" #include "libc/dce.h" #include "libc/fmt/fmt.h" @@ -44,18 +43,18 @@ int strerror_wr(int err, uint32_t winerr, char *buf, size_t size) { for (; (c = *sym++); --size) if (size > 1) *buf++ = c; if (size) *buf = 0; - } else if (!IsWindows()) { - ksnprintf(buf, size, "%s[%d][%s]", sym, err, msg); + } else if (!IsWindows() || err == winerr || !winerr) { + ksnprintf(buf, size, "%s:%d:%s", sym, err, msg); } else { - if ((n = __imp_FormatMessageW( + if ((n = FormatMessage( kNtFormatMessageFromSystem | kNtFormatMessageIgnoreInserts, 0, winerr, MAKELANGID(kNtLangNeutral, kNtSublangDefault), winmsg, ARRAYLEN(winmsg), 0))) { while ((n && winmsg[n - 1] <= ' ') || winmsg[n - 1] == '.') --n; - ksnprintf(buf, size, "%s[%d][%s][%.*hs][%d]", sym, err, msg, n, winmsg, - winerr); + ksnprintf(buf, size, "%s:%d:%s:%d:%.*hs", sym, err, msg, winerr, n, + winmsg); } else { - ksnprintf(buf, size, "%s[%d][%s][%d]", sym, err, msg, winerr); + ksnprintf(buf, size, "%s:%d:%s:%d", sym, err, msg, winerr); } } return 0; diff --git a/libc/integral/normalize.inc b/libc/integral/normalize.inc index 335a24b9b..a268369b6 100644 --- a/libc/integral/normalize.inc +++ b/libc/integral/normalize.inc @@ -71,7 +71,7 @@ #define CACHELINE 0x40 /* nexgen32e */ #define CHAR_BIT 8 /* b/c von neumann */ #define ARG_MAX 0x8000 /* b/c windows */ -#define PATH_MAX 512 /* b/c bloat */ +#define PATH_MAX 511 /* b/c bloat */ #define NAME_MAX 63 /* b/c dns */ #define CHILD_MAX 25 /* only if malloc isn't linked */ #define OPEN_MAX 16 /* only if malloc isn't linked */ diff --git a/libc/runtime/getinterpreterexecutablename.c b/libc/runtime/getinterpreterexecutablename.c index fd4331b8a..2f3aa6c24 100644 --- a/libc/runtime/getinterpreterexecutablename.c +++ b/libc/runtime/getinterpreterexecutablename.c @@ -62,17 +62,17 @@ char *GetInterpreterExecutableName(char *p, size_t n) { } else if ((rc = sys_readlinkat(AT_FDCWD, "/proc/curproc/file", p, n - 1)) > 0) { errno = e; - p[n] = 0; + p[rc] = 0; return p; } else if (IsFreebsd() || IsNetbsd()) { - cmd[0] = 1 /* CTL_KERN */; - cmd[1] = 14 /* KERN_PROC */; - if (IsFreebsd()) { - cmd[2] = 12 /* KERN_PROC_PATHNAME */; - } else { - cmd[2] = 5 /* KERN_PROC_PATHNAME */; - } - cmd[3] = -1; /* current process */ + cmd[0] = 1; // CTL_KERN + cmd[1] = 14; // KERN_PROC + if (IsFreebsd()) { // + cmd[2] = 12; // KERN_PROC_PATHNAME + } else { // + cmd[2] = 5; // KERN_PROC_PATHNAME + } // + cmd[3] = -1; // current process if (sysctl(cmd, ARRAYLEN(cmd), p, &n, 0, 0) != -1) { errno = e; return p; diff --git a/libc/runtime/printargs.greg.c b/libc/runtime/printargs.greg.c index 322de9e2f..d5a83c32e 100644 --- a/libc/runtime/printargs.greg.c +++ b/libc/runtime/printargs.greg.c @@ -324,8 +324,8 @@ textstartup void __printargs(const char *prologue) { PRINT(" ☼ %s = %#s", "kTmpPath", kTmpPath); PRINT(" ☼ %s = %#s", "kNtSystemDirectory", kNtSystemDirectory); PRINT(" ☼ %s = %#s", "kNtWindowsDirectory", kNtWindowsDirectory); - PRINT(" ☼ %s = %#s", "program_executable_name", GetProgramExecutableName()); - PRINT(" ☼ %s = %#s", "GetInterpreterExecutableName()", + PRINT(" ☼ %s = %#s", "GetProgramExecutableName", GetProgramExecutableName()); + PRINT(" ☼ %s = %#s", "GetInterpreterExecutableName", GetInterpreterExecutableName(path, sizeof(path))); PRINT(" ☼ %s = %p", "RSP", __builtin_frame_address(0)); PRINT(" ☼ %s = %p", "GetStackAddr()", GetStackAddr(0)); diff --git a/libc/runtime/runtime.h b/libc/runtime/runtime.h index 2da6ea692..82142b687 100644 --- a/libc/runtime/runtime.h +++ b/libc/runtime/runtime.h @@ -14,7 +14,6 @@ extern char **__argv; /* CRT */ extern char **__envp; /* CRT */ extern unsigned long *__auxv; /* CRT */ extern intptr_t __oldstack; /* CRT */ -extern char program_executable_name[]; /* RII */ extern char *program_invocation_name; /* RII */ extern char *program_invocation_short_name; /* RII */ extern int g_ftrace; /* CRT */ diff --git a/libc/runtime/winmain.greg.c b/libc/runtime/winmain.greg.c index 0ad345803..ea72891ae 100644 --- a/libc/runtime/winmain.greg.c +++ b/libc/runtime/winmain.greg.c @@ -219,9 +219,6 @@ __msabi static textwindows wontreturn void WinMainNew(const char16_t *cmdline) { } } env16 = GetEnvironmentStrings(); - for (char16_t *e = env16; *e; e += StrLen16(e) + 1) { - NTTRACE("GetEnvironmentStrings() → %!#hs", e); - } NTTRACE("WinMainNew() loading environment"); GetDosEnviron(env16, wa->envblock, ARRAYLEN(wa->envblock) - 8, wa->envp, ARRAYLEN(wa->envp) - 1); diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh index febb4bb42..ec140561e 100755 --- a/libc/sysv/consts.sh +++ b/libc/sysv/consts.sh @@ -436,10 +436,10 @@ syscon ioctl TIOCINQ 0x541b 0x4004667f 0x4004667f 0x4004667f 0x4004667f # group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary syscon at AT_FDCWD -100 -2 -100 -100 -100 -100 # faked nt syscon at AT_SYMLINK_NOFOLLOW 0x0100 0x20 0x0200 2 0x200 0x0100 # faked nt +syscon at AT_SYMLINK_FOLLOW 0x0400 0x40 0x0400 4 0x400 0 # see linkat(2) syscon at AT_REMOVEDIR 0x0200 0x80 0x0800 8 0x800 0x0200 # faked nt syscon at AT_EACCESS 0x0200 0x10 0x0100 1 0x100 0 syscon at AT_EMPTY_PATH 0x1000 0 0 0 0 0 # linux 2.6.39+; see unlink, O_TMPFILE, etc. -syscon at AT_SYMLINK_FOLLOW 0x0400 0x40 0x0400 4 0x400 0 # uhhh wut # memfd_create() flags # @@ -1229,6 +1229,11 @@ syscon mount MNT_NOCLUSTERR 0 0 0x40000000 0 0 0 # disable cluster syscon mount MNT_NOCLUSTERW 0 0 0x80000000 0 0 0 # disable cluster write syscon mount MNT_SNAPSHOT 0 0x40000000 0x01000000 0 0 0 # confusing +# limits +# +# group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary +syscon misc PIPE_BUF 4096 512 512 512 512 4096 # bsd consensus + # unmount() flags # a.k.a. umount2() on linux # @@ -3055,7 +3060,6 @@ syscon misc NGREG 23 0 0 0 0 0 syscon misc NOGROUP -1 0xffff 0xffff 0xffff 0xffff 0 # bsd consensus syscon misc ORDERED_QUEUE_TAG 34 0 0 0 0 0 syscon misc ORIG_RAX 15 0 0 0 0 0 -syscon misc PIPE_BUF 0x1000 0x0200 0x0200 0x0200 0x0200 0 # bsd consensus syscon misc PRE_FETCH 52 0 0 0 0 0 syscon misc QUEUE_FULL 20 0 0 0 0 0 syscon misc REASSIGN_BLOCKS 7 0 0 0 0 0 diff --git a/libc/sysv/consts/PIPE_BUF.S b/libc/sysv/consts/PIPE_BUF.S index 8a0b5b01c..2d0310f41 100644 --- a/libc/sysv/consts/PIPE_BUF.S +++ b/libc/sysv/consts/PIPE_BUF.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon misc,PIPE_BUF,0x1000,0x0200,0x0200,0x0200,0x0200,0 +.syscon misc,PIPE_BUF,4096,512,512,512,512,4096 diff --git a/test/libc/calls/access_test.c b/test/libc/calls/access_test.c index 88e101a7a..07587ab20 100644 --- a/test/libc/calls/access_test.c +++ b/test/libc/calls/access_test.c @@ -72,5 +72,5 @@ TEST(access, testRequestWriteOnReadOnly_returnsEaccess) { } TEST(access, runThisExecutable) { - ASSERT_SYS(0, 0, access(program_executable_name, R_OK | X_OK)); + ASSERT_SYS(0, 0, access(GetProgramExecutableName(), R_OK | X_OK)); } diff --git a/libc/calls/nowl.S b/test/libc/calls/clock_gettime_test.c similarity index 71% rename from libc/calls/nowl.S rename to test/libc/calls/clock_gettime_test.c index 9c2d8bad0..e67d61473 100644 --- a/libc/calls/nowl.S +++ b/test/libc/calls/clock_gettime_test.c @@ -1,7 +1,7 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ +/*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ Copyright 2022 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 │ @@ -16,22 +16,14 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/nexgen32e/x86feature.h" -#include "libc/macros.internal.h" +#include "libc/calls/calls.h" +#include "libc/calls/struct/timespec.h" +#include "libc/sysv/consts/clock.h" +#include "libc/testlib/ezbench.h" +#include "libc/testlib/testlib.h" -// Returns timestamp without needing system calls. -// -// @return seconds since unix epoch in %st0 -// @note uses microsecond scale fallback on k8 or vm - .initbss 202,_init_nowl -nowl: .quad 0 - .endobj nowl,globl - .previous - - .init.start 202,_init_nowl - ezlea nowl_sys,ax - ezlea nowl_art,cx - testb X86_HAVE(INVTSC)+kCpuids(%rip) - cmovnz %rcx,%rax - stosq - .init.end 202,_init_nowl +BENCH(clock_gettime, bench) { + struct timespec ts; + EZBENCH2("nowl", donothing, nowl()); + EZBENCH2("clock_gettime", donothing, clock_gettime(CLOCK_REALTIME, &ts)); +} diff --git a/test/libc/calls/dup_test.c b/test/libc/calls/dup_test.c index 1f7ff126a..5c8734786 100644 --- a/test/libc/calls/dup_test.c +++ b/test/libc/calls/dup_test.c @@ -51,8 +51,8 @@ TEST(dup, clearsCloexecFlag) { ASSERT_NE(-1, (ws = xspawn(0))); if (ws == -2) { dup2(3, 0); - execv(program_executable_name, - (char *const[]){program_executable_name, "boop", 0}); + execv(GetProgramExecutableName(), + (char *const[]){GetProgramExecutableName(), "boop", 0}); _exit(127); } ASSERT_EQ(72, WEXITSTATUS(ws)); diff --git a/test/libc/log/backtrace_test.c b/test/libc/log/backtrace_test.c index eb8ab23cd..e283d19cc 100644 --- a/test/libc/log/backtrace_test.c +++ b/test/libc/log/backtrace_test.c @@ -177,8 +177,8 @@ TEST(ShowCrashReports, testMemoryLeakCrash) { if (!pid) { dup2(fds[1], 1); dup2(fds[1], 2); - execv(program_executable_name, - (char *const[]){program_executable_name, "6", 0}); + execv(GetProgramExecutableName(), + (char *const[]){GetProgramExecutableName(), "6", 0}); _exit(127); } close(fds[1]); @@ -255,8 +255,8 @@ TEST(ShowCrashReports, testStackOverrunCrash) { if (!pid) { dup2(fds[1], 1); dup2(fds[1], 2); - execv(program_executable_name, - (char *const[]){program_executable_name, "5", 0}); + execv(GetProgramExecutableName(), + (char *const[]){GetProgramExecutableName(), "5", 0}); _exit(127); } close(fds[1]); @@ -364,8 +364,8 @@ TEST(ShowCrashReports, testDivideByZero) { if (!pid) { dup2(fds[1], 1); dup2(fds[1], 2); - execv(program_executable_name, - (char *const[]){program_executable_name, "1", 0}); + execv(GetProgramExecutableName(), + (char *const[]){GetProgramExecutableName(), "1", 0}); _exit(127); } close(fds[1]); @@ -486,8 +486,8 @@ TEST(ShowCrashReports, testBssOverrunCrash) { if (!pid) { dup2(fds[1], 1); dup2(fds[1], 2); - execv(program_executable_name, - (char *const[]){program_executable_name, "2", 0}); + execv(GetProgramExecutableName(), + (char *const[]){GetProgramExecutableName(), "2", 0}); _exit(127); } close(fds[1]); @@ -565,8 +565,8 @@ TEST(ShowCrashReports, testNpeCrash) { if (!pid) { dup2(fds[1], 1); dup2(fds[1], 2); - execv(program_executable_name, - (char *const[]){program_executable_name, "7", 0}); + execv(GetProgramExecutableName(), + (char *const[]){GetProgramExecutableName(), "7", 0}); _exit(127); } close(fds[1]); @@ -625,8 +625,8 @@ TEST(ShowCrashReports, testDataOverrunCrash) { if (!pid) { dup2(fds[1], 1); dup2(fds[1], 2); - execv(program_executable_name, - (char *const[]){program_executable_name, "4", 0}); + execv(GetProgramExecutableName(), + (char *const[]){GetProgramExecutableName(), "4", 0}); _exit(127); } close(fds[1]); @@ -680,8 +680,8 @@ TEST(ShowCrashReports, testNpeCrashAfterFinalize) { if (!pid) { dup2(fds[1], 1); dup2(fds[1], 2); - execv(program_executable_name, - (char *const[]){program_executable_name, "8", 0}); + execv(GetProgramExecutableName(), + (char *const[]){GetProgramExecutableName(), "8", 0}); _exit(127); } close(fds[1]); diff --git a/test/libc/mem/test.mk b/test/libc/mem/test.mk index 63b50a2d4..cdb4dc420 100644 --- a/test/libc/mem/test.mk +++ b/test/libc/mem/test.mk @@ -4,6 +4,7 @@ PKGS += TEST_LIBC_MEM TEST_LIBC_MEM_FILES := $(wildcard test/libc/mem/*) +TEST_LIBC_MEM_SRCS = $(TEST_LIBC_MEM_SRCS_C) $(TEST_LIBC_MEM_SRCS_CC) TEST_LIBC_MEM_SRCS_C = $(filter %_test.c,$(TEST_LIBC_MEM_FILES)) TEST_LIBC_MEM_SRCS_CC = $(filter %_test.cc,$(TEST_LIBC_MEM_FILES)) @@ -12,7 +13,7 @@ TEST_LIBC_MEM_OBJS = \ $(TEST_LIBC_MEM_SRCS_CC:%.cc=o/$(MODE)/%.o) TEST_LIBC_MEM_COMS = \ - $(TEST_LIBC_MEM_SRCS_C:%.c=o/$(MODE)/%.com) \ + $(TEST_LIBC_MEM_SRCS_C:%.c=o/$(MODE)/%.com) \ $(TEST_LIBC_MEM_SRCS_CC:%.cc=o/$(MODE)/%.com) TEST_LIBC_MEM_BINS = \ @@ -20,7 +21,7 @@ TEST_LIBC_MEM_BINS = \ $(TEST_LIBC_MEM_COMS:%=%.dbg) TEST_LIBC_MEM_TESTS = \ - $(TEST_LIBC_MEM_SRCS_C:%.c=o/$(MODE)/%.com.ok) \ + $(TEST_LIBC_MEM_SRCS_C:%.c=o/$(MODE)/%.com.ok) \ $(TEST_LIBC_MEM_SRCS_CC:%.cc=o/$(MODE)/%.com.ok) TEST_LIBC_MEM_CHECKS = \ diff --git a/third_party/linenoise/linenoise.c b/third_party/linenoise/linenoise.c index e091faa90..964ed1e24 100644 --- a/third_party/linenoise/linenoise.c +++ b/third_party/linenoise/linenoise.c @@ -1810,6 +1810,7 @@ struct linenoiseState *linenoiseBegin(const char *prompt, int ifd, int ofd) { } void linenoiseReset(struct linenoiseState *l) { + l->buf[0] = 0; l->dirty = true; l->final = 0; l->hindex = 0; diff --git a/third_party/lua/llimits.h b/third_party/lua/llimits.h index 941ea9219..03c2fd812 100644 --- a/third_party/lua/llimits.h +++ b/third_party/lua/llimits.h @@ -1,6 +1,7 @@ #ifndef llimits_h #define llimits_h +#include "libc/limits.h" #include "libc/math.h" #include "third_party/lua/lua.h" diff --git a/third_party/mbedtls/test/lib.c b/third_party/mbedtls/test/lib.c index a95392471..2d6532367 100644 --- a/third_party/mbedtls/test/lib.c +++ b/third_party/mbedtls/test/lib.c @@ -1011,7 +1011,7 @@ int execute_tests(int argc, const char **argv, const char *default_filename) { file = fopen(test_filename, "r"); if (file == NULL) { WRITE("%s (%s) failed to open test file: %s %m\n", - program_invocation_short_name, program_executable_name, + program_invocation_short_name, GetProgramExecutableName(), test_filename); if (outcome_file != NULL) fclose(outcome_file); return 1; diff --git a/third_party/python/Modules/getpath.c b/third_party/python/Modules/getpath.c index d45728be0..59178a01c 100644 --- a/third_party/python/Modules/getpath.c +++ b/third_party/python/Modules/getpath.c @@ -663,7 +663,7 @@ Py_GetProgramFullPath(void) { static bool once; if (_cmpxchg(&once, false, true)) { - progpath = utf8toutf32(program_executable_name, -1, 0); + progpath = utf8toutf32(GetProgramExecutableName(), -1, 0); __cxa_atexit(free, progpath, 0); } return progpath; diff --git a/tool/decode/dumpvdso.c b/tool/decode/dumpvdso.c new file mode 100644 index 000000000..e0f64fd46 --- /dev/null +++ b/tool/decode/dumpvdso.c @@ -0,0 +1,32 @@ +/*-*- 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 2022 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/calls/calls.h" +#include "libc/elf/struct/ehdr.h" +#include "libc/runtime/runtime.h" +#include "libc/sysv/consts/auxv.h" + +noasan int main(int argc, char *argv[]) { + int i = 0; + Elf64_Ehdr *ehdr = (Elf64_Ehdr *)getauxval(AT_SYSINFO_EHDR); + if (isatty(1)) exit(1); + for (;;) { + write(1, ((char *)ehdr) + i++, 1); + } + return 0; +} diff --git a/tool/decode/elf.c b/tool/decode/elf.c index 4d9a5c4a3..4a6a4b6e3 100644 --- a/tool/decode/elf.c +++ b/tool/decode/elf.c @@ -27,8 +27,10 @@ #include "libc/log/check.h" #include "libc/log/log.h" #include "libc/macros.internal.h" +#include "libc/runtime/runtime.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" +#include "libc/sysv/consts/auxv.h" #include "libc/sysv/consts/map.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/prot.h" @@ -231,6 +233,21 @@ static void printelfsymboltable(void) { } } +static void printelfdynsymboltable(void) { + size_t i, symcount = 0; + Elf64_Sym *symtab = GetElfDynSymbolTable(elf, st->st_size, &symcount); + char *strtab = GetElfDynStringTable(elf, st->st_size); + char *shstrtab = GetElfSectionNameStringTable(elf, st->st_size); + if (symtab && strtab) { + printf("\n\n"); + printf("\t.org\t%#x\n", (intptr_t)symtab - (intptr_t)elf); + for (i = 0; i < symcount; ++i) { + printf(".Lsym%d:\n", i); + printelfsymbol(&symtab[i], strtab, shstrtab); + } + } +} + static char *getelfsymbolname(const Elf64_Ehdr *elf, size_t mapsize, const char *strtab, const char *shstrtab, const Elf64_Sym *sym) { @@ -324,12 +341,14 @@ int main(int argc, char *argv[]) { fprintf(stderr, "error: not an elf executable: %'s\n", path); exit(1); } + elf = (Elf64_Ehdr *)getauxval(AT_SYSINFO_EHDR); startfile(); printelfehdr(); printelfsegmentheaders(); printelfsectionheaders(); printelfrelocations(); printelfsymboltable(); + printelfdynsymboltable(); munmap(elf, st->st_size); close(fd); return 0; diff --git a/tool/net/demo/script.lua b/tool/net/demo/script.lua new file mode 100755 index 000000000..e92b4e4dc --- /dev/null +++ b/tool/net/demo/script.lua @@ -0,0 +1,2 @@ +#!/usr/bin/redbean -i +print('hello world') diff --git a/tool/net/demo/unix-dir.lua b/tool/net/demo/unix-dir.lua new file mode 100644 index 000000000..c06008d74 --- /dev/null +++ b/tool/net/demo/unix-dir.lua @@ -0,0 +1,93 @@ +Write('\r\n') +Write('redbean\r\n') +Write('\r\n') + +Write('

UNIX Directory Stream Demo

\r\n') +Write('\r\n') +Write('\r\n') +Write('\r\n') +Write('\r\n') + +dir = '.' +for name, kind, ino, off in assert(unix.opendir(dir)) do + Write('\r\n') + + Write('
name\r\n') +Write('type\r\n') +Write('ino\r\n') +Write('off\r\n') +Write('size\r\n') +Write('blocks\r\n') +Write('mode\r\n') +Write('uid\r\n') +Write('gid\r\n') +Write('dev\r\n') +Write('rdev\r\n') +Write('nlink\r\n') +Write('blksize\r\n') +Write('gen\r\n') +Write('flags\r\n') +Write('birthtim\r\n') +Write('mtim\r\n') +Write('atim\r\n') +Write('ctim\r\n') +Write('
') + Write(EscapeHtml(name)) + if kind == unix.DT_DIR then + Write('/') + end + Write('\r\n') + + Write('') + if kind == unix.DT_REG then Write('DT_REG') + elseif kind == unix.DT_DIR then Write('DT_DIR') + elseif kind == unix.DT_FIFO then Write('DT_FIFO') + elseif kind == unix.DT_CHR then Write('DT_CHR') + elseif kind == unix.DT_BLK then Write('DT_BLK') + elseif kind == unix.DT_LNK then Write('DT_LNK') + elseif kind == unix.DT_SOCK then Write('DT_SOCK') + else Write('DT_UNKNOWN') + end + Write('\r\n') + + Write('%d\r\n' % {ino}) + Write('%d\r\n' % {off}) + + st,err = unix.stat(dir..'/'..name, unix.AT_SYMLINK_NOFOLLOW) + if st then + + Write('%d\r\n' % {st:size()}) + Write('%d\r\n' % {st:blocks()}) + Write('%.7o\r\n' % {st:mode()}) + Write('%d\r\n' % {st:uid()}) + Write('%d\r\n' % {st:gid()}) + Write('%d\r\n' % {st:dev()}) + Write('%d,%d\r\n' % {unix.major(st:rdev()), unix.minor(st:rdev())}) + Write('%d\r\n' % {st:nlink()}) + Write('%d\r\n' % {st:blksize()}) + Write('%d\r\n' % {st:gen()}) + Write('%#x\r\n' % {st:flags()}) + + function WriteTime(unixsec,nanos) + year,mon,mday,hour,min,sec,gmtoffsec = unix.localtime(unixsec) + Write('%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.9d%+.2d%.2d\r\n' % { + year, mon, mday, hour, min, sec, nanos, + gmtoffsec / (60 * 60), math.abs(gmtoffsec) % 60}) + end + + WriteTime(st:birthtim()) + WriteTime(st:mtim()) + WriteTime(st:atim()) + WriteTime(st:ctim()) + + else + Write('%s\r\n' % {err}) + end + +end +Write('
\r\n') diff --git a/tool/net/demo/unix-info.lua b/tool/net/demo/unix-info.lua index b772d069b..42cc5d26c 100644 --- a/tool/net/demo/unix-info.lua +++ b/tool/net/demo/unix-info.lua @@ -26,7 +26,7 @@ sid, errno = unix.getsid(0) if sid then Write('
%d\r\n' % {sid}) else - Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
%s\r\n' % {tostring(errno)}) end Write('
unix.gethostname()\r\n') @@ -54,7 +54,7 @@ function PrintResourceLimit(name, id) end Write('\r\n') else - Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) end end PrintResourceLimit('RLIMIT_AS', unix.RLIMIT_AS) @@ -77,13 +77,13 @@ if ifs then Write('%s %s/%d
\r\n' % {EscapeHtml(ifs[i].name), FormatIp(ifs[i].ip), cidr}) end else - Write('%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('%s\r\n' % {EscapeHtml(tostring(errno))}) end enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_DEBUG) Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_DEBUG)\r\n') if errno then - Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
%s\r\n' % {enabled}) end @@ -91,7 +91,7 @@ end enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_ACCEPTCONN) Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_ACCEPTCONN)\r\n') if errno then - Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
%s\r\n' % {enabled}) end @@ -99,7 +99,7 @@ end enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_REUSEADDR) Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_REUSEADDR)\r\n') if errno then - Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
%s\r\n' % {enabled}) end @@ -107,7 +107,7 @@ end enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_REUSEPORT) Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_REUSEPORT)\r\n') if errno then - Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
%s\r\n' % {enabled}) end @@ -115,7 +115,7 @@ end enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_KEEPALIVE) Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_KEEPALIVE)\r\n') if errno then - Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
%s\r\n' % {enabled}) end @@ -123,7 +123,7 @@ end enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_NODELAY) Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_NODELAY)\r\n') if errno then - Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
%s\r\n' % {enabled}) end @@ -131,7 +131,7 @@ end secs, errno, micros = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_RCVTIMEO) Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_RCVTIMEO)\r\n') if errno then - Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
%d sec %d µs\r\n' % {secs, micros}) end @@ -139,7 +139,7 @@ end secs, errno, micros = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_SNDTIMEO) Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_SNDTIMEO)\r\n') if errno then - Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
%d sec %d µs\r\n' % {secs, micros}) end @@ -147,7 +147,7 @@ end enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_DONTROUTE) Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_DONTROUTE)\r\n') if errno then - Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
%s\r\n' % {enabled}) end @@ -155,7 +155,7 @@ end bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_SNDBUF) Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_SNDBUF)\r\n') if errno then - Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
%d\r\n' % {bytes}) end @@ -163,7 +163,7 @@ end bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_RCVBUF) Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_RCVBUF)\r\n') if errno then - Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
%d\r\n' % {bytes}) end @@ -171,7 +171,7 @@ end bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_FASTOPEN) Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_FASTOPEN)\r\n') if errno then - Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
%d\r\n' % {bytes}) end @@ -179,7 +179,7 @@ end enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_BROADCAST) Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_BROADCAST)\r\n') if errno then - Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
%s\r\n' % {enabled}) end @@ -187,7 +187,7 @@ end enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_CORK) Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_CORK)\r\n') if errno then - Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
%s\r\n' % {enabled}) end @@ -195,7 +195,7 @@ end enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_QUICKACK) Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_QUICKACK)\r\n') if errno then - Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
%s\r\n' % {enabled}) end @@ -203,7 +203,7 @@ end enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_DEFER_ACCEPT) Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_DEFER_ACCEPT)\r\n') if errno then - Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
%s\r\n' % {enabled}) end @@ -211,7 +211,7 @@ end enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_FASTOPEN_CONNECT) Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_FASTOPEN_CONNECT)\r\n') if errno then - Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
%s\r\n' % {enabled}) end @@ -219,7 +219,7 @@ end bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_SNDLOWAT) Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_SNDLOWAT)\r\n') if errno then - Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
%d\r\n' % {bytes}) end @@ -227,7 +227,7 @@ end bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_RCVLOWAT) Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_RCVLOWAT)\r\n') if errno then - Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
%d\r\n' % {bytes}) end @@ -235,7 +235,7 @@ end bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_KEEPCNT) Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_KEEPCNT)\r\n') if errno then - Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
%d\r\n' % {bytes}) end @@ -243,7 +243,7 @@ end bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_MAXSEG) Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_MAXSEG)\r\n') if errno then - Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
%d\r\n' % {bytes}) end @@ -251,7 +251,7 @@ end bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_SYNCNT) Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_SYNCNT)\r\n') if errno then - Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
%d\r\n' % {bytes}) end @@ -259,7 +259,7 @@ end bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_NOTSENT_LOWAT) Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_NOTSENT_LOWAT)\r\n') if errno then - Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
%d\r\n' % {bytes}) end @@ -267,7 +267,7 @@ end bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_WINDOW_CLAMP) Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_WINDOW_CLAMP)\r\n') if errno then - Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
%d\r\n' % {bytes}) end @@ -275,7 +275,7 @@ end bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_KEEPIDLE) Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_KEEPIDLE)\r\n') if errno then - Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
%d\r\n' % {bytes}) end @@ -283,7 +283,7 @@ end bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_KEEPINTVL) Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_KEEPINTVL)\r\n') if errno then - Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
%d\r\n' % {bytes}) end diff --git a/tool/net/demo/unix-raise.lua b/tool/net/demo/unix-raise.lua new file mode 100755 index 000000000..acb36ca96 --- /dev/null +++ b/tool/net/demo/unix-raise.lua @@ -0,0 +1,11 @@ +#!/home/jart/bin/redbean -i +assert(unix.sigaction(unix.SIGUSR1, function(sig) + gotsigusr1 = true +end)) +gotsigusr1 = false +assert(unix.raise(unix.SIGUSR1)) +if gotsigusr1 then + print('hooray the signal was delivered') +else + print('oh no some other signal was handled') +end diff --git a/tool/net/demo/unix-rawsocket.lua b/tool/net/demo/unix-rawsocket.lua index 38be17135..f0f9aaf8f 100644 --- a/tool/net/demo/unix-rawsocket.lua +++ b/tool/net/demo/unix-rawsocket.lua @@ -1,4 +1,4 @@ -local unix = require "unix" +local unix = require 'unix' local function main() if GetMethod() ~= 'GET' and GetMethod() ~= 'HEAD' then @@ -45,7 +45,7 @@ local function main() Write(EncodeBase64(LoadAsset('/redbean.png'))) Write('">\r\n') Write('redbean unix demo\r\n') - Write(' %s\r\n' % {unix.strerrno(errno)}) + Write(' %s\r\n' % {EscapeHtml(tostring(errno))}) Write('\r\n') Write([[

@@ -63,7 +63,6 @@ local function main() unix.close(fd) return end - -- if pid is zero then we're the child -- turn into a daemon unix.umask(0) @@ -106,34 +105,6 @@ local function main() unix.write(fd, 'became an autonomous daemon reparented on your init!\r\n') unix.write(fd, '

\r\n') - unix.write(fd, '

listing of current directory

\r\n') - dir, err = unix.opendir('.') - if dir then - unix.write(fd, '\r\n') - else - unix.write(fd, '

\r\n') - unix.write(fd, 'failed: %s\r\n' % {EscapeHtml(VisualizeControlCodes(unix:strerror(err)))}) - unix.write(fd, '

\r\n') - end - -- terminate unix.close(fd) unix.exit(0) diff --git a/tool/net/demo/unix-subprocess.lua b/tool/net/demo/unix-subprocess.lua index ae387d945..5d060a42b 100644 --- a/tool/net/demo/unix-subprocess.lua +++ b/tool/net/demo/unix-subprocess.lua @@ -9,55 +9,48 @@ function main() end syscall = 'commandv' ls = assert(unix.commandv(cmd)) - if ls then - syscall = 'pipe' - reader, writer, errno = unix.pipe() - if reader then - -- oldint = assert(unix.sigaction(unix.SIGINT, unix.SIG_IGN)) - -- oldquit = assert(unix.sigaction(unix.SIGQUIT, unix.SIG_IGN)) - -- oldmask = assert(unix.sigprocmask(unix.SIG_BLOCK, (1 << (unix.SIGCHLD - 1)))) - syscall = 'fork' - child, errno = unix.fork() - if child then - if child == 0 then - unix.close(1) - unix.dup(writer) - unix.close(writer) - unix.close(reader) - -- unix.sigaction(unix.SIGINT, oldint) - -- unix.sigaction(unix.SIGQUIT, oldquit) - -- unix.sigprocmask(unix.SIG_SETMASK, oldmask) - unix.execve(ls, {ls, '-Shal'}) - unix.exit(127) + syscall = 'pipe' + reader, writer = assert(unix.pipe(unix.O_CLOEXEC)) + oldint = assert(unix.sigaction(unix.SIGINT, unix.SIG_IGN)) + oldquit = assert(unix.sigaction(unix.SIGQUIT, unix.SIG_IGN)) + oldmask = assert(unix.sigprocmask(unix.SIG_BLOCK, unix.Sigset(unix.SIGCHLD))) + syscall = 'fork' + child = assert(unix.fork()) + if child == 0 then + unix.close(0) + unix.open("/dev/null", unix.O_RDONLY) + unix.close(1) + unix.dup(writer) + unix.close(2) + unix.open("/dev/null", unix.O_RDONLY) + unix.sigaction(unix.SIGINT, oldint) + unix.sigaction(unix.SIGQUIT, oldquit) + unix.sigprocmask(unix.SIG_SETMASK, oldmask) + unix.execve(ls, {ls, '-Shal'}) + unix.exit(127) + else + unix.close(writer) + SetStatus(200) + SetHeader('Content-Type', 'text/plain') + while true do + data, err = unix.read(reader) + if data then + if data ~= '' then + Write(data) else - unix.close(writer) - SetStatus(200) - SetHeader('Content-Type', 'text/plain') - while true do - data, errno = unix.read(reader) - if data then - if data ~= '' then - Write(data) - else - break - end - elseif errno ~= unix.EINTR then - Log(kLogWarn, 'read() failed: %s' % {unix.strerror(errno)}) - break - end - end - unix.close(reader) - unix.wait(-1) - -- unix.sigaction(unix.SIGINT, oldint) - -- unix.sigaction(unix.SIGQUIT, oldquit) - -- unix.sigprocmask(unix.SIG_SETMASK, oldmask) - return + break end + elseif err:errno() ~= unix.EINTR then + Log(kLogWarn, 'read() failed: %s' % {tostring(err)}) + break end end + unix.close(reader) + unix.wait(-1) + unix.sigaction(unix.SIGINT, oldint) + unix.sigaction(unix.SIGQUIT, oldquit) + unix.sigprocmask(unix.SIG_SETMASK, oldmask) + return end - SetStatus(200) - SetHeader('Content-Type', 'text/plain') - Write('error %s calling %s()' % {unix.strerrno(errno), syscall}) end main() diff --git a/tool/net/demo/unix-webserver.lua b/tool/net/demo/unix-webserver.lua index 69c4ef444..b6837aae3 100644 --- a/tool/net/demo/unix-webserver.lua +++ b/tool/net/demo/unix-webserver.lua @@ -45,7 +45,7 @@ function main() server = unix.socket() unix.bind(server, ifs[i].ip) unix.listen(server) - ip, errno, port = unix.getsockname(server) + ip, port = assert(unix.getsockname(server)) addr = '%s:%d' % {FormatIp(ip), port} url = 'http://%s' % {addr} Log(kLogInfo, 'listening on %s' % {addr}) @@ -65,7 +65,7 @@ function main() if fd == mainfd then data, errno = unix.read(mainfd) if not data then - Log(kLogInfo, 'got %s from parent client' % {unix.strerrno(errno)}) + Log(kLogInfo, 'got %s from parent client' % {tostring(errno)}) -- prevent redbean core from writing a response unix.exit(1) end @@ -79,8 +79,7 @@ function main() unix.write(mainfd, data) elseif servers[fd] then unix.write(mainfd, 'preparing to accept from %d
\r\n' % {fd}) - client, errno, clientip, clientport = unix.accept(fd) - unix.write(mainfd, 'preparing to accept from %d
\r\n' % {fd}) + client, clientip, clientport = assert(unix.accept(fd)) addr = '%s:%d' % {FormatIp(clientip), clientport} addrs[client] = addr unix.write(mainfd, 'got client %s
\r\n' % {addr}) diff --git a/tool/net/help.txt b/tool/net/help.txt index 9a1c60ce4..85470e687 100644 --- a/tool/net/help.txt +++ b/tool/net/help.txt @@ -77,7 +77,6 @@ FLAGS --strace enables system call tracing --ftrace enables function call tracing - KEYBOARD CTRL-D EXIT @@ -126,6 +125,7 @@ KEYBOARD ALT-\ SQUEEZE ADJACENT WHITESPACE PROTIP REMAP CAPS LOCK TO CTRL +──────────────────────────────────────────────────────────────────────────────── USAGE @@ -234,37 +234,7 @@ USAGE inside the binary. redbean also respects your privacy and won't phone home because your computer is its home. - -REPL - - Your redbean displays a Read-Eval-Print-Loop that lets you modify the - state of the main server process while your server is running. Any - changes will propagate into forked clients. - - Your REPL is displayed only when redbean is run as a non-daemon in a - UNIX terminal or the Windows 10 command prompt or powershell. Since - the REPL is a Lua REPL it's not included in a redbean-static builds. - - redbean uses the same keyboard shortcuts as GNU Readline and Emacs. - Some of its keyboard commands (listed in a previous section) were - inspired by Paredit. - - A history of your commands is saved to `~/.redbean_history`. - - If you love the redbean repl and want to use it as your language - interpreter then you can pass the `-i` flag to put redbean into - interpreter mode. - - redbean.com -i binarytrees.lua 15 - - In this mode redbean won't start a web server and instead functions - like the `lua` command. The first command line argument becomes the - script you want to run. If you don't supply a script, then the repl - without a web server is displayed. - - This can be useful for testing, since the redbean extensions and - modules for the Lua language are still made available. - +──────────────────────────────────────────────────────────────────────────────── SECURITY @@ -283,6 +253,7 @@ SECURITY See http://redbean.dev for further details. +──────────────────────────────────────────────────────────────────────────────── LUA SERVER PAGES @@ -323,6 +294,88 @@ LUA SERVER PAGES one-time operations like importing modules. Then, as requests roll in, isolated processes are cloned from the blueprint you created. +──────────────────────────────────────────────────────────────────────────────── + +REPL + + Your redbean displays a Read-Eval-Print-Loop that lets you modify the + state of the main server process while your server is running. Any + changes will propagate into forked clients. + + Your REPL is displayed only when redbean is run as a non-daemon in a + UNIX terminal or the Windows 10 command prompt or PowerShell. Since + the REPL is a Lua REPL it's not included in a redbean-static builds. + + redbean uses the same keyboard shortcuts as GNU Readline and Emacs. + Some of its keyboard commands (listed in a previous section) were + inspired by Paredit. + + A history of your commands is saved to `~/.redbean_history`. + + If you love the redbean repl and want to use it as your language + interpreter then you can pass the `-i` flag to put redbean into + interpreter mode. + + redbean.com -i binarytrees.lua 15 + + In this mode redbean won't start a web server and instead functions + like the `lua` command. The first command line argument becomes the + script you want to run. If you don't supply a script, then the repl + without a web server is displayed. This is useful for testing since + redbean extensions and modules for the Lua language, are still made + available. You can also write redbean scripts with shebang lines: + + #!/usr/bin/redbean -i + print('hello world') + + However operating systems like Linux usually require that script + interperters be in the local executable format. You can "assimilate" + and install your redbean using the following commands: + + zip -d redbean.com .ape # remove the ape header + ./redbean.com -h >/dev/null # assimilate the binary + sudo cp redbean.com /usr/bin/redbean + + By following the above steps, redbean can be installed systemwide for + multiple user accounts. It's also possible to chmod the binary to have + setuid privileges, provided it's configured to drop privileges in the + most appropriate manner; see the UNIX section for further details. + +──────────────────────────────────────────────────────────────────────────────── + +GLOBALS + + arg: array[str] + + Array of command line arguments, excluding those parsed by + getopt() in the C code, which stops parsing at the first + non-hyphenated arg. In some cases you can use the magic -- + argument to delimit C from Lua arguments. + + For example, if you launch your redbean as follows: + + redbean.com -v arg1 arg2 + + Then your `/.init.lua` file will have the `arg` array like: + + arg[-1] = '/usr/bin/redbean.com' + arg[ 0] = '/zip/.init.lua' + arg[ 1] = 'arg1' + arg[ 2] = 'arg2' + + If you launch redbean in interpreter mode (rather than web + server) mode, then an invocation like this: + + ./redbean.com -i script.lua arg1 arg2 + + Would have an `arg` array like this: + + arg[-1] = './redbean.com' + arg[ 0] = 'script.lua' + arg[ 1] = 'arg1' + arg[ 2] = 'arg2' + +──────────────────────────────────────────────────────────────────────────────── SPECIAL PATHS @@ -369,39 +422,7 @@ SPECIAL PATHS If you enable HTTPS client verification then redbean will check that HTTPS clients (a) have a certificate and (b) it was signed. - -GLOBALS - - arg: array[str] - - Array of command line arguments, excluding those parsed by - getopt() in the C code, which stops parsing at the first - non-hyphenated arg. In some cases you can use the magic -- - argument to delimit C from Lua arguments. - - For example, if you launch your redbean as follows: - - redbean.com -v arg1 arg2 - - Then your `/.init.lua` file will have the `arg` array like: - - arg[-1] = '/usr/bin/redbean.com' - arg[ 0] = '/zip/.init.lua' - arg[ 1] = 'arg1' - arg[ 2] = 'arg2' - - If you launch redbean in interpreter mode (rather than web - server) mode, then an invocation like this: - - ./redbean.com -i script.lua arg1 arg2 - - Would have an `arg` array like this: - - arg[-1] = './redbean.com' - arg[ 0] = 'script.lua' - arg[ 1] = 'arg1' - arg[ 2] = 'arg2' - +──────────────────────────────────────────────────────────────────────────────── HOOKS @@ -450,6 +471,7 @@ HOOKS process once _exit() is ready to be called. This won't be called in uniprocess mode. +──────────────────────────────────────────────────────────────────────────────── FUNCTIONS @@ -1196,6 +1218,9 @@ FUNCTIONS Returns 64-bit hardware random integer from RDSEED instruction, with automatic fallback to RDRND and getrandom() if not available. + GetCpuCount() → int + Returns CPU core count or 0 if it couldn't be determined. + GetCpuCore() → int Returns 0-indexed CPU core on which process is currently scheduled. @@ -1216,6 +1241,7 @@ FUNCTIONS the density of information. Cryptographic random should be in the ballpark of 7.9 whereas plaintext will be more like 4.5. +──────────────────────────────────────────────────────────────────────────────── CONSTANTS @@ -1239,6 +1265,7 @@ CONSTANTS Logging anything at this level will result in a backtrace and process exit. +──────────────────────────────────────────────────────────────────────────────── LSQLITE3 MODULE @@ -1275,6 +1302,7 @@ LSQLITE3 MODULE we provide an APE build of the SQLite shell which you can use to administrate your redbean database. See the sqlite3.com download above. +──────────────────────────────────────────────────────────────────────────────── RE MODULE @@ -1344,6 +1372,7 @@ RE MODULE end of the line. This flag may only be used with re.search and regex_t*:search. +──────────────────────────────────────────────────────────────────────────────── MAXMIND MODULE @@ -1365,13 +1394,16 @@ MAXMIND MODULE For further details, please see maxmind.lua in redbean-demo.com. +──────────────────────────────────────────────────────────────────────────────── UNIX MODULE This module exposes the low-level UNIX system call interface. This module works on all supported platforms, including Windows NT. - unix.open(path:str, flags:int[, mode:int]) → fd:int, unix.Errno + unix.open(path:str, flags:int[, mode:int[, dirfd:int]]) + ├─→ fd:int + └─→ nil, unix.Errno Opens file. @@ -1404,6 +1436,7 @@ UNIX MODULE - `O_VERIFY` it's complicated (zero on non-FreeBSD) - `O_SHLOCK` it's complicated (zero on non-BSD) - `O_EXLOCK` it's complicated (zero on non-BSD) + - `O_NOATIME` don't record access time (zero on non-Linux) - `O_RANDOM` hint random access intent (zero on non-Windows) - `O_SEQUENTIAL` hint sequential access intent (zero on non-Windows) - `O_COMPRESSED` ask fs to abstract compression (zero on non-Windows) @@ -1424,26 +1457,42 @@ UNIX MODULE already. If it does exist then `nil` is returned along with `errno` set to `EEXIST`. - unix.close(fd:int) → ok:bool, unix.Errno + `dirfd` defaults to to `unix.AT_FDCWD` and may optionally be set to + a directory file descriptor to which `path` is relative. + + Returns `ENOENT` if `path` doesn't exist. + + Returns `ENOTDIR` if `path` contained a directory component that + wasn't a directory. + + unix.close(fd:int) + ├─→ true + └─→ nil, unix.Errno Closes file descriptor. - unix.read(fd:int[, bufsiz:int, offset:int]) → data:str, unix.Errno + unix.read(fd:int[, bufsiz:str[, offset:int]]) + ├─→ data:str + └─→ nil, unix.Errno Reads from file descriptor. - unix.write(fd:int, data[, offset:int]) → wrote:int, unix.Errno + unix.write(fd:int, data:str[, offset:int]) + ├─→ wrotebytes:int + └─→ nil, unix.Errno Writes to file descriptor. - unix.exit([exitcode]) → ⊥ + unix.exit([exitcode:int]) + └─→ ⊥ Invokes `_Exit(exitcode)` on the process. This will immediately halt the current process. Memory will be freed. File descriptors will be closed. Any open connections it owns will be reset. This function never returns. - unix.environ() → {str, ...} + unix.environ() + └─→ {str,...} Returns raw environment variables. @@ -1463,12 +1512,38 @@ UNIX MODULE command prompt inserts multiple environment variables with empty string as keys, for its internal bookkeeping. - unix.fork() → childpid|0:int, unix.Errno + unix.fork() + ├─┬─→ 0 + │ └─→ childpid:int + └─→ nil, unix.Errno - Creates a new process mitosis style. This returns twice. The - parent process gets the nonzero pid. The child gets zero. + Creates a new process mitosis style. - unix.commandv(prog:str) → path:str, unix.Errno + This system call returns twice. The parent process gets the nonzero + pid. The child gets zero. + + Here's some benchmarks for fork() performance across platforms: + + Linux 5.4 fork l: 97,200𝑐 31,395𝑛𝑠 [metal] + FreeBSD 12 fork l: 236,089𝑐 78,841𝑛𝑠 [vmware] + Darwin 20.6 fork l: 295,325𝑐 81,738𝑛𝑠 [metal] + NetBSD 9 fork l: 5,832,027𝑐 1,947,899𝑛𝑠 [vmware] + OpenBSD 6.8 fork l: 13,241,940𝑐 4,422,103𝑛𝑠 [vmware] + Windows10 fork l: 18,802,239𝑐 6,360,271𝑛𝑠 [metal] + + One of the benefits of using fork() is it creates an isolation + barrier between the different parts of your app. This can lead to + enhanced reliability and security. For example, redbean uses fork so + it can wipe your ssl keys from memory before handing over control to + request handlers that process untrusted input. It also ensures that + if your Lua app crashes, it won't take down the server as a whole. + Hence it should come as no surprise that fork() would go slower on + operating systems that have more security features. So depending on + your use case, you can choose the operating system that suits you. + + unix.commandv(prog:str) + ├─→ path:str + └─→ nil, unix.Errno Performs `$PATH` lookup of executable. @@ -1483,7 +1558,8 @@ UNIX MODULE `prog` contains slashes then it's not path searched either and will be returned if it exists. - unix.execve(prog:str[, args:List<*>[, env:List<*>]]) → false, unix.Errno + unix.execve(prog:str[, args:List<*>, env:List<*>]) + └─→ nil, unix.Errno Exits current process, replacing it with a new instance of the specified program. `prog` needs to be an absolute path, see @@ -1516,7 +1592,9 @@ UNIX MODULE `EAGAIN` is returned if you've enforced a max number of processes using `setrlimit(RLIMIT_NPROC)`. - unix.dup(oldfd:int[, newfd:int[, flags:int]]) → newfd:int, unix.Errno + unix.dup(oldfd:int[, newfd:int[, flags:int]]) + ├─→ newfd:int + └─→ nil, unix.Errno Duplicates file descriptor. @@ -1527,20 +1605,31 @@ UNIX MODULE `flags` can have `O_CLOEXEC` which means the returned file descriptors will be automatically closed upon execve(). - unix.pipe([flags:int]) → reader:int, unix.Errno, writer:int + unix.pipe([flags:int]) + ├─→ reader:int, writer:int + └─→ nil, unix.Errno Creates fifo which enables communication between processes. - Returns two file descriptors: one for reading and one for - writing. `flags` can have `O_CLOEXEC`. On error, `reader` and - `writer` will be `nil` and `errno` will be set to non-nil. + + `flags` can have any of + + - `O_CLOEXEC`: Automatically close file descriptor upon execve() + + - `O_NONBLOCK`: Request `EAGAIN` be raised rather than blocking. + + - `O_DIRECT`: Enable packet mode w/ atomic reads and writes, so long + as they're no larger than `PIPE_BUF` (guaranteed to be 512+ bytes) + with support limited to Linux, Windows NT, FreeBSD, and NetBSD. + + Returns two file descriptors: one for reading and one for writing. Here's an example of how pipe(), fork(), dup(), etc. may be used to serve an HTTP response containing the output of a subprocess. local unix = require "unix" - ls = unix.commandv("ls") - reader, writer = unix.pipe() - if unix.fork() == 0 then + ls = assert(unix.commandv("ls")) + reader, writer = assert(unix.pipe()) + if assert(unix.fork()) == 0 then unix.close(1) unix.dup(writer) unix.close(writer) @@ -1551,19 +1640,25 @@ UNIX MODULE unix.close(writer) SetHeader('Content-Type', 'text/plain') while true do - data = unix.read(reader) - if data ~= "" then - Write(data) - else + data, err = unix.read(reader) + if data then + if data ~= "" then + Write(data) + else + break + end + elseif err:errno() ~= EINTR then + Log(kLogWarn, tostring(err)) break end end - unix.close(reader) - unix.wait() + assert(unix.close(reader)) + assert(unix.wait()) end unix.wait([pid:int, options:int]) - → pid:int, unix.Errno, wstatus:int + ├─→ pid:int, wstatus:int + └─→ nil, unix.Errno Waits for subprocess to terminate. @@ -1586,52 +1681,90 @@ UNIX MODULE -- wait for zombies -- traditional technique for SIGCHLD handlers while true do - pid, wstatus, errno = unix.wait(-1, unix.WNOHANG) + pid, status = unix.wait(-1, unix.WNOHANG) if pid then - if unix.WIFEXITED(wstatus) then + if unix.WIFEXITED(status) then print('child', pid, 'exited with', - unix.WEXITSTATUS(wstatus)) - elseif unix.WIFSIGNALED(wstatus) then + unix.WEXITSTATUS(status)) + elseif unix.WIFSIGNALED(status) then print('child', pid, 'crashed with', - unix.strsignal(unix.WTERMSIG(wstatus))) + unix.strsignal(unix.WTERMSIG(status))) end - elseif errno == unix.ECHILD then - print('no more zombies') + elseif status:errno() == unix.ECHILD then + Log(kLogDebug, 'no more zombies') break - elseif errno == unix.ECHILD then - print('wait failed', unix.strerror(errno)) + else + Log(kLogWarn, tostring(err)) break end end - unix.getpid() → pid:int + unix.getpid() + └─→ pid:int Returns process id of current process. This function does not fail. - unix.getppid() → pid:int + unix.getppid() + └─→ pid:int Returns process id of parent process. This function does not fail. - unix.kill(pid, sig) → ok:bool, unix.Errno + unix.kill(pid:int, sig:int) + ├─→ true + └─→ nil, unix.Errno - Returns process id of current process. + Sends signal to process(es). - unix.raise(sig) → rc:int, unix.Errno + The impact of this action can be terminating the process, or + interrupting it to request something happen. + + `pid` can be: + + - `pid > 0` signals one process by id + - `== 0` signals all processes in current process group + - `-1` signals all processes possible (except init) + - `< -1` signals all processes in -pid process group + + `sig` can be: + + - `0` checks both if pid exists and we can signal it + - `SIGINT` sends ctrl-c keyboard interrupt + - `SIGQUIT` sends backtrace and exit signal + - `SIGTERM` sends shutdown signal + - etc. + + Windows NT only supports the kill() signals required by the ANSI C89 + standard, which are `SIGINT` and `SIGQUIT`. All other signals on the + Windows platform that are sent to another process via kill() will be + treated like `SIGKILL`. + + unix.raise(sig:int) + ├─→ rc:int + └─→ nil, unix.Errno Triggers signal in current process. + This is pretty much the same as `kill(getpid(), sig)`. - unix.access(path:str, how) → ok:bool, unix.Errno + unix.access(path:str, how:int[, flags:int[, dirfd:int]]) + ├─→ true + └─→ nil, unix.Errno Checks if effective user of current process has permission to access file. `how` can be `R_OK`, `W_OK`, `X_OK`, or `F_OK` to check for read, write, execute, and existence respectively. - unix.mkdir(path:str, mode) → ok:bool, unix.Errno + `flags` may have any of: + + - `AT_SYMLINK_NOFOLLOW`: do not follow symbolic links. + + unix.mkdir(path:str[, mode:int[, dirfd:int]]) + ├─→ true + └─→ nil, unix.Errno Makes directory. @@ -1654,7 +1787,9 @@ UNIX MODULE Fails with `ENAMETOOLONG` if the path is too long. - unix.makedirs(path:str, mode) → ok:bool, unix.Errno + unix.makedirs(path:str[, mode:int]) + ├─→ true + └─→ nil, unix.Errno Makes directories. @@ -1665,48 +1800,106 @@ UNIX MODULE Unlike mkdir() this convenience wrapper will automatically create parent parent directories as needed. - unix.chdir(path:str) → ok:bool, unix.Errno + unix.chdir(path:str) + ├─→ true + └─→ nil, unix.Errno Changes current directory to `path`. - unix.unlink(path:str) → ok:bool, unix.Errno + unix.unlink(path:str[, dirfd:int]) + ├─→ true + └─→ nil, unix.Errno Removes file at `path`. - unix.rmdir(path:str) → ok:bool, unix.Errno + If `path` refers to a symbolic link, the link is removed. + + Returns `EISDIR` if `path` refers to a directory. See rmdir(). + + unix.rmdir(path:str[, dirfd:int]) + ├─→ true + └─→ nil, unix.Errno Removes empty directory at `path`. - unix.rename(oldpath:str, newpath:str) → ok:bool, unix.Errno + Returns `ENOTDIR` if `path` isn't a directory, or a path component + in `path` exists yet wasn't a directory. + + unix.rename(oldpath:str, newpath:str[, olddirfd:int, newdirfd:int]) + ├─→ true + └─→ nil, unix.Errno Renames file or directory. - unix.link(existingpath:str, newpath:str) → ok:bool, unix.Errno + unix.link(existingpath:str, newpath:str[, flags:int[, olddirfd, newdirfd]]) + ├─→ true + └─→ nil, unix.Errno Creates hard link, so your underlying inode has two names. - unix.symlink(target:str, linkpath:str) → ok:bool, unix.Errno + unix.symlink(target:str, linkpath:str[, newdirfd:int]) + ├─→ true + └─→ nil, unix.Errno - Creates soft link, or a symbolic link. + Creates symbolic link. - unix.realpath(filename:str) → abspath:str, unix.Errno + On Windows NT a symbolic link is called a "reparse point" and can + only be created from an administrator account. Your redbean will + automatically request the appropriate permissions. + + unix.readlink(path:str[, dirfd:int]) + ├─→ content:str + └─→ nil, unix.Errno + + Reads contents of symbolic link. + + Note that broken links are supported on all platforms. A symbolic + link can contain just about anything. It's important to not assume + that `content` will be a valid filename. + + On Windows NT, this function transliterates `\` to `/` and + furthermore prefixes `//?/` to WIN32 DOS-style absolute paths, + thereby assisting with simple absolute filename checks in addition + to enabling one to exceed the traditional 260 character limit. + + unix.realpath(path:str) + ├─→ path:str + └─→ nil, unix.Errno Returns absolute path of filename, with `.` and `..` components removed, and symlinks will be resolved. - unix.chown(path:str, uid, gid) → ok:bool, unix.Errno + unix.chown(path:str, uid:int, gid:int[, flags:int[, dirfd:int]]) + ├─→ true + └─→ nil, unix.Errno Changes user and gorup on file. - unix.chmod(path:str, mode) → ok:bool, unix.Errno + Returns `ENOSYS` on Windows NT. + + unix.chmod(path:str, mode:int[, flags:int[, dirfd:int]]) + ├─→ true + └─→ nil, unix.Errno Changes mode bits on file. - unix.getcwd() → path:str, unix.Errno + On Windows NT the chmod system call only changes the read-only + status of a file. + + unix.getcwd() + ├─→ path:str + └─→ nil, unix.Errno Returns current working directory. - unix.fcntl(fd:int, cmd:int[, arg:int]) → rc:int, unix.Errno + On Windows NT, this function transliterates `\` to `/` and + furthermore prefixes `//?/` to WIN32 DOS-style absolute paths, + thereby assisting with simple absolute filename checks in addition + to enabling one to exceed the traditional 260 character limit. + + unix.fcntl(fd:int, cmd:int, ...) + ├─→ ... + └─→ nil, unix.Errno Manipulates file descriptor. @@ -1714,30 +1907,42 @@ UNIX MODULE lets you query and/or change the status of file descriptors. For example, it's possible using this to change `FD_CLOEXEC`. - POSIX advisory locks can be controlled by setting `cmd` to - `F_UNLCK`, `F_RDLCK`, `F_WRLCK`, `F_SETLK`, or `F_SETLKW`. + [work in progress] POSIX advisory locks can be controlled by setting + `cmd` to `F_UNLCK`, `F_RDLCK`, `F_WRLCK`, `F_SETLK`, or `F_SETLKW`. - unix.getsid(pid:int) → sid:int, unix.Errno + unix.getsid(pid:int) + ├─→ sid:int + └─→ nil, unix.Errno Gets session id. - unix.getpgrp() → pgid:int, unix.Errno + unix.getpgrp() + ├─→ pgid:int + └─→ nil, unix.Errno Gets process group id. - unix.setpgrp() → pgid:int, unix.Errno + unix.setpgrp() + ├─→ pgid:int + └─→ nil, unix.Errno Sets process group id. This is the same as `setpgid(0,0)`. - unix.setpgid(pid:int, pgid:int) → pgid:int, unix.Errno + unix.setpgid(pid:int, pgid:int) + ├─→ true + └─→ nil, unix.Errno Sets process group id the modern way. - unix.getpgid(pid) → pgid:int, unix.Errno + unix.getpgid(pid:int) + ├─→ pgid:int + └─→ nil, unix.Errno Gets process group id the modern wayp. - unix.setsid() → sid:int, unix.Errno + unix.setsid() + ├─→ sid:int + └─→ nil, unix.Errno Sets session id. @@ -1745,7 +1950,8 @@ UNIX MODULE Fails with `ENOSYS` on Windows NT. - unix.getuid() → uid:int + unix.getuid() + └─→ uid:int Gets real user id. @@ -1754,7 +1960,8 @@ UNIX MODULE This function does not fail. - unix.getgid() → gid:int + unix.getgid() + └─→ gid:int Sets real group id. @@ -1762,7 +1969,8 @@ UNIX MODULE This function does not fail. - unix.geteuid() → uid:int + unix.geteuid() + └─→ uid:int Gets effective user id. @@ -1774,7 +1982,8 @@ UNIX MODULE This function does not fail. - unix.getegid() → gid:int + unix.getegid() + └─→ gid:int Gets effective group id. @@ -1782,13 +1991,17 @@ UNIX MODULE This function does not fail. - unix.chroot(path:str) → ok:bool, unix.Errno + unix.chroot(path:str) + ├─→ true + └─→ nil, unix.Errno Changes root directory. Returns `ENOSYS` on Windows NT. - unix.setuid(uid:int) → ok:bool, unix.Errno + unix.setuid(uid:int) + ├─→ true + └─→ nil, unix.Errno Sets user id. @@ -1797,26 +2010,18 @@ UNIX MODULE `-G` and `-U` flags, you can replicate that behavior in the Lua processes you spawn as follows: - errno = unix.setgid(1000) -- check your /etc/groups - if errno then - Log(kLogFatal, setgid() failed: %s' % {unix.strerror(errno)}) - end - errno = unix.setuid(1000) -- check your /etc/passwd - if errno then - Log(kLogFatal, setuid() failed: %s' % {unix.strerror(errno)}) - end + ok, err = unix.setgid(1000) -- check your /etc/groups + if not ok then Log(kLogFatal, tostring(err)) end + ok, err = unix.setuid(1000) -- check your /etc/passwd + if not ok then Log(kLogFatal, tostring(err)) end If your goal is to relinquish privileges because redbean is a setuid binary, then things are more straightforward: - errno = unix.setgid(unix.getgid()) - if errno then - Log(kLogFatal, setgid() failed: %s' % {unix.strerror(errno)}) - end - errno = unix.setuid(unix.getuid()) - if errno then - Log(kLogFatal, setuid() failed: %s' % {unix.strerror(errno)}) - end + ok, err = unix.setgid(unix.getgid()) + if not ok then Log(kLogFatal, tostring(err)) end + ok, err = unix.setuid(unix.getuid()) + if not ok then Log(kLogFatal, tostring(err)) end See also the setresuid() function and be sure to refer to your local system manual about the subtleties of changing user id in a way that @@ -1824,13 +2029,17 @@ UNIX MODULE Returns `ENOSYS` on Windows NT if `uid` isn't `getuid()`. - unix.setgid(gid:int) → ok:bool, unix.Errno + unix.setgid(gid:int) + ├─→ true + └─→ nil, unix.Errno Sets group id. Returns `ENOSYS` on Windows NT if `gid` isn't `getgid()`. - unix.setresuid(real:int, effective:int, saved:int) → ok:bool, unix.Errno + unix.setresuid(real:int, effective:int, saved:int) + ├─→ true + └─→ nil, unix.Errno Sets real, effective, and saved user ids. @@ -1839,7 +2048,9 @@ UNIX MODULE Returns `ENOSYS` on Windows NT. Returns `ENOSYS` on Macintosh and NetBSD if `saved` isn't -1. - unix.setresgid(real:int, effective:int, saved:int) → ok:bool, unix.Errno + unix.setresgid(real:int, effective:int, saved:int) + ├─→ true + └─→ nil, unix.Errno Sets real, effective, and saved group ids. @@ -1848,7 +2059,8 @@ UNIX MODULE Returns `ENOSYS` on Windows NT. Returns `ENOSYS` on Macintosh and NetBSD if `saved` isn't -1. - unix.umask(mask:int) → oldmask:int + unix.umask(newmask:int) + └─→ oldmask:int Sets file permission mask and returns the old one. @@ -1880,7 +2092,9 @@ UNIX MODULE This function currently works on Linux, Windows, and NetBSD. On WIN32 it uses the ReportEvent() facility. - unix.clock_gettime([clock:int]) → seconds:int, unix.Errno, nanos:int + unix.clock_gettime([clock:int]) + ├─→ seconds:int, nanos:int + └─→ nil, unix.Errno Returns nanosecond precision timestamp from the system. @@ -1901,19 +2115,30 @@ UNIX MODULE Returns `EINVAL` if clock isn't supported on platform. - unix.nanosleep(seconds:int[, nanos:int]) - → remseconds:int, unix.Errno, remnanos:int + This function only fails if `clock` is invalid. + + unix.nanosleep(seconds:int, nanos:int) + ├─→ remseconds:int, remnanos:int + └─→ nil, unix.Errno Sleeps with nanosecond precision. + Returns `EINTR` if a signal was received while waiting. + unix.sync() - unix.fsync(fd:int) → ok:bool, unix.Errno - unix.fdatasync(fd:int) → ok:bool, unix.Errno + unix.fsync(fd:int) + ├─→ true + └─→ nil, unix.Errno + unix.fdatasync(fd:int) + ├─→ true + └─→ nil, unix.Errno These functions are used to make programs slower by asking the operating system to flush data to the physical medium. - unix.lseek(fd:int, offset:int, whence:int) → newpos:int, unix.Errno + unix.lseek(fd:int, offset:int[, whence:int]) + ├─→ newposbytes:int + └─→ nil, unix.Errno Seeks to file position. @@ -1925,21 +2150,27 @@ UNIX MODULE Returns the new position relative to the start of the file. - unix.truncate(path:str[, length:int]) → ok:bool, unix.Errno + unix.truncate(path:str[, length:int]) + ├─→ true + └─→ nil, unix.Errno Reduces or extends underlying physical medium of file. If file was originally larger, content >length is lost. `length` defaults to zero. - unix.ftruncate(fd:int[, length:int]) → ok:bool, unix.Errno + unix.ftruncate(fd:int[, length:int]) + ├─→ true + └─→ nil, unix.Errno Reduces or extends underlying physical medium of open file. If file was originally larger, content >length is lost. `length` defaults to zero. - unix.socket([family:int[, type:int[, protocol:int]]]) → fd:int, unix.Errno + unix.socket([family:int[, type:int[, protocol:int]]]) + ├─→ fd:int + └─→ nil, unix.Errno `family` defaults to `AF_INET` and can be: @@ -1965,14 +2196,17 @@ UNIX MODULE `SOCK_CLOEXEC` may be bitwise or'd into `type`. unix.socketpair([family:int[, type:int[, protocol:int]]]) - → fd1:int, unix.Errno, fd2:int + ├─→ fd1:int, fd2:int + └─→ nil, unix.Errno `SOCK_CLOEXEC` may be or'd into type `family` defaults to `AF_INET` `type` defaults to `SOCK_STREAM` `protocol` defaults to `IPPROTO_TCP` - unix.bind(fd:int[, ip:uint32, port:uint16]) → ok:bool, unix.Errno + unix.bind(fd:int[, ip:uint32, port:uint16]) + ├─→ true + └─→ nil, unix.Errno Binds socket. @@ -1987,18 +2221,14 @@ UNIX MODULE is to listen on all interfaces with a kernel-assigned ephemeral port number, that can be retrieved and used as follows: - local sock = unix.socket() -- create ipv4 tcp socket - errno = unix.bind(sock) -- all interfaces ephemeral port - if errno then - Log(kLogWarn, bind() failed: %s' % {unix.strerror(errno)}) - return - end - ip, port = unix.getsockname(sock) + sock = assert(unix.socket()) -- create ipv4 tcp socket + assert(unix.bind(sock)) -- all interfaces ephemeral port + ip, port = assert(unix.getsockname(sock)) print("listening on ip", FormatIp(ip), "port", port) - unix.listen(sock) - unix.accept(sock) + assert(unix.listen(sock)) + assert(unix.accept(sock)) while true do - client, clientip, clientport = unix.accept(sock) + client, clientip, clientport = assert(unix.accept(sock)) print("got client ip", FormatIp(clientip), "port", clientport) unix.close(client) end @@ -2006,11 +2236,13 @@ UNIX MODULE Further note that calling `unix.bind(sock)` is equivalent to not calling bind() at all, since the above behavior is the default. - unix.siocgifconf() → {{name:str,ip:uint32,netmask:uint32}, ...}, unix.Errno + unix.siocgifconf() + ├─→ {{name:str,ip:uint32,netmask:uint32}, ...} + └─→ nil, unix.Errno Returns list of network adapter addresses. - unix.getsockopt(fd:int, level:int, optname:int) → ok:bool, unix.Errno, ... + unix.getsockopt(fd:int, level:int, optname:int) → ... unix.setsockopt(fd:int, level:int, optname:int, ...) → ok:bool, unix.Errno Tunes networking parameters. @@ -2018,44 +2250,87 @@ UNIX MODULE `level` and `optname` may be one of the following. The ellipses type signature above changes depending on which options are used. - - `SOL_SOCKET` + `SO_TYPE`: bool - - `SOL_SOCKET` + `SO_DEBUG`: bool - - `SOL_SOCKET` + `SO_ACCEPTCONN`: bool - - `SOL_SOCKET` + `SO_BROADCAST`: bool - - `SOL_SOCKET` + `SO_REUSEADDR`: bool - - `SOL_SOCKET` + `SO_REUSEPORT`: bool - - `SOL_SOCKET` + `SO_KEEPALIVE`: bool - - `SOL_SOCKET` + `SO_DONTROUTE`: bool - - `SOL_SOCKET` + `SO_SNDBUF`: int - - `SOL_SOCKET` + `SO_RCVBUF`: int - - `SOL_SOCKET` + `SO_RCVLOWAT`: int - - `SOL_SOCKET` + `SO_SNDLOWAT`: int - - `SOL_SOCKET` + `SO_RCVTIMEO`: seconds:int[, micros:int] - - `SOL_SOCKET` + `SO_SNDTIMEO`: seconds:int[, micros:int] - - `SOL_TCP` + `TCP_NODELAY`: bool - - `SOL_TCP` + `TCP_CORK`: bool - - `SOL_TCP` + `TCP_QUICKACK`: bool - - `SOL_TCP` + `TCP_FASTOPEN_CONNECT`: bool - - `SOL_TCP` + `TCP_DEFER_ACCEPT`: bool - - `SOL_TCP` + `TCP_KEEPIDLE`: seconds:int - - `SOL_TCP` + `TCP_KEEPINTVL`: seconds:int - - `SOL_TCP` + `TCP_FASTOPEN`: int - - `SOL_TCP` + `TCP_KEEPCNT`: int - - `SOL_TCP` + `TCP_MAXSEG`: int - - `SOL_TCP` + `TCP_SYNCNT`: int - - `SOL_TCP` + `TCP_NOTSENT_LOWAT`: int - - `SOL_TCP` + `TCP_WINDOW_CLAMP`: int - - `SOL_IP` + `IP_TOS`: int - - `SOL_IP` + `IP_MTU`: int - - `SOL_IP` + `IP_TTL`: int - - `SOL_IP` + `IP_HDRINCL`: bool + unix.getsockopt(fd:int, level:int, optname:int) + ├─→ value:int + └─→ nil, unix.Errno + unix.setsockopt(fd:int, level:int, optname:int, value:bool) + ├─→ true + └─→ nil, unix.Errno + + - `SOL_SOCKET` + `SO_TYPE` + - `SOL_SOCKET` + `SO_DEBUG` + - `SOL_SOCKET` + `SO_ACCEPTCONN` + - `SOL_SOCKET` + `SO_BROADCAST` + - `SOL_SOCKET` + `SO_REUSEADDR` + - `SOL_SOCKET` + `SO_REUSEPORT` + - `SOL_SOCKET` + `SO_KEEPALIVE` + - `SOL_SOCKET` + `SO_DONTROUTE` + - `SOL_TCP` + `TCP_NODELAY` + - `SOL_TCP` + `TCP_CORK` + - `SOL_TCP` + `TCP_QUICKACK` + - `SOL_TCP` + `TCP_FASTOPEN_CONNECT` + - `SOL_TCP` + `TCP_DEFER_ACCEPT` + - `SOL_IP` + `IP_HDRINCL` + + unix.getsockopt(fd:int, level:int, optname:int) + ├─→ value:int + └─→ nil, unix.Errno + unix.setsockopt(fd:int, level:int, optname:int, value:int) + ├─→ true + └─→ nil, unix.Errno + + - `SOL_SOCKET` + `SO_SNDBUF` + - `SOL_SOCKET` + `SO_RCVBUF` + - `SOL_SOCKET` + `SO_RCVLOWAT` + - `SOL_SOCKET` + `SO_SNDLOWAT` + - `SOL_TCP` + `TCP_KEEPIDLE` + - `SOL_TCP` + `TCP_KEEPINTVL` + - `SOL_TCP` + `TCP_FASTOPEN` + - `SOL_TCP` + `TCP_KEEPCNT` + - `SOL_TCP` + `TCP_MAXSEG` + - `SOL_TCP` + `TCP_SYNCNT` + - `SOL_TCP` + `TCP_NOTSENT_LOWAT` + - `SOL_TCP` + `TCP_WINDOW_CLAMP` + - `SOL_IP` + `IP_TOS` + - `SOL_IP` + `IP_MTU` + - `SOL_IP` + `IP_TTL` + + unix.getsockopt(fd:int, level:int, optname:int) + ├─→ secs:int, nsecs:int + └─→ nil, unix.Errno + unix.setsockopt(fd:int, level:int, optname:int, secs:int[, nanos:int]) + ├─→ true + └─→ nil, unix.Errno + + - `SOL_SOCKET` + `SO_RCVTIMEO`: If this option is specified then + your stream socket will have a read() / recv() timeout. If the + specified interval elapses without receiving data, then EAGAIN + shall be returned by read. If this option is used on listening + sockets, it'll be inherited by accepted sockets. Your redbean + already does this for GetClientFd() based on the `-t` flag. + + - `SOL_SOCKET` + `SO_SNDTIMEO`: This is the same as `SO_RCVTIMEO` + but it applies to the write() / send() functions. + + unix.getsockopt(fd:int, unix.SOL_SOCKET, unix.SO_LINGER) + ├─→ seconds:int, enabled:bool + └─→ nil, unix.Errno + unix.setsockopt(fd:int, unix.SOL_SOCKET, unix.SO_LINGER, secs:int, enabled:bool) + ├─→ true + └─→ nil, unix.Errno + + This `SO_LINGER` parameter can be used to make close() a blocking + call. Normally when the kernel returns immediately when it receives + close(). Sometimes it's desirable to have extra assurance on errors + happened, even if it comes at the cost of performance. Returns `EINVAL` if settings other than the above are used. Returns `ENOSYS` if setting isn't supported by the host o/s. - unix.poll({fd:int=events:int, ...}[, timeoutms:int]) - → {fd:int=revents:int, ...}, unix.Errno + unix.poll({[fd:int]=events:int, ...}[, timeoutms:int]) + ├─→ {[fd:int]=revents:int, ...} + └─→ nil, unix.Errno Checks for events on a set of file descriptors. @@ -2068,16 +2343,21 @@ UNIX MODULE then that means block as long as it takes until there's an event or an interrupt. If the timeout expires, an empty table is returned. - unix.gethostname() → host:str, unix.Errno + unix.gethostname() + ├─→ host:str + └─→ nil, unix.Errno Returns hostname of system. - unix.listen(fd:int[, backlog:int]) → ok:bool, unix.Errno + unix.listen(fd:int[, backlog:int]) + ├─→ true + └─→ nil, unix.Errno Begins listening for incoming connections on a socket. unix.accept(serverfd:int[, flags:int]) - → clientfd:int, unix.Errno, ip:uint32, port:uint16 + ├─→ clientfd:int, ip:uint32, port:uint16 + └─→ nil, unix.Errno Accepts new client socket descriptor for a listening tcp socket. @@ -2086,7 +2366,9 @@ UNIX MODULE - `SOCK_CLOEXEC` - `SOCK_NONBLOCK` - unix.connect(fd:int, ip:uint32, port:uint16) → ok:bool, unix.Errno + unix.connect(fd:int, ip:uint32, port:uint16) + ├─→ true + └─→ nil, unix.Errno Connects a TCP socket to a remote host. @@ -2094,15 +2376,21 @@ UNIX MODULE remembers the intended address so that send() or write() may be used rather than sendto(). - unix.getsockname(fd:int) → ip:uint32, unix.Errno, port:uint16 + unix.getsockname(fd:int) + ├─→ ip:uint32, port:uint16 + └─→ nil, unix.Errno Retrieves the local address of a socket. - unix.getpeername(fd:int) → ip:uint32, unix.Errno, port:uint16 + unix.getpeername(fd:int) + ├─→ ip:uint32, port:uint16 + └─→ nil, unix.Errno Retrieves the remote address of a socket. - unix.recv(fd:int[, bufsiz:int[, flags:int]]) → data:str, unix.Errno + unix.recv(fd:int[, bufsiz:int[, flags:int]]) + ├─→ data:str + └─→ nil, unix.Errno `flags` can have: @@ -2112,7 +2400,8 @@ UNIX MODULE - `MSG_OOB` unix.recvfrom(fd:int[, bufsiz:int[, flags:int]]) - → data:str, unix.Errno, ip:uint32, port:uint16 + ├─→ data:str, ip:uint32, port:uint16 + └─→ nil, unix.Errno `flags` can have: @@ -2121,27 +2410,50 @@ UNIX MODULE - `MSG_PEEK` - `MSG_OOB` - unix.send(fd:int, data:str[, flags:int]) → sent:int, unix.Errno + unix.send(fd:int, data:str[, flags:int]) + ├─→ sent:int + └─→ nil, unix.Errno This is the same as `write` except it has a `flags` argument that's intended for sockets. - `flags` can have `MSG_OOB`, `MSG_DONTROUTE`, or `MSG_NOSIGNAL`. + `flags` may have any of: - unix.sendto(fd:int, data:str, ip:int, port:int[, flags:int]) - → sent:int, unix.Errno + - `MSG_OOB` + - `MSG_DONTROUTE` + - `MSG_NOSIGNAL` + + unix.sendto(fd:int, data:str, ip:uint32, port:uint16[, flags:int]) + ├─→ sent:int + └─→ nil, unix.Errno This is useful for sending messages over UDP sockets to specific addresses. - `flags` can have `MSG_OOB`, `MSG_DONTROUTE`, or `MSG_NOSIGNAL`. + `flags` may have any of: - unix.shutdown(fd:int, how:int) → ok:bool, unix.Errno + - `MSG_OOB` + - `MSG_DONTROUTE` + - `MSG_NOSIGNAL` - Partially closes socket. `how` can be `SHUT_RD`, `SHUT_WR`, or - `SHUT_RDWR`. + unix.shutdown(fd:int, how:int) + ├─→ true + └─→ nil, unix.Errno - unix.sigprocmask(how:int[, mask:int]) → oldmask:int, unix.Errno + Partially closes socket. + + `how` is set to one of: + + - `SHUT_RD`: sends a tcp half close for reading + - `SHUT_WR`: sends a tcp half close for writing + - `SHUT_RDWR` + + This system call currently has issues on Macintosh, so portable code + should log rather than assert failures reported by shutdown(). + + unix.sigprocmask(how:int, newmask:unix.Sigset) + ├─→ oldmask:unix.Sigset + └─→ nil, unix.Errno Manipulates bitset of signals blocked by process. @@ -2151,38 +2463,101 @@ UNIX MODULE - `SIG_UNBLOCK`: removes bits in `mask` from set of blocked signals - `SIG_SETMASK`: replaces process signal mask with `mask` - `mask` is a word encoded bitset of signals. Valid signal numbers - start at 1 and vary between platforms. The most famous `SIGKILL` - can't be masked, but if it could, it's assigned the number `9` - across all platforms, so if you wanted to add it to a bitset, you - would say, `1 << 8` or in general terms `1 << (sig - 1)`. + `mask` is a unix.Sigset() object (see section below). - unix.sigaction(sig:int[, handler:func|int[, flags:int[, mask:int]]]) - → oldhandler:func|int, unix.Errno, flags:int, mask:int + For example, to temporarily block `SIGTERM` and `SIGINT` so critical + work won't be interrupted, sigprocmask() can be used as follows: - `handler` can be `SIG_IGN`, `SIG_DFL`, `intptr_t`, or a Lua - function. `sig` can be `SIGINT`, `SIGQUIT`, `SIGTERM`, etc. - `flags` can have `SA_RESTART`, `SA_RESETHAND`, etc. Example: + newmask = unix.Sigset(unix.SIGTERM) + oldmask = assert(unix.sigprocmask(unix.SIG_BLOCK, newmask)) + -- do something... + assert(unix.sigprocmask(unix.SIG_SETMASK, oldmask)) - unix = require "unix" - unix.sigaction(unix.SIGUSR1, function(sig) - print('got %s' % {unix.strsignal(sig)}) - end) - unix.sigprocmask(unix.SIG_SETMASK, -1) - unix.raise(unix.SIGUSR1) - unix.sigsuspend() + unix.sigaction(sig:int[, handler:func|int[, flags:int[, mask:unix.Sigset]]]) + ├─→ oldhandler:func|int, flags:int, mask:unix.Sigset + └─→ nil, unix.Errno + + `sig` can be one of: + + - `unix.SIGINT` + - `unix.SIGQUIT` + - `unix.SIGTERM` + - etc. + + `handler` can be: + + - Lua function + - `unix.SIG_IGN` + - `unix.SIG_DFL` + + `flags` can have: + + - `unix.SA_RESTART`: Enables BSD signal handling semantics. Normally + i/o entrypoints check for pending signals to deliver. If one gets + delivered during an i/o call, the normal behavior is to cancel the + i/o operation and return -1 with `EINTR` in errno. If you use the + `SA_RESTART` flag then that behavior changes, so that any function + that's been annotated with @restartable will not return `EINTR` + and will instead resume the i/o operation. This makes coding + easier but it can be an anti-pattern if not used carefully, since + poor usage can easily result in latency issues. It also requires + one to do more work in signal handlers, so special care needs to + be given to which C library functions are @asyncsignalsafe. + + - `unix.SA_RESETHAND`: Causes signal handler to be single-shot. This + means that, upon entry of delivery to a signal handler, it's reset + to the `SIG_DFL` handler automatically. You may use the alias + `SA_ONESHOT` for this flag, which means the same thing. + + - `unix.SA_NODEFER`: Disables the reentrancy safety check on your signal + handler. Normally that's a good thing, since for instance if your + `SIGSEGV` signal handler happens to segfault, you're going to want + your process to just crash rather than looping endlessly. But in + some cases it's desirable to use `SA_NODEFER` instead, such as at + times when you wish to `longjmp()` out of your signal handler and + back into your program. This is only safe to do across platforms + for non-crashing signals such as `SIGCHLD` and `SIGINT`. Crash + handlers should use Xed instead to recover execution, because on + Windows a `SIGSEGV` or `SIGTRAP` crash handler might happen on a + separate stack and/or a separate thread. You may use the alias + `SA_NOMASK` for this flag, which means the same thing. + + - `unix.SA_NOCLDWAIT`: Changes `SIGCHLD` so the zombie is gone and + you can't call wait() anymore; similar but may still deliver the + SIGCHLD. + + - `unix.SA_NOCLDSTOP`: Lets you set `SIGCHLD` handler that's only + notified on exit/termination and not notified on `SIGSTOP`, + `SIGTSTP`, `SIGTTIN`, `SIGTTOU`, or `SIGCONT`. + + Example: + + assert(unix.sigaction(unix.SIGUSR1, function(sig) + gotsigusr1 = true + end)) + gotsigusr1 = false + assert(unix.raise(unix.SIGUSR1)) + ok, err = unix.sigsuspend() + assert(err:errno == unix.EINTR) + if gotsigusr1 + print('hooray the signal was delivered') + else + print('oh no some other signal was handled') + end It's a good idea to not do too much work in a signal handler. - unix.sigsuspend([mask]) → false, unix.Errno + unix.sigsuspend([mask:Sigmask]) + └─→ nil, unix.Errno Waits for signal to be delivered. The signal mask is temporarily replaced with `mask` during this system call. `mask` specifies which signals should be blocked. - unix.setitimer(which[, intsec, intmicros, valsec, valmicros]) - → intsec, unix.Errno, intns, valsec, valns + unix.setitimer(which[, intervalsec, intns, valuesec, valuens]) + ├─→ intervalsec:int, intervalns:int, valuesec:int, valuens:int + └─→ nil, unix.Errno Causes `SIGALRM` signals to be generated at some point(s) in the future. The `which` parameter should be `ITIMER_REAL`. @@ -2190,11 +2565,11 @@ UNIX MODULE Here's an example of how to create a 400 ms interval timer: ticks = 0 - unix.sigaction(unix.SIGALRM, function(sig) + assert(unix.sigaction(unix.SIGALRM, function(sig) print('tick no. %d' % {ticks}) ticks = ticks + 1 - end) - unix.setitimer(unix.ITIMER_REAL, 0, 400000, 0, 400000) + end)) + assert(unix.setitimer(unix.ITIMER_REAL, 0, 400e6, 0, 400e6)) while true do unix.sigsuspend() end @@ -2204,29 +2579,23 @@ UNIX MODULE unix.sigaction(unix.SIGALRM, MyOnSigAlrm, unix.SA_RESETHAND) unix.setitimer(unix.ITIMER_REAL, 0, 0, 1, 0) - unix.strerrno(unix.Errno) → str - - Turns `errno` code into its symbolic name, e.g. `"EINTR"`. If - `errno` isn't known, this function returns nil. - - unix.strerdoc(unix.Errno) → str - - Turns `errno` code into a descriptive string. If `errno` isn't - known, this function returns nil. - - unix.strerror(unix.Errno) → str - - Turns `errno` code into longest string describing the error. This - includes the output of both strerrno() and strerror() as well as the - error number. On Windows it includes FormatMessage() output too. If - `errno` isn't known, this function still returns a string. - unix.strsignal(sig:int) → str - Turns platform-specific `sig` code into its name, e.g. - `strsignal(9)` always returns `"SIGKILL"`. + Turns platform-specific `sig` code into its symbolic name. - unix.setrlimit(resource:int, soft:int[, hard:int]) → ok:bool, unix.Errno + For example: + + >: unix.strsignal(9) + "SIGKILL" + >: unix.strsignal(unix.SIGKILL) + "SIGKILL" + + Please note that signal numbers are normally different across + supported platforms, and the constants should be preferred. + + unix.setrlimit(resource:int, soft:int[, hard:int]) + ├─→ true + └─→ nil, unix.Errno Changes resource limit. @@ -2257,21 +2626,69 @@ UNIX MODULE 127. On most platforms these limits are enforced by the kernel and as such are inherited by subprocesses. - unix.getrlimit(resource:int) → soft:int, unix.Errno, hard:int + `hard` defaults to whatever was specified in `soft`. + + unix.getrlimit(resource:int) + ├─→ soft:int, hard:int + └─→ nil, unix.Errno Returns information about resource limit. - unix.stat(x) → unix.Stat, Errno* + unix.stat(path:str[, flags:int[, dirfd:int]]) + ├─→ unix.Stat + └─→ nil, unix.Errno - Gets information about file or directory. `x` may be a file or - directory path string, or it may be a file descriptor int that - was made by open(). + Gets information about file or directory. - unix.opendir(path:str) → unix.Dir, Errno* + `flags` may have any of: + + - `AT_SYMLINK_NOFOLLOW`: do not follow symbolic links. + + `dirfd` defaults to to `unix.AT_FDCWD` and may optionally be set to + a directory file descriptor to which `path` is relative. + + unix.fstat(fd:int) + ├─→ unix.Stat + └─→ nil, unix.Errno + + Gets information about opened file descriptor. + + `fd` should be a file descriptor that was opened using + `unix.open(path, O_RDONLY|O_DIRECTORY)`. + + `flags` may have any of: + + - `AT_SYMLINK_NOFOLLOW`: do not follow symbolic links. + + `dirfd` defaults to to `unix.AT_FDCWD` and may optionally be set to + a directory file descriptor to which `path` is relative. + + A common use for fstat() is getting the size of a file. For example: + + fd = assert(unix.open("hello.txt", unix.O_RDONLY)) + st = assert(unix.fstat(fd)) + Log(kLogInfo, 'hello.txt is %d bytes in size' % {st:size()}) + unix.close(fd) + + unix.opendir(path:str) + ├─→ state:unix.Dir + └─→ nil, unix.Errno Opens directory for listing its contents. - unix.fdopendir(fd:int) → unix.Dir, Errno* + For example, to print a simple directory listing: + + Write('\r\n') + + unix.fdopendir(fd:int) + ├─→ next:function, state:unix.Dir + └─→ nil, unix.Errno Opens directory for listing its contents, via an fd. @@ -2279,40 +2696,58 @@ UNIX MODULE returned unix.Dir ownership takes ownership of the file descriptor and will close it automatically when garbage collected. +──────────────────────────────────────────────────────────────────────────────── + UNIX DIR OBJECT unix.Dir objects are created by opendir() or fdopendir(). The following methods are available: - unix.Dir:close() → ok:bool, unix.Errno + unix.Dir:close() + ├─→ true + └─→ nil, unix.Errno - may be called multiple times - called by the garbage collector too + Closes directory stream object and associated its file descriptor. - unix.Dir:read() → name:str, unix.Errno, kind:int, ino:int, off:int + This is called automatically by the garbage collector. + + This may be called multiple times. + + unix.Dir:read() + ├─→ name:str, kind:int, ino:int, off:int + └─→ nil + + Reads entry from directory stream. Returns `nil` if there are no more entries. Or error, `nil` will be returned and `errno` will be non-nil. `kind` can be any of: + - `DT_REG`: file is a regular file + - `DT_DIR`: file is a directory + - `DT_BLK`: file is a block device + - `DT_LNK`: file is a symbolic link + - `DT_CHR`: file is a character device + - `DT_FIFO`: file is a named pipe + - `DT_SOCK`: file is a named socket - `DT_UNKNOWN` - - `DT_REG` - - `DT_DIR` - - `DT_BLK` - - `DT_LNK` - - `DT_CHR` - - `DT_FIFO` - - `DT_SOCK` - unix.Dir:fd() → fd:int, unix.Errno + Note: This function also serves as the `__call` metamethod, so that + unix.Dir objects may be used as a for loop iterator. + + unix.Dir:fd() + ├─→ fd:int + └─→ nil, unix.Errno Returns file descriptor of open directory object. Returns `EOPNOTSUPP` if using a `/zip/...` path. Returns `EOPNOTSUPP` if using Windows NT. - unix.Dir:tell() → offset:int + unix.Dir:tell() + ├─→ off:int + └─→ nil, unix.Errno Returns current arbitrary offset into stream. @@ -2320,16 +2755,20 @@ UNIX MODULE Resets stream back to beginning. +──────────────────────────────────────────────────────────────────────────────── + UNIX STAT OBJECT unix.Stat objects are created by stat() or fstat(). The following methods are available: - unix.Stat:size() → bytes:int + unix.Stat:size() + └─→ bytes:int Size of file in bytes. - unix.Stat:mode() → mode:int + unix.Stat:mode() + └─→ mode:int Contains file type and permissions. @@ -2346,339 +2785,691 @@ UNIX MODULE - `(st:mode() & 0170000) == 0120000` means symbolic link - `(st:mode() & 0170000) == 0140000` means socket - unix.Stat:atim() → secs:int, nanos:int - - Size of file in bytes. - - unix.Stat:uid() → int + unix.Stat:uid() + └─→ uid:int User ID of file owner. - unix.Stat:gid() → int + unix.Stat:gid() + └─→ gid:int Group ID of file owner. - unix.Stat:mtim() → secs:int, nanos:int + unix.Stat:birthtim() + └─→ unixts:int, nanos:int + + File birth time. + + This field should be accurate on Apple, Windows, and BSDs. On Linux + this is the mimimum of atim/mtim/ctim. On Windows NT nanos is only + accurate to hectonanoseconds. + + Here's an example of how you might print a file timestamp: + + st = assert(unix.stat('/etc/passwd')) + unixts, nanos = st:birthtim() + year,mon,mday,hour,min,sec,gmtoffsec = unix.localtime(unixts) + Write('%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.9d%+.2d%.2d % { + year, mon, mday, hour, min, sec, nanos, + gmtoffsec / (60 * 60), math.abs(gmtoffsec) % 60}) + + unix.Stat:mtim() + └─→ unixts:int, nanos:int Last modified time. - unix.Stat:birthtim() → secs:int, nanos:int + unix.Stat:atim() + └─→ unixts:int, nanos:int - Creation time. Note that on Linux this is the mimimum of - atom/mtim/ctim. + Last access time. - unix.Stat:ctim() → secs:int, nanos:int + Please note that file systems are sometimes mounted with `noatime` + out of concern for i/o performance. Linux also provides `O_NOATIME` + as an option for open(). - Complicated time. Means time file status was last changed on - UNIX. Means creation time on Windows. + On Windows NT this is the same as birth time. - unix.Stat:blocks() → int + unix.Stat:ctim() + └─→ unixts:int, nanos:int - Number of blocks used by storage medium. + Complicated time. - unix.Stat:blksize() → int + Means time file status was last changed on UNIX. - Block size is usually 4096 for file system files. + On Windows NT this is the same as birth time. - unix.Stat:dev() → int + unix.Stat:blocks() + └─→ count512:int - ID of device containing file. + Number of 512-byte blocks used by storage medium. - unix.Stat:ino() → int + This provides some indication of how much physical storage a file + actually consumes. For example, for small file systems, your system + might report this number as being 8, which means 4096 bytes. + + On Windows NT, if compression is enabled for a file, then this + number will reflect the size *after* compression. you can use: + + st = assert(unix.stat("moby.txt")) + print('file size is %d bytes' % {st:size()}) + print('file takes up %d bytes of space' % {st:blocks() * 512}) + if GetHostOs() == 'WINDOWS' and st:flags() & 0x800 then + print('thanks to file system compression') + end + + To tell whether or not compression is being used on a file, + + unix.Stat:blksize() + └─→ bytes:int + + Block size that underlying device uses. + + This field might be of assistance in computing optimal i/o sizes. + + Please note this field has no relationship to blocks, as the latter + is fixed at a 512 byte size. + + unix.Stat:ino() + └─→ inode:int Inode number. - unix.Stat:rdev() → int + This can be used to detect some other process used rename() to swap + out a file underneath you, so you can do a refresh. redbean does it + during each main process heartbeat for its own use cases. - Device ID (if special file) + On Windows NT this is set to NtByHandleFileInformation::FileIndex. + unix.Stat:dev() + └─→ dev:int + + ID of device containing file. + + On Windows NT this is set to + NtByHandleFileInformation::VolumeSerialNumber. + + unix.Stat:rdev() + └─→ rdev:int + + Information about device type. + + This value may be set to 0 or -1 for files that aren't devices, + depending on the operating system. unix.major() and unix.minor() + may be used to extract the device numbers. + +──────────────────────────────────────────────────────────────────────────────── + + UNIX SIGSET OBJECT + + unix.Sigset(sig:int, ...) + └─→ unix.Sigset + + Creates new signal bitset. + + unix.Sigset:add(sig:int) + + Adds signal to bitset. + + Invalid signal numbers are ignored. + + unix.Sigset:remove(sig:int) + + Removes signal from bitset. + + Invalid signal numbers are ignored. + + unix.Sigset:fill() + + Sets all bits in signal bitset to true. + + unix.Sigset:clear() + + Sets all bits in signal bitset to false. + + unix.Sigset:contains(sig:int) + └─→ bool + + Returns true if `sig` is member of signal bitset. + + unix.Sigset:__repr() + unix.Sigset:__tostring() + + Returns Lua code string that recreates object. + +──────────────────────────────────────────────────────────────────────────────── + + UNIX SIGNALS + + unix.SIGINT + Terminal CTRL-C keystroke. + + unix.SIGQUIT + Terminal CTRL-\ keystroke. + + unix.SIGHUP + Terminal hangup or daemon reload; auto-broadcasted to process group. + + unix.SIGILL + Illegal instruction. + + unix.SIGTRAP + INT3 instruction. + + unix.SIGABRT + Process aborted. + + unix.SIGBUS + Valid memory access that went beyond underlying end of file. + + unix.SIGFPE + Illegal math. + + unix.SIGKILL + Terminate with extreme prejudice. + + unix.SIGUSR1 + Do whatever you want. + + unix.SIGUSR2 + Do whatever you want. + + unix.SIGSEGV + Invalid memory access. + + unix.SIGPIPE + Write to closed file descriptor. + + unix.SIGALRM + Sent by setitimer(). + + unix.SIGTERM + Terminate. + + unix.SIGCHLD + Child process exited or terminated and is now a zombie (unless this + is SIG_IGN or SA_NOCLDWAIT) or child process stopped due to terminal + i/o or profiling/debugging (unless you used SA_NOCLDSTOP) + + unix.SIGCONT + Child process resumed from profiling/debugging. + + unix.SIGSTOP + Child process stopped due to profiling/debugging. + + unix.SIGTSTP + Terminal CTRL-Z keystroke. + + unix.SIGTTIN + Terminal input for background process. + + unix.SIGTTOU + Terminal output for background process. + + unix.SIGXCPU + CPU time limit exceeded. + + unix.SIGXFSZ + File size limit exceeded. + + unix.SIGVTALRM + Virtual alarm clock. + + unix.SIGPROF + Profiling timer expired. + + unix.SIGWINCH + Terminal resized. + + unix.SIGPWR + Not implemented in most community editions of system five. + +──────────────────────────────────────────────────────────────────────────────── + UNIX ERRORS - - `EINVAL`: Invalid argument. Raised by [pretty much everything]. + unix.EINVAL + Invalid argument. - - `ENOSYS`: System call not available on this platform. On Windows - this is raised by chroot(), setuid(), setgid(), getsid(), setsid(). + Raised by [pretty much everything]. - - `ENOENT`: no such file or directory. Raised by access(), - alloc_hugepages(), bind(), chdir(), chmod(), chown(), chroot(), - clock_getres(), execve(), opendir(), inotify_add_watch(), kcmp(), - link(), mkdir(), mknod(), msgget(), open(), readlink(), rename(), - rmdir(), semget(), shmget(), stat(), swapon(), symlink(), - truncate(), unlink(), utime(), utimensat(). + unix.ENOSYS + System call not available on this platform. On Windows this is - - `ENOTDIR`: Not a directory. This means that a directory component in - a supplied path *existed* but wasn't a directory. For example, if - you try to `open("foo/bar")` and `foo` is a regular file, then - `ENOTDIR` will be returned. Raised by open(), access(), chdir(), - chroot(), execve(), link(), mkdir(), mknod(), opendir(), readlink(), - rename(), rmdir(), stat(), symlink(), truncate(), unlink(), - utimensat(), bind(), chmod(), chown(), fcntl(), futimesat(), - inotify_add_watch(). + Raised by chroot(), setuid(), setgid(), getsid(), setsid(). - - `EINTR`: The greatest of all errnos; crucial for building real time - reliable software. Raised by accept(), clock_nanosleep(), close(), - connect(), dup(), fcntl(), flock(), getrandom(), nanosleep(), - open(), pause(), poll(), ptrace(), read(), recv(), select(), send(), - sigsuspend(), sigwaitinfo(), truncate(), wait(), write() + unix.ENOENT + No such file or directory. - - `EIO`: Raised by access() acct() chdir() chmod() chown() chroot() - close() copy_file_range() execve() fallocate() fsync() ioperm() - link() madvise() mbind() pciconfig_read() ptrace() read() readlink() - sendfile() statfs() symlink() sync_file_range() truncate() unlink() - write() + Raised by access(), bind(), chdir(), chmod(), chown(), chroot(), + clock_getres(), execve(), opendir(), inotify_add_watch(), link(), + mkdir(), mknod(), open(), readlink(), rename(), rmdir(), stat(), + swapon(), symlink(), truncate(), unlink(), utime(), utimensat(). - - `ENXIO`: No such device or address. Raised by lseek(), open(), - prctl() + unix.ENOTDIR + Not a directory. This means that a directory component in a supplied + path *existed* but wasn't a directory. For example, if you try to + `open("foo/bar")` and `foo` is a regular file, then `ENOTDIR` will + be returned. - - `E2BIG`: Argument list too long. Raised by execve(), msgop(), - sched_setattr(), semop() + Raised by open(), access(), chdir(), chroot(), execve(), link(), + mkdir(), mknod(), opendir(), readlink(), rename(), rmdir(), stat(), + symlink(), truncate(), unlink(), utimensat(), bind(), chmod(), + chown(), fcntl(), futimesat(), inotify_add_watch(). - - `ENOEXEC`: exec format error. Raised by execve(), kexec_load(), - uselib() + unix.EINTR + The greatest of all errnos; crucial for building real time reliable + software. - - `ECHILD`: no child process. Raised by wait(), waitpid(), waitid(), - wait3(), wait4() + Raised by accept(), clock_nanosleep(), close(), connect(), dup(), + fcntl(), flock(), getrandom(), nanosleep(), open(), pause(), poll(), + ptrace(), read(), recv(), select(), send(), sigsuspend(), + sigwaitinfo(), truncate(), wait(), write(). - - `ESRCH`: No such process. Raised by getpriority(), getrlimit(), - getsid(), ioprio_set(), kill(), setpgid(), tkill(), utimensat(), + unix.EIO + Raised by access(), acct(), chdir(), chmod(), chown(), chroot(), + close(), copy_file_range(), execve(), fallocate(), fsync(), + ioperm(), link(), madvise(), mbind(), pciconfig_read(), ptrace(), + read(), readlink(), sendfile(), statfs(), symlink(), + sync_file_range(), truncate(), unlink(), write(). - - `EBADF`: bad file descriptor; cf. EBADFD. Raised by accept(), - access(), bind(), chdir(), chmod(), chown(), close(), connect(), - copy_file_range(), dup(), fcntl(), flock(), fsync(), futimesat(), - opendir(), getpeername(), getsockname(), getsockopt(), - inotify_add_watch(), inotify_rm_watch(), ioctl(), kcmp(), - kexec_load(), link(), listen(), llseek(), lseek(), mkdir(), mknod(), - mmap(), open(), prctl(), read(), readahead(), readlink(), recv(), - rename(), select(), send(), shutdown(), splice(), stat(), symlink(), - sync(), sync_file_range(), timerfd_create(), truncate(), unlink(), + unix.ENXIO + No such device or address. + + Raised by lseek(), open(), prctl() + + unix.E2BIG + Argument list too long. + + Raised by execve(), sched_setattr(). + + unix.ENOEXEC + Exec format error. + + Raised by execve(), uselib(). + + unix.ECHILD + No child process. + + Raised by wait(), waitpid(), waitid(), wait3(), wait4(). + + unix.ESRCH + No such process. + + Raised by getpriority(), getrlimit(), getsid(), ioprio_set(), + kill(), setpgid(), tkill(), utimensat(), + + unix.EBADF + Bad file descriptor; cf. EBADFD. + + Raised by accept(), access(), bind(), chdir(), chmod(), chown(), + close(), connect(), copy_file_range(), dup(), fcntl(), flock(), + fsync(), futimesat(), opendir(), getpeername(), getsockname(), + getsockopt(), inotify_add_watch(), inotify_rm_watch(), ioctl(), + link(), listen(), llseek(), lseek(), mkdir(), mknod(), mmap(), + open(), prctl(), read(), readahead(), readlink(), recv(), rename(), + select(), send(), shutdown(), splice(), stat(), symlink(), sync(), + sync_file_range(), timerfd_create(), truncate(), unlink(), utimensat(), write(), - - `EAGAIN`: resource temporarily unavailable (e.g. SO_RCVTIMEO - expired, too many processes, too much memory locked, read or write - with O_NONBLOCK needs polling, etc.). Raised by accept(), connect(), - eventfd(), fcntl(), fork(), getrandom(), mincore(), mlock(), mmap(), - mremap(), msgop(), poll(), read(), select(), send(), setresuid(), - setreuid(), setuid(), sigwaitinfo(), splice(), tee(), - timer_create(), timerfd_create(), tkill(), write(), + unix.EAGAIN + Resource temporarily unavailable (e.g. SO_RCVTIMEO expired, too many + processes, too much memory locked, read or write with O_NONBLOCK + needs polling, etc.). - - `EPIPE`: Broken pipe. Returned by write(), send(). This happens when - you try to write data to a subprocess via a pipe() but the reader - end has already closed, possibly because the process died. Normally - i/o routines only return this if `SIGPIPE` doesn't kill the process. + Raised by accept(), connect(), fcntl(), fork(), getrandom(), + mincore(), mlock(), mmap(), mremap(), poll(), read(), select(), + send(), setresuid(), setreuid(), setuid(), sigwaitinfo(), splice(), + tee(), timer_create(), timerfd_create(), tkill(), write(), + + unix.EPIPE + Broken pipe. Returned by write(), send(). This happens when you try + to write data to a subprocess via a pipe() but the reader end has + already closed, possibly because the process died. Normally i/o + routines only return this if `SIGPIPE` doesn't kill the process. Unlike default UNIX programs, redbean currently ignores `SIGPIPE` by default, so this error code is a distinct possibility when pipes or sockets are being used. - - `ENAMETOOLONG`: Filename too long. Cosmopolitan Libc currently - defines `PATH_MAX` as 512 characters. On UNIX, that limit should - only apply to system call wrappers like realpath(). On Windows NT - it's observed by all system calls that accept a pathname. Raised by - access(), bind(), chdir(), chmod(), chown(), chroot(), execve(), - gethostname(), inotify_add_watch(), link(), mkdir(), mknod(), - open(), readlink(), rename(), rmdir(), stat(), symlink(), + unix.ENAMETOOLONG + Filename too long. Cosmopolitan Libc currently defines `PATH_MAX` as + 512 characters. On UNIX, that limit should only apply to system call + wrappers like realpath(). On Windows NT it's observed by all system + calls that accept a pathname. + + Raised by access(), bind(), chdir(), chmod(), chown(), chroot(), + execve(), gethostname(), inotify_add_watch(), link(), mkdir(), + mknod(), open(), readlink(), rename(), rmdir(), stat(), symlink(), truncate(), u unlink(), utimensat() - - `EACCES`: Permission denied. Raised by access(), bind(), bpf(), - chdir(), chmod(), chown(), chroot(), clock_getres(), connect(), - execve(), fcntl(), getpriority(), inotify_add_watch(), link(), - mkdir(), mknod(), mmap(), mprotect(), msgctl(), msgget(), msgop(), - open(), prctl(), ptrace(), readlink(), rename(), rmdir(), semget(), - send(), setpgid(), shmget(), socket(), stat(), symlink(), + unix.EACCES + Permission denied. + + Raised by access(), bind(), chdir(), chmod(), chown(), chroot(), + clock_getres(), connect(), execve(), fcntl(), getpriority(), + inotify_add_watch(), link(), mkdir(), mknod(), mmap(), mprotect(), + msgctl(), open(), prctl(), ptrace(), readlink(), rename(), rmdir(), + semget(), send(), setpgid(), socket(), stat(), symlink(), truncate(), unlink(), uselib(), utime(), utimensat(), - - `ENOMEM`: We require more vespene gas. Raised by access(), bind(), - chdir(), chmod(), chown(), chroot(), clone(), copy_file_range(), - create_module(), eventfd(), execve(), fanotify_init(), fork(), + unix.ENOMEM + We require more vespene gas. + + Raised by access(), bind(), chdir(), chmod(), chown(), chroot(), + clone(), copy_file_range(), execve(), fanotify_init(), fork(), getgroups(), getrlimit(), inotify_add_watch(), inotify_init(), - ioperm(), kexec_load(), link(), mbind(), memfd_create(), mincore(), - mkdir(), mknod(), mlock(), mmap(), mprotect(), mremap(), msgget(), - msgop(), msync(), open(), poll(), readlink(), recv(), rename(), - rmdir(), select(), semget(), send(), shmget(), sigaltstack(), + ioperm(), link(), mbind(), mincore(), mkdir(), mknod(), mlock(), + mmap(), mprotect(), mremap(), msync(), open(), poll(), readlink(), + recv(), rename(), rmdir(), select(), send(), sigaltstack(), splice(), stat(), subpage_prot(), swapon(), symlink(), sync_file_range(), tee(), timer_create(), timerfd_create(), unlink(). - - `EPERM`: Operation not permitted. Raised by accept(), chmod(), - chown(), chroot(), copy_file_range(), execve(), fallocate(), - fanotify_init(), fcntl(), futex(), get_robust_list(), - getdomainname(), getgroups(), gethostname(), getpriority(), - getrlimit(), getsid(), gettimeofday(), idle(), init_module(), - io_submit(), ioctl_console(), ioctl_ficlonerange(), - ioctl_fideduperange(), ioctl_ns(), ioctl_tty(), ioperm(), iopl(), - ioprio_set(), kcmp(), kexec_load(), keyctl(), kill(), link(), - lookup_dcookie(), madvise(), mbind(), membarrier(), migrate_pages(), - mkdir(), mknod(), mlock(), mmap(), mount(), move_pages(), msgctl(), - nice(), open(), open_by_handle_at(), pciconfig_read(), - perf_event_open(), pidfd_getfd(), pidfd_send_signal(), pivot_root(), - prctl(), process_vm_readv(), ptrace(), quotactl(), reboot(), - rename(), request_key(), rmdir(), rt_sigqueueinfo(), - sched_setaffinity(), sched_setattr(), sched_setparam(), - sched_setscheduler(), semctl(), seteuid(), setfsgid(), setfsuid(), - setgid(), setns(), setpgid(), setresuid(), setreuid(), setsid(), - setuid(), setup(), setxattr(), shmctl(), shmget(), sigaltstack(), + unix.EPERM + Operation not permitted. + + Raised by accept(), chmod(), chown(), chroot(), copy_file_range(), + execve(), fallocate(), fanotify_init(), fcntl(), futex(), + get_robust_list(), getdomainname(), getgroups(), gethostname(), + getpriority(), getrlimit(), getsid(), gettimeofday(), idle(), + init_module(), io_submit(), ioctl_console(), ioctl_ficlonerange(), + ioctl_fideduperange(), ioperm(), iopl(), ioprio_set(), keyctl(), + kill(), link(), lookup_dcookie(), madvise(), mbind(), membarrier(), + migrate_pages(), mkdir(), mknod(), mlock(), mmap(), mount(), + move_pages(), msgctl(), nice(), open(), open_by_handle_at(), + pciconfig_read(), perf_event_open(), pidfd_getfd(), + pidfd_send_signal(), pivot_root(), prctl(), process_vm_readv(), + ptrace(), quotactl(), reboot(), rename(), request_key(), rmdir(), + rt_sigqueueinfo(), sched_setaffinity(), sched_setattr(), + sched_setparam(), sched_setscheduler(), seteuid(), setfsgid(), + setfsuid(), setgid(), setns(), setpgid(), setresuid(), setreuid(), + setsid(), setuid(), setup(), setxattr(), sigaltstack(), spu_create(), stime(), swapon(), symlink(), syslog(), truncate(), unlink(), utime(), utimensat(), write() - - `ENOTBLK`: Block device required. Raised by umount(). + unix.ENOTBLK + Block device required. - - `EBUSY`: Device or resource busy. Raised by bdflush(), dup(), - fcntl(), msync(), prctl(), ptrace(), rename(), rmdir(). + Raised by umount(). - - `EEXIST`: File exists. Raised by bpf(), create_module(), - inotify_add_watch(), link(), mkdir(), mknod(), mmap(), msgget(), - open(), rename(), rmdir(), semget(), shmget(), symlink() + unix.EBUSY + Device or resource busy. - - `EXDEV`: Improper link. Raised by copy_file_range(), link(), - rename() + Raised by dup(), fcntl(), msync(), prctl(), ptrace(), rename(), + rmdir(). - - `ENODEV`: No such device. Raised by arch_prctl(), eventfd(), mmap(), - open(), prctl(), timerfd_create() + unix.EEXIST + File exists. - - `EISDIR`: Is a a directory. Raised by copy_file_range(), execve(), - open(), read(), rename(), truncate(), unlink(). + Raised by inotify_add_watch(), link(), mkdir(), mknod(), mmap(), + open(), rename(), rmdir(), symlink() - - `ENFILE`: Too many open files in system. Raised by accept(), - eventfd(), execve(), inotify_init(), memfd_create(), mmap(), open(), - pipe(), shmget(), socket(), socketpair(), swapon(), - timerfd_create(), uselib(), userfaultfd(). + unix.EXDEV + Improper link. - - `EMFILE`: Too many open files. Raised by accept(), dup(), eventfd(), - execve(), fanotify_init(), fcntl(), inotify_init(), memfd_create(), - open(), pipe(), socket(), socketpair(), timerfd_create(). + Raised by copy_file_range(), link(), rename(). - - `ENOTTY`: Inappropriate i/o control operation. Raised by ioctl(). + unix.ENODEV + No such device. + + Raised by arch_prctl(), mmap(), open(), prctl(), timerfd_create(). + + unix.EISDIR + Is a a directory. + + Raised by copy_file_range(), execve(), open(), read(), rename(), + truncate(), unlink(). + + unix.ENFILE + Too many open files in system. + + Raised by accept(), execve(), inotify_init(), mmap(), open(), + pipe(), socket(), socketpair(), swapon(), timerfd_create(), + uselib(), userfaultfd(). + + unix.EMFILE + Too many open files. + + Raised by accept(), dup(), execve(), fanotify_init(), fcntl(), + inotify_init(), open(), pipe(), socket(), socketpair(), + timerfd_create(). + + unix.ENOTTY + Inappropriate i/o control operation. + + Raised by ioctl(). + + unix.ETXTBSY + Won't open executable that's executing in write mode. - - `ETXTBSY`: Won't open executable that's executing in write mode. Raised by access(), copy_file_range(), execve(), mmap(), open(), truncate(). - - `EFBIG`: File too large. Raised by copy_file_range(), open(), - truncate(), write(). + unix.EFBIG + File too large. - - `ENOSPC`: No space left on device. Raised by copy_file_range(), - fsync(), inotify_add_watch(), link(), mkdir(), mknod(), msgget(), - open(), rename(), semget(), shmget(), symlink(), sync_file_range(), + Raised by copy_file_range(), open(), truncate(), write(). + + unix.ENOSPC + No space left on device. + + Raised by copy_file_range(), fsync(), inotify_add_watch(), link(), + mkdir(), mknod(), open(), rename(), symlink(), sync_file_range(), write(). - - `EDQUOT`: Disk quota exceeded. Raised by link(), mkdir(), mknod(), - open(), rename(), symlink(), write() + unix.EDQUOT + Disk quota exceeded. - - `ESPIPE`: Invalid seek. Raised by lseek(), splice(), - sync_file_range(). - - - `EROFS`: Read-only filesystem. Raised by access(), bind(), chmod(), - chown(), link(), mkdir(), mknod(), open(), rename(), rmdir(), - symlink(), truncate(), unlink(), utime(), utimensat() - - - `EMLINK`: Too many links; raised by link(), mkdir(), rename() - - - `ERANGE`: Result too large. Raised by prctl(), semop(). - - - `EDEADLK`: Resource deadlock avoided. Raised by fcntl(). - - - `ENOLCK`: No locks available. Raised by fcntl(), flock(). - - - `ENOTEMPTY`: Directory not empty. Raised by rmdir(). - - - `ELOOP`: Too many levels of symbolic links. Raised by access(), - bind(), chdir(), chmod(), chown(), chroot(), execve(), link(), - mkdir(), mknod(), open(), readlink(), rename(), rmdir(), stat(), - symlink(), truncate(), unlink(), utimensat(). - - - `ENOMSG`: Raised by msgop(). - - - `EIDRM`: Identifier removed. Raised by msgctl(), msgget(), msgop(), - shmget(). - - - `ETIME`: Timer expired; timer expired. Raised by connect(). - - - `EPROTO`: Raised by accept(), connect(), socket(), socketpair(). - - - `EOVERFLOW`: Raised by copy_file_range(), fanotify_init(), lseek(), - mmap(), open(), stat(), statfs() - - - `ENOTSOCK`: Not a socket. Raised by accept(), bind(), connect(), - getpeername(), getsockname(), getsockopt(), listen(), recv(), - send(), shutdown(). - - - `EDESTADDRREQ`: Destination address required. Raised by send(), + Raised by link(), mkdir(), mknod(), open(), rename(), symlink(), write(). - - `EMSGSIZE`: Message too long. Raised by send(). + unix.ESPIPE + Invalid seek. - - `EPROTOTYPE`: Protocol wrong type for socket. Raised by connect(). + Raised by lseek(), splice(), sync_file_range(). - - `ENOPROTOOPT`: Protocol not available. Raised by getsockopt(), - accept(). + unix.EROFS + Read-only filesystem. - - `EPROTONOSUPPORT`: Protocol not supported. Raised by socket(), + Raised by access(), bind(), chmod(), chown(), link(), mkdir(), + mknod(), open(), rename(), rmdir(), symlink(), truncate(), unlink(), + utime(), utimensat(). + + unix.EMLINK + Too many links; + + raised by link(), mkdir(), rename(). + + unix.ERANGE + Result too large. + + Raised by prctl(). + + unix.EDEADLK + Resource deadlock avoided. + + Raised by fcntl(). + + unix.ENOLCK + No locks available. + + Raised by fcntl(), flock(). + + unix.ENOTEMPTY + Directory not empty. Raised by rmdir(). + + unix.ELOOP + Too many levels of symbolic links. Raised by access(), bind(), + chdir(), chmod(), chown(), chroot(), execve(), link(), mkdir(), + mknod(), open(), readlink(), rename(), rmdir(), stat(), symlink(), + truncate(), unlink(), utimensat(). + + unix.ENOMSG + Raised by msgop(). + + unix.EIDRM + Identifier removed. + + Raised by msgctl(). + + unix.ETIME + Timer expired; timer expired. + + Raised by connect(). + + unix.EPROTO + Raised by accept(), connect(), socket(), socketpair(). + + unix.EOVERFLOW + Raised by copy_file_range(), fanotify_init(), lseek(), mmap(), + open(), stat(), statfs() + + unix.ENOTSOCK + Not a socket. + + Raised by accept(), bind(), connect(), getpeername(), getsockname(), + getsockopt(), listen(), recv(), send(), shutdown(). + + unix.EDESTADDRREQ + Destination address required. + + Raised by send(), write(). + + unix.EMSGSIZE + Message too long. + + Raised by send(). + + unix.EPROTOTYPE + Protocol wrong type for socket. + + Raised by connect(). + + unix.ENOPROTOOPT + Protocol not available. + + Raised by getsockopt(), accept(). + + unix.EPROTONOSUPPORT + Protocol not supported. + + Raised by socket(), socketpair(). + + unix.ESOCKTNOSUPPORT + Socket type not supported. + + unix.ENOTSUP + Operation not supported. + + Raised by chmod(), clock_getres(), clock_nanosleep(), + timer_create(). + + unix.EOPNOTSUPP + Socket operation not supported. + + Raised by accept(), listen(), mmap(), prctl(), readv(), send(), socketpair(). - - `ESOCKTNOSUPPORT`: Socket type not supported. + unix.EPFNOSUPPORT + Protocol family not supported. - - `ENOTSUP`: Operation not supported. Raised by chmod(), - clock_getres(), clock_nanosleep(), timer_create() + unix.EAFNOSUPPORT + Address family not supported. - - `EOPNOTSUPP`: Socket operation not supported. Raised by accept(), - listen(), mmap(), prctl(), readv(), send(), socketpair(), + Raised by connect(), socket(), socketpair() - - `EPFNOSUPPORT`: protocol family not supported + unix.EADDRINUSE + Address already in use. - - `EAFNOSUPPORT`: address family not supported. Raised by connect(), - socket(), socketpair() + Raised by bind(), connect(), listen() - - `EADDRINUSE`: address already in use. Raised by bind(), connect(), - listen() + unix.EADDRNOTAVAIL + Address not available. - - `EADDRNOTAVAIL`: address not available. Raised by bind(), connect(). + Raised by bind(), connect(). - - `ENETDOWN`: network is down; ; WSAENETDOWN. Raised by accept() + unix.ENETDOWN + Network is down. - - `ENETUNREACH`: host is unreachable; ; WSAENETUNREACH. Raised by - accept(), connect() + Raised by accept() - - `ENETRESET`: connection reset by network + unix.ENETUNREACH + Host is unreachable. - - `ECONNABORTED`: connection reset before accept. Raised by accept() + Raised by accept(), connect() - - `ECONNRESET`: connection reset by client. Raised by send(), + unix.ENETRESET + Connection reset by network. - - `ENOBUFS`: no buffer space available; raised by getpeername(), - getsockname(), send(), + unix.ECONNABORTED + Connection reset before accept. - - `EISCONN`: socket is connected. Raised by connect(), send(). + Raised by accept(). - - `ENOTCONN`: socket is not connected. Raised by getpeername(), - recv(), send(), shutdown(), + unix.ECONNRESET + Connection reset by client. - - `ESHUTDOWN`: cannot send after transport endpoint shutdown; note - that shutdown write is an `EPIPE` + Raised by send(). - - `ETOOMANYREFS`: too many references: cannot splice. Raised by - sendmsg(), + unix.ENOBUFS + No buffer space available; - - `ETIMEDOUT`: connection timed out; ; WSAETIMEDOUT; raised by - connect(), + raised by getpeername(), getsockname(), send(). - - `ECONNREFUSED`: system-imposed limit on the number of threads was - encountered.; WSAECONNREFUSED. Raised by connect(), listen(), recv() + unix.EISCONN + Socket is connected. - - `EHOSTDOWN`: Host is down. Raised by accept() + Raised by connect(), send(). - - `EHOSTUNREACH`: Host is unreachable. Raised by accept() + unix.ENOTCONN + Socket is not connected. - - `EALREADY`: Connection already in progress. Raised by connect(), - send() + Raised by getpeername(), recv(), send(), shutdown(). - - `ENODATA`: No message is available in xsi stream or named pipe is - being closed; no data available; barely in posix; returned by ioctl; - very close in spirit to EPIPE? + unix.ESHUTDOWN + Cannot send after transport endpoint shutdown; note that shutdown + write is an `EPIPE`. + unix.ETOOMANYREFS + Too many references: cannot splice. + + Raised by sendmsg(). + + unix.ETIMEDOUT + Connection timed out; ; WSAETIMEDOUT; + + raised by connect(). + + unix.ECONNREFUSED + System-imposed limit on the number of threads was encountered. + + Raised by connect(), listen(), recv() + + unix.EHOSTDOWN + Host is down. + + Raised by accept() + + unix.EHOSTUNREACH + Host is unreachable. + + Raised by accept() + + unix.EALREADY + Connection already in progress. + + Raised by connect(), send() + + unix.ENODATA + No message is available in xsi stream or named pipe is being closed; + no data available; barely in posix; returned by ioctl; very close in + spirit to EPIPE? + +──────────────────────────────────────────────────────────────────────────────── LUA ENHANCEMENTS We've made some enhancements to the Lua language that should make it - more comfortable for C/C++ and Python developers. Some of these + more comfortable for C/C++ and Python developers. Some of these - redbean supports a printf modulus operator, like Python. For example, you can say `"hello %s" % {"world"}` instead of @@ -2691,6 +3482,7 @@ LUA ENHANCEMENTS - redbean supports the GNU syntax for the ASCII ESC character in string literals. For example, `"\e"` is the same as `"\x1b"`. +──────────────────────────────────────────────────────────────────────────────── SEE ALSO diff --git a/tool/net/lfuncs.c b/tool/net/lfuncs.c index d1be7df68..d828985ba 100644 --- a/tool/net/lfuncs.c +++ b/tool/net/lfuncs.c @@ -33,6 +33,7 @@ #include "libc/nexgen32e/rdtscp.h" #include "libc/rand/rand.h" #include "libc/runtime/gc.internal.h" +#include "libc/runtime/sysconf.h" #include "libc/sock/sock.h" #include "libc/sysv/consts/af.h" #include "libc/sysv/consts/rusage.h" @@ -82,6 +83,11 @@ int LuaGetCpuCore(lua_State *L) { return 1; } +int LuaGetCpuCount(lua_State *L) { + lua_pushinteger(L, GetCpuCount()); + return 1; +} + int LuaGetLogLevel(lua_State *L) { lua_pushinteger(L, __log_level); return 1; diff --git a/tool/net/lfuncs.h b/tool/net/lfuncs.h index b731d8a03..991ec9eea 100644 --- a/tool/net/lfuncs.h +++ b/tool/net/lfuncs.h @@ -35,6 +35,7 @@ int LuaEscapeUser(lua_State *); int LuaFormatHttpDateTime(lua_State *); int LuaFormatIp(lua_State *); int LuaGetCpuCore(lua_State *); +int LuaGetCpuCount(lua_State *); int LuaGetCpuNode(lua_State *); int LuaGetCryptoHash(lua_State *); int LuaGetHostOs(lua_State *); diff --git a/tool/net/lunix.c b/tool/net/lunix.c index cc3b47d10..f1b1fc567 100644 --- a/tool/net/lunix.c +++ b/tool/net/lunix.c @@ -17,10 +17,13 @@ │ 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/siginfo.h" @@ -34,14 +37,18 @@ #include "libc/fmt/fmt.h" #include "libc/fmt/magnumstrs.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/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" @@ -52,6 +59,7 @@ #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" @@ -70,10 +78,12 @@ #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" @@ -95,11 +105,44 @@ struct UnixStat { struct UnixErrno { int refs; - int errno; + uint16_t errno; + uint16_t winerr; + const char *call; }; static lua_State *GL; +static void *LuaUnixRealloc(lua_State *L, void *p, size_t n) { + void *p2; + if ((p2 = realloc(p, n))) { + return p2; + } + if (IsLegalSize(n)) { + luaC_fullgc(L, 1); + p2 = realloc(p, n); + } + return p2; +} + +static void *LuaUnixAllocRaw(lua_State *L, size_t n) { + return LuaUnixRealloc(L, 0, n); +} + +static void *LuaUnixAlloc(lua_State *L, size_t n) { + void *p; + if ((p = LuaUnixAllocRaw(L, n))) { + bzero(p, n); + } + return p; +} + +static void LuaPushSigset(lua_State *L, struct sigset set) { + struct sigset *sp; + sp = lua_newuserdatauv(L, sizeof(*sp), 1); + luaL_setmetatable(L, "unix.Sigset"); + *sp = set; +} + static void LuaSetIntField(lua_State *L, const char *k, lua_Integer v) { lua_pushinteger(L, v); lua_setfield(L, -2, k); @@ -110,45 +153,42 @@ static dontinline int ReturnInteger(lua_State *L, lua_Integer 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 void LuaUnixPushErrno(lua_State *L, int err) { +static void LuaUnixPushErrno(lua_State *L, const char *sc, int uerr, int werr) { struct UnixErrno *ue, **uep; - ue = xcalloc(1, sizeof(struct UnixErrno)); + ue = LuaUnixAlloc(L, sizeof(*ue)); ue->refs = 1; - ue->errno = err; + ue->call = sc; + ue->errno = uerr; + ue->winerr = werr; uep = lua_newuserdatauv(L, sizeof(*uep), 1); luaL_setmetatable(L, "unix.Errno"); *uep = ue; } -static dontinline int SysretErrnoImpl(lua_State *L, int olderr, bool usebool) { - int i, newerr = errno; - if (!IsTiny() && !(0 < newerr && newerr < (!IsWindows() ? 4096 : 65536))) { - WARNF("errno should not be %d", newerr); +static dontinline int SysretErrno(lua_State *L, const char *call, int olderr) { + int i, unixerr, winerr; + unixerr = errno; + winerr = GetLastError(); + if (!IsTiny() && !(0 < unixerr && unixerr < (!IsWindows() ? 4096 : 65536))) { + WARNF("errno should not be %d", unixerr); } - if (usebool) { - lua_pushboolean(L, false); - } else { - lua_pushnil(L); - } - LuaUnixPushErrno(L, newerr); + lua_pushnil(L); + LuaUnixPushErrno(L, call, unixerr, winerr); errno = olderr; return 2; } -static dontinline int SysretErrnoNil(lua_State *L, int olderr) { - return SysretErrnoImpl(L, olderr, false); -} - -static dontinline int SysretErrnoBool(lua_State *L, int olderr) { - return SysretErrnoImpl(L, olderr, true); -} - -static int SysretBool(lua_State *L, int rc, int olderr) { +static 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); } @@ -156,11 +196,12 @@ static int SysretBool(lua_State *L, int rc, int olderr) { lua_pushboolean(L, true); return 1; } else { - return SysretErrnoBool(L, olderr); + return SysretErrno(L, call, olderr); } } -static int SysretInteger(lua_State *L, int64_t rc, int 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); @@ -168,7 +209,7 @@ static int SysretInteger(lua_State *L, int64_t rc, int olderr) { lua_pushinteger(L, rc); return 1; } else { - return SysretErrnoNil(L, olderr); + return SysretErrno(L, call, olderr); } } @@ -196,7 +237,7 @@ static char **ConvertLuaArrayToStringList(lua_State *L, int i) { lua_len(L, i); n = lua_tointeger(L, -1); lua_pop(L, 1); - if ((p = calloc(n + 1, sizeof(*p)))) { + if ((p = LuaUnixAllocRaw(L, (n + 1) * sizeof(*p)))) { for (j = 1; j <= n; ++j) { lua_geti(L, i, j); s = strdup(lua_tostring(L, -1)); @@ -209,6 +250,7 @@ static char **ConvertLuaArrayToStringList(lua_State *L, int i) { break; } } + p[j - 1] = 0; } return p; } @@ -216,130 +258,203 @@ static char **ConvertLuaArrayToStringList(lua_State *L, int i) { //////////////////////////////////////////////////////////////////////////////// // System Calls -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(mask:int) → oldmask:int -static int LuaUnixUmask(lua_State *L) { - return ReturnInteger(L, umask(luaL_checkinteger(L, 1))); -} - -// unix.exit([exitcode:int]) → ⊥ +// unix.exit([exitcode:int]) +// └─→ ⊥ static wontreturn int LuaUnixExit(lua_State *L) { _Exit(luaL_optinteger(L, 1, 0)); } -// unix.access(path:str, how:int) → ok:bool, unix.Errno -// how can be: R_OK, W_OK, X_OK, F_OK -static int LuaUnixAccess(lua_State *L) { - int olderr = errno; - return SysretBool(L, access(luaL_checkstring(L, 1), luaL_checkinteger(L, 2)), - olderr); +static dontinline int LuaUnixGetid(lua_State *L, int f(void)) { + return ReturnInteger(L, f()); } -// unix.mkdir(path:str[, mode:int]) → ok:bool, unix.Errno -// mode should be octal +// 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(luaL_checkstring(L, 1), luaL_optinteger(L, 2, 0755)), olderr); + 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]) → ok:bool, unix.Errno -// mode should be octal +// unix.makedirs(path:str[, mode:int]) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixMakedirs(lua_State *L) { int olderr = errno; return SysretBool( - L, makedirs(luaL_checkstring(L, 1), luaL_optinteger(L, 2, 0755)), olderr); + L, "makedirs", olderr, + makedirs(luaL_checkstring(L, 1), luaL_optinteger(L, 2, 0755))); } -// unix.chdir(path:str) → ok:bool, unix.Errno +// unix.chdir(path:str) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixChdir(lua_State *L) { int olderr = errno; - return SysretBool(L, chdir(luaL_checkstring(L, 1)), olderr); + return SysretBool(L, "chdir", olderr, chdir(luaL_checkstring(L, 1))); } -// unix.unlink(path:str) → ok:bool, unix.Errno +// unix.unlink(path:str[, dirfd:int]) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixUnlink(lua_State *L) { int olderr = errno; - return SysretBool(L, unlink(luaL_checkstring(L, 1)), olderr); + return SysretBool( + L, "unlink", olderr, + unlinkat(luaL_optinteger(L, 2, AT_FDCWD), luaL_checkstring(L, 1), 0)); } -// unix.rmdir(path:str) → ok:bool, unix.Errno +// unix.rmdir(path:str[, dirfd:int]) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixRmdir(lua_State *L) { int olderr = errno; - return SysretBool(L, rmdir(luaL_checkstring(L, 1)), olderr); + return SysretBool(L, "rmdir", olderr, + unlinkat(luaL_optinteger(L, 2, AT_FDCWD), + luaL_checkstring(L, 1), AT_REMOVEDIR)); } -// unix.rename(oldpath:str, newpath:str) → ok:bool, unix.Errno +// 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(luaL_checkstring(L, 1), luaL_checkstring(L, 2)), - olderr); + 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) → ok:bool, unix.Errno +// 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(luaL_checkstring(L, 1), luaL_checkstring(L, 2)), - olderr); + 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) → ok:bool, unix.Errno +// 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(luaL_checkstring(L, 1), luaL_checkstring(L, 2)), - olderr); + 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) → ok:bool, unix.Errno +// 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(luaL_checkstring(L, 1), luaL_checkinteger(L, 2), - luaL_checkinteger(L, 3)), - olderr); + 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) → ok:bool, unix.Errno +// 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(luaL_checkstring(L, 1), luaL_checkinteger(L, 2)), - olderr); + 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.getcwd() → path:str, unix.Errno +// unix.readlink(path:str[, dirfd:int]) +// ├─→ content:str +// └─→ nil, unix.Errno +static int LuaUnixReadlink(lua_State *L) { + char *buf; + ssize_t rc; + int olderr = errno; + size_t got, bufsiz = 8192; + if ((buf = LuaUnixAllocRaw(L, bufsiz))) { + if ((rc = readlinkat(luaL_optinteger(L, 2, AT_FDCWD), + luaL_checkstring(L, 1), 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) { int olderr; char *path; @@ -349,17 +464,21 @@ static int LuaUnixGetcwd(lua_State *L) { free(path); return 1; } else { - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "getcwd", olderr); } } -// unix.fork() → childpid|0:int, unix.Errno +// unix.fork() +// ├─┬─→ 0 +// │ └─→ childpid:int +// └─→ nil, unix.Errno static int LuaUnixFork(lua_State *L) { int olderr = errno; - return SysretInteger(L, fork(), olderr); + return SysretInteger(L, "fork", olderr, fork()); } -// unix.environ() → {str,...} +// unix.environ() +// └─→ {str,...} static int LuaUnixEnviron(lua_State *L) { int i; char **e; @@ -371,7 +490,8 @@ static int LuaUnixEnviron(lua_State *L) { return 1; } -// unix.execve(prog:str[, args:List<*>, env:List<*>]) → false, unix.Errno +// unix.execve(prog:str[, args:List<*>, env:List<*>]) +// └─→ nil, unix.Errno static int LuaUnixExecve(lua_State *L) { int olderr; const char *prog; @@ -386,14 +506,14 @@ static int LuaUnixExecve(lua_State *L) { freeme2 = envp; } else { FreeStringList(argv); - return SysretErrnoBool(L, olderr); + return SysretErrno(L, "execve", olderr); } } else { envp = environ; freeme2 = 0; } } else { - return SysretErrnoBool(L, olderr); + return SysretErrno(L, "execve", olderr); } } else { ezargs[0] = prog; @@ -406,31 +526,35 @@ static int LuaUnixExecve(lua_State *L) { execve(prog, argv, envp); FreeStringList(freeme1); FreeStringList(freeme2); - return SysretErrnoBool(L, olderr); + return SysretErrno(L, "execve", olderr); } -// unix.commandv(prog:str) → path:str, unix.Errno +// 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); - if ((pathbuf = malloc(PATH_MAX + 1))) { + if ((pathbuf = LuaUnixAllocRaw(L, PATH_MAX + 1))) { if ((resolved = commandv(prog, pathbuf, PATH_MAX + 1))) { lua_pushstring(L, resolved); free(pathbuf); return 1; } else { free(pathbuf); - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "commandv", olderr); } } else { - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "commandv", olderr); } } -// unix.realpath(path:str) → path:str, unix.Errno +// unix.realpath(path:str) +// ├─→ path:str +// └─→ nil, unix.Errno static int LuaUnixRealpath(lua_State *L) { char *resolved; int olderr; @@ -442,7 +566,7 @@ static int LuaUnixRealpath(lua_State *L) { free(resolved); return 1; } else { - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "realpath", olderr); } } @@ -452,24 +576,29 @@ static int LuaUnixSyslog(lua_State *L) { return 0; } -// unix.chroot(path:str) → ok:bool, unix.Errno +// unix.chroot(path:str) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixChroot(lua_State *L) { int olderr = errno; - return SysretBool(L, chroot(luaL_checkstring(L, 1)), olderr); + return SysretBool(L, "chroot", olderr, chroot(luaL_checkstring(L, 1))); } -// unix.setrlimit(resource:int, soft:int[, hard:int]) → ok:bool, unix.Errno +// 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, + L, "setrlimit", olderr, setrlimit(luaL_checkinteger(L, 1), - &(struct rlimit){soft, luaL_optinteger(L, 3, soft)}), - olderr); + &(struct rlimit){soft, luaL_optinteger(L, 3, soft)})); } -// unix.getrlimit(resource:int) → soft:int, unix.Errno, hard:int +// unix.getrlimit(resource:int) +// ├─→ soft:int, hard:int +// └─→ nil, unix.Errno static int LuaUnixGetrlimit(lua_State *L) { struct rlimit rlim; int rc, olderr, resource; @@ -477,51 +606,58 @@ static int LuaUnixGetrlimit(lua_State *L) { resource = luaL_checkinteger(L, 1); if (!getrlimit(resource, &rlim)) { lua_pushinteger(L, rlim.rlim_cur < RLIM_INFINITY ? rlim.rlim_cur : -1); - lua_pushnil(L); lua_pushinteger(L, rlim.rlim_max < RLIM_INFINITY ? rlim.rlim_max : -1); return 3; } else { - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "getrlimit", olderr); } } -// unix.kill(pid:int, sig:int) → ok:bool, unix.Errno +// unix.kill(pid:int, sig:int) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixKill(lua_State *L) { int olderr = errno; - return SysretBool(L, kill(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2)), - olderr); + return SysretBool(L, "kill", olderr, + kill(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2))); } -// unix.raise(sig:int) → rc:int, unix.Errno +// unix.raise(sig:int) +// ├─→ rc:int +// └─→ nil, unix.Errno static int LuaUnixRaise(lua_State *L) { int olderr = errno; - return SysretInteger(L, raise(luaL_checkinteger(L, 1)), olderr); + return SysretInteger(L, "raise", olderr, raise(luaL_checkinteger(L, 1))); } -// unix.wait([pid:int, options:int]) → pid:int, unix.Errno, wstatus:int +// unix.wait([pid:int, options:int]) +// ├─→ pid:int, wstatus:int +// └─→ nil, unix.Errno static int LuaUnixWait(lua_State *L) { int pid, wstatus, olderr = errno; if ((pid = wait4(luaL_optinteger(L, 1, -1), &wstatus, luaL_optinteger(L, 2, 0), 0)) != -1) { lua_pushinteger(L, pid); - lua_pushnil(L); lua_pushinteger(L, wstatus); return 3; } else { - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "wait", olderr); } } -// unix.fcntl(fd:int, cmd:int[, arg:int]) → rc:int, unix.Errno +// unix.fcntl(fd:int, cmd:int[, arg:int]) +// ├─→ true, ... +// └─→ nil, unix.Errno static int LuaUnixFcntl(lua_State *L) { int olderr = errno; - return SysretInteger(L, - fcntl(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2), - luaL_optinteger(L, 3, 0)), - olderr); + 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, unix.Errno +// 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; @@ -533,111 +669,133 @@ static int LuaUnixDup(lua_State *L) { } else { rc = dup3(oldfd, newfd, flags); } - return SysretInteger(L, rc, olderr); + return SysretInteger(L, "dup", olderr, rc); } -// unix.pipe([flags:int]) → reader:int, unix.Errno, writer:int +// 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_pushnil(L); lua_pushinteger(L, pipefd[1]); return 2; } else { - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "pipe", olderr); } } -// unix.getsid(pid) → sid:int, unix.Errno +// unix.getsid(pid:int) +// ├─→ sid:int +// └─→ nil, unix.Errno static int LuaUnixGetsid(lua_State *L) { int olderr = errno; - return SysretInteger(L, getsid(luaL_checkinteger(L, 1)), olderr); + return SysretInteger(L, "getsid", olderr, getsid(luaL_checkinteger(L, 1))); } -static dontinline int LuaUnixRc0(lua_State *L, int f(void)) { +static dontinline int LuaUnixRc0(lua_State *L, const char *call, int f(void)) { int olderr = errno; - return SysretInteger(L, f(), olderr); + return SysretInteger(L, call, olderr, f()); } -// unix.getpgrp() → pgid:int, unix.Errno +// unix.getpgrp() +// ├─→ pgid:int +// └─→ nil, unix.Errno static int LuaUnixGetpgrp(lua_State *L) { - return LuaUnixRc0(L, getpgrp); + return LuaUnixRc0(L, "getpgrp", getpgrp); } -// unix.setpgrp() → pgid:int, unix.Errno +// unix.setpgrp() +// ├─→ pgid:int +// └─→ nil, unix.Errno static int LuaUnixSetpgrp(lua_State *L) { - return LuaUnixRc0(L, setpgrp); + return LuaUnixRc0(L, "setpgrp", setpgrp); } -// unix.setsid() → sid:int, unix.Errno +// unix.setsid() +// ├─→ sid:int +// └─→ nil, unix.Errno static int LuaUnixSetsid(lua_State *L) { - return LuaUnixRc0(L, setsid); + return LuaUnixRc0(L, "setsid", setsid); } -// unix.getpgid(pid:int) → pgid:int, unix.Errno +// unix.getpgid(pid:int) +// ├─→ pgid:int +// └─→ nil, unix.Errno static int LuaUnixGetpgid(lua_State *L) { int olderr = errno; - return SysretInteger(L, getpgid(luaL_checkinteger(L, 1)), olderr); + return SysretInteger(L, "getpgid", olderr, getpgid(luaL_checkinteger(L, 1))); } -// unix.setpgid(pid:int, pgid:int) → pgid:int, unix.Errno +// unix.setpgid(pid:int, pgid:int) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixSetpgid(lua_State *L) { int olderr = errno; - return SysretInteger( - L, setpgid(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2)), olderr); + return SysretBool(L, "setpgid", olderr, + setpgid(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2))); } -static dontinline int LuaUnixSetid(lua_State *L, int f(int)) { +static dontinline int LuaUnixSetid(lua_State *L, const char *call, int f(int)) { int olderr = errno; - return SysretBool(L, f(luaL_checkinteger(L, 1)), olderr); + return SysretBool(L, call, olderr, f(luaL_checkinteger(L, 1))); } -// unix.setuid(uid:int) → ok:bool, unix.Errno +// unix.setuid(uid:int) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixSetuid(lua_State *L) { - return LuaUnixSetid(L, setuid); + return LuaUnixSetid(L, "setuid", setuid); } -// unix.setgid(gid:int) → ok:bool, unix.Errno +// unix.setgid(gid:int) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixSetgid(lua_State *L) { - return LuaUnixSetid(L, setgid); + return LuaUnixSetid(L, "setgid", setgid); } -static dontinline int LuaUnixSetresid(lua_State *L, +static dontinline int LuaUnixSetresid(lua_State *L, const char *call, int f(uint32_t, uint32_t, uint32_t)) { int olderr = errno; - return SysretBool(L, + return SysretBool(L, call, olderr, f(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2), - luaL_checkinteger(L, 3)), - olderr); + luaL_checkinteger(L, 3))); } -// unix.setresuid(real:int, effective:int, saved:int) → ok:bool, unix.Errno +// unix.setresuid(real:int, effective:int, saved:int) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixSetresuid(lua_State *L) { - return LuaUnixSetresid(L, setresuid); + return LuaUnixSetresid(L, "setresuid", setresuid); } -// unix.setresgid(real:int, effective:int, saved:int) → ok:bool, unix.Errno +// unix.setresgid(real:int, effective:int, saved:int) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixSetresgid(lua_State *L) { - return LuaUnixSetresid(L, setresgid); + return LuaUnixSetresid(L, "setresgid", setresgid); } -// unix.clock_gettime([clock:int]) → seconds:int, unix.Errno, nanos:int +// 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_pushnil(L); lua_pushinteger(L, ts.tv_nsec); - return 3; + return 2; } else { - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "clock_gettime", olderr); } } // unix.nanosleep(seconds:int, nanos:int) -// → remseconds:int, unix.Errno, remnanos:int +// ├─→ remseconds:int, remnanos:int +// └─→ nil, unix.Errno static int LuaUnixNanosleep(lua_State *L) { int olderr = errno; struct timespec req, rem; @@ -645,11 +803,10 @@ static int LuaUnixNanosleep(lua_State *L) { req.tv_nsec = luaL_optinteger(L, 2, 0); if (!nanosleep(&req, &rem)) { lua_pushinteger(L, rem.tv_sec); - lua_pushnil(L); lua_pushinteger(L, rem.tv_nsec); - return 3; + return 2; } else { - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "nanosleep", olderr); } } @@ -659,68 +816,85 @@ static int LuaUnixSync(lua_State *L) { return 0; } -// unix.fsync(fd:int) → ok:bool, unix.Errno +// unix.fsync(fd:int) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixFsync(lua_State *L) { int olderr = errno; - return SysretBool(L, fsync(luaL_checkinteger(L, 1)), olderr); + return SysretBool(L, "fsync", olderr, fsync(luaL_checkinteger(L, 1))); } -// unix.fdatasync(fd:int) → ok:bool, unix.Errno +// unix.fdatasync(fd:int) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixFdatasync(lua_State *L) { int olderr = errno; - return SysretBool(L, fdatasync(luaL_checkinteger(L, 1)), olderr); + return SysretBool(L, "fdatasync", olderr, fdatasync(luaL_checkinteger(L, 1))); } -// unix.open(path:str, flags:int[, mode:int]) → fd:int, unix.Errno +// 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(luaL_checkstring(L, 1), luaL_checkinteger(L, 2), - luaL_optinteger(L, 3, 0)), - olderr); + return SysretInteger( + L, "open", olderr, + openat(luaL_optinteger(L, 4, AT_FDCWD), luaL_checkstring(L, 1), + luaL_checkinteger(L, 2), luaL_optinteger(L, 3, 0))); } -// unix.close(fd:int) → ok:bool, unix.Errno +// unix.close(fd:int) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixClose(lua_State *L) { int olderr = errno; - return SysretBool(L, close(luaL_checkinteger(L, 1)), olderr); + return SysretBool(L, "close", olderr, close(luaL_checkinteger(L, 1))); } -// unix.lseek(fd:int, offset:int[, whence:int]) → newpos:int, unix.Errno +// 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, + return SysretInteger(L, "lseek", olderr, lseek(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2), - luaL_optinteger(L, 3, SEEK_SET)), - olderr); + luaL_optinteger(L, 3, SEEK_SET))); } -// unix.truncate(path:str[, length:int]) → ok:bool, unix.Errno +// unix.truncate(path:str[, length:int]) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixTruncate(lua_State *L) { int olderr = errno; - return SysretBool( - L, truncate(luaL_checkstring(L, 1), luaL_optinteger(L, 2, 0)), olderr); + return SysretBool(L, "truncate", olderr, + truncate(luaL_checkstring(L, 1), luaL_optinteger(L, 2, 0))); } -// unix.ftruncate(fd:int[, length:int]) → ok:bool, unix.Errno +// unix.ftruncate(fd:int[, length:int]) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixFtruncate(lua_State *L) { int olderr = errno; return SysretBool( - L, ftruncate(luaL_checkinteger(L, 1), luaL_optinteger(L, 2, 0)), olderr); + L, "ftruncate", olderr, + ftruncate(luaL_checkinteger(L, 1), luaL_optinteger(L, 2, 0))); } -// unix.read(fd:int[, bufsiz:str, offset:int]) → data:str, unix.Errno +// 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; - int64_t rc, bufsiz, offset; + 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); - if ((buf = malloc(bufsiz))) { + if ((buf = LuaUnixAllocRaw(L, bufsiz))) { if (offset == -1) { rc = read(fd, buf, bufsiz); } else { @@ -733,19 +907,22 @@ static int LuaUnixRead(lua_State *L) { return 1; } else { free(buf); - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "read", olderr); } } else { - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "read", olderr); } } -// unix.write(fd:int, data:str[, offset:int]) → rc:int, unix.Errno +// 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; - int64_t rc, offset; + lua_Integer offset; olderr = errno; fd = luaL_checkinteger(L, 1); data = luaL_checklstring(L, 2, &size); @@ -756,7 +933,7 @@ static int LuaUnixWrite(lua_State *L) { } else { rc = pwrite(fd, data, size, offset); } - return SysretInteger(L, rc, olderr); + return SysretInteger(L, "write", olderr, rc); } static int ReturnStat(lua_State *L, struct UnixStat *ust) { @@ -768,72 +945,40 @@ static int ReturnStat(lua_State *L, struct UnixStat *ust) { return 1; } -// unix.stat(path:str) → unix.Stat, unix.Errno +// unix.stat(path:str[, flags:int[, dirfd:int]]) +// ├─→ unix.Stat +// └─→ nil, unix.Errno static int LuaUnixStat(lua_State *L) { const char *path; - int olderr = errno; struct UnixStat *ust; + int dirfd, flags, olderr = errno; path = luaL_checkstring(L, 1); - if ((ust = malloc(sizeof(*ust)))) { - if (!stat(path, &ust->st)) { + flags = luaL_optinteger(L, 2, 0); + dirfd = luaL_optinteger(L, 3, AT_FDCWD); + if ((ust = LuaUnixAllocRaw(L, sizeof(*ust)))) { + if (!fstatat(dirfd, path, &ust->st, flags)) { return ReturnStat(L, ust); } free(ust); } - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "stat", olderr); } -// unix.fstat(fd:int) → unix.Stat, unix.Errno +// unix.fstat(fd:int) +// ├─→ unix.Stat +// └─→ nil, unix.Errno static int LuaUnixFstat(lua_State *L) { int fd, olderr = errno; struct UnixStat *ust; olderr = errno; fd = luaL_checkinteger(L, 1); - if ((ust = malloc(sizeof(*ust)))) { + if ((ust = LuaUnixAllocRaw(L, sizeof(*ust)))) { if (!fstat(fd, &ust->st)) { return ReturnStat(L, ust); } free(ust); } - return SysretErrnoNil(L, olderr); -} - -static int ReturnDir(lua_State *L, struct UnixDir *udir) { - struct UnixDir **udirp; - udir->refs = 1; - udirp = lua_newuserdatauv(L, sizeof(*udirp), 1); - luaL_setmetatable(L, "unix.Dir"); - *udirp = udir; - return 1; -} - -// unix.opendir(path:str) → unix.Dir, unix.Errno -static int LuaUnixOpendir(lua_State *L) { - int olderr = errno; - const char *path; - struct UnixDir *udir; - path = luaL_checkstring(L, 1); - if ((udir = calloc(1, sizeof(*udir)))) { - if ((udir->dir = opendir(path))) { - return ReturnDir(L, udir); - } - free(udir); - } - return SysretErrnoNil(L, olderr); -} - -// unix.fdopendir(fd:int) → unix.Dir, unix.Errno -static int LuaUnixFdopendir(lua_State *L) { - int fd, olderr = errno; - struct UnixDir *udir; - fd = luaL_checkinteger(L, 1); - if ((udir = calloc(1, sizeof(*udir)))) { - if ((udir->dir = fdopendir(fd))) { - return ReturnDir(L, udir); - } - free(udir); - } - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "fstat", olderr); } static bool IsSockoptBool(int l, int x) { @@ -893,157 +1038,167 @@ static bool IsSockoptTimeval(int l, int x) { } } -// unix.setsockopt(fd:int, level:int, optname:int, ...) -// → ok:bool, unix.Errno static int LuaUnixSetsockopt(lua_State *L) { + void *optval; struct linger l; + uint32_t optsize; struct timeval tv; - int rc, fd, level, optname, optval, olderr = errno; + 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)) { - optval = lua_toboolean(L, 4); - return SysretBool( - L, setsockopt(fd, level, optname, &optval, sizeof(optval)), olderr); + // 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)) { - optval = luaL_checkinteger(L, 4); - return SysretBool( - L, setsockopt(fd, level, optname, &optval, sizeof(optval)), olderr); + // 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); - tv.tv_usec = luaL_optinteger(L, 5, 0); - return SysretBool(L, setsockopt(fd, level, optname, &tv, sizeof(tv)), - olderr); + optval = &tv; + optsize = sizeof(tv); } else if (level == SOL_SOCKET && optname == SO_LINGER) { - l.l_onoff = lua_toboolean(L, 4); - l.l_linger = luaL_checkinteger(L, 5); - return SysretBool(L, setsockopt(fd, level, optname, &l, sizeof(l)), olderr); + // 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 SysretErrnoBool(L, olderr); + 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; - uint32_t lsize, tvsize, optvalsize; 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)) { + if (IsSockoptBool(level, optname) || IsSockoptInt(level, optname)) { // unix.getsockopt(fd:int, level:int, optname:int) - // → bool, unix.Errno - optvalsize = sizeof(optval); - if (getsockopt(fd, level, optname, &optval, &optvalsize) != -1) { - CheckOptvalsize(L, sizeof(optval), optvalsize); - lua_pushboolean(L, optval); - return 1; - } else { - return SysretErrnoNil(L, olderr); - } - } else if (IsSockoptInt(level, optname)) { - optvalsize = sizeof(optval); - if (getsockopt(fd, level, optname, &optval, &optvalsize) != -1) { - CheckOptvalsize(L, sizeof(optval), optvalsize); + // ├─→ 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 { - return SysretErrnoNil(L, olderr); } } else if (IsSockoptTimeval(level, optname)) { - tvsize = sizeof(tv); - if (getsockopt(fd, level, optname, &tv, &tvsize) != -1) { - CheckOptvalsize(L, sizeof(tv), tvsize); + // 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_pushnil(L); - lua_pushinteger(L, tv.tv_usec); - return 3; - } else { - return SysretErrnoNil(L, olderr); + lua_pushinteger(L, tv.tv_usec * 1000); + return 2; } } else if (level == SOL_SOCKET && optname == SO_LINGER) { - lsize = sizeof(l); - if (getsockopt(fd, level, optname, &l, &lsize) != -1) { - CheckOptvalsize(L, sizeof(l), lsize); - lua_pushinteger(L, l.l_onoff); - lua_pushnil(L); + // 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); - return 3; - } else { - return SysretErrnoNil(L, olderr); + lua_pushboolean(L, !!l.l_onoff); + return 1; } } else { einval(); - return SysretErrnoNil(L, olderr); } + return SysretErrno(L, "getsockopt", olderr); } -// unix.socket([family:int[, type:int[, protocol:int]]]) → fd:int[, -// unix.Errno] +// 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, + L, "socket", olderr, socket(luaL_optinteger(L, 1, AF_INET), luaL_optinteger(L, 2, SOCK_STREAM), - luaL_optinteger(L, 3, IPPROTO_TCP)), - olderr); + luaL_optinteger(L, 3, IPPROTO_TCP))); } // unix.socketpair([family:int[, type:int[, protocol:int]]]) -// → fd1:int, unix.Errno, fd2: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_INET), luaL_optinteger(L, 2, SOCK_STREAM), luaL_optinteger(L, 3, IPPROTO_TCP), sv)) { lua_pushinteger(L, sv[0]); - lua_pushnil(L); lua_pushinteger(L, sv[1]); return 2; } else { - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "socketpair", olderr); } } -// unix.bind(fd:int[, ip:uint32, port:uint16]) → ok:bool, unix.Errno +// unix.bind(fd:int[, ip:uint32, port:uint16]) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixBind(lua_State *L) { int olderr = errno; - return SysretBool(L, + 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)), - olderr); + sizeof(struct sockaddr_in))); } -// unix.connect(fd:int, ip:uint32, port:uint16) → ok:bool, unix.Errno +// unix.connect(fd:int, ip:uint32, port:uint16) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixConnect(lua_State *L) { int olderr = errno; return SysretBool( - L, + 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)), - olderr); + sizeof(struct sockaddr_in))); } -// unix.listen(fd:int[, backlog:int]) → ok:bool, unix.Errno +// unix.listen(fd:int[, backlog:int]) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixListen(lua_State *L) { int olderr = errno; - return SysretBool( - L, listen(luaL_checkinteger(L, 1), luaL_optinteger(L, 2, 10)), olderr); + return SysretBool(L, "listen", olderr, + listen(luaL_checkinteger(L, 1), luaL_optinteger(L, 2, 10))); } -// unix.getsockname(fd:int) → ip:uint32, unix.Errno, port:uint16 +// unix.getsockname(fd:int) +// ├─→ ip:uint32, port:uint16 +// └─→ nil, unix.Errno static int LuaUnixGetsockname(lua_State *L) { int fd, olderr; uint32_t addrsize; @@ -1053,15 +1208,16 @@ static int LuaUnixGetsockname(lua_State *L) { fd = luaL_checkinteger(L, 1); if (!getsockname(fd, &sa, &addrsize)) { lua_pushinteger(L, ntohl(sa.sin_addr.s_addr)); - lua_pushnil(L); lua_pushinteger(L, ntohs(sa.sin_port)); - return 3; + return 2; } else { - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "getsockname", olderr); } } -// unix.getpeername(fd:int) → ip:uint32, unix.Errno, port:uint16 +// unix.getpeername(fd:int) +// ├─→ ip:uint32, port:uint16 +// └─→ nil, unix.Errno static int LuaUnixGetpeername(lua_State *L) { int fd, olderr; uint32_t addrsize; @@ -1071,16 +1227,16 @@ static int LuaUnixGetpeername(lua_State *L) { fd = luaL_checkinteger(L, 1); if (!getpeername(fd, &sa, &addrsize)) { lua_pushinteger(L, ntohl(sa.sin_addr.s_addr)); - lua_pushnil(L); lua_pushinteger(L, ntohs(sa.sin_port)); - return 3; + return 2; } else { - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "getpeername", olderr); } } // unix.siocgifconf() -// → {{name:str,ip:uint32,netmask:uint32}, ...}, unix.Errno +// ├─→ {{name:str,ip:uint32,netmask:uint32}, ...} +// └─→ nil, unix.Errno static int LuaUnixSiocgifconf(lua_State *L) { size_t n; char *data; @@ -1088,19 +1244,19 @@ static int LuaUnixSiocgifconf(lua_State *L) { struct ifreq *ifr; struct ifconf conf; olderr = errno; - if (!(data = malloc((n = 4096)))) { - return SysretErrnoNil(L, olderr); + if (!(data = LuaUnixAllocRaw(L, (n = 4096)))) { + return SysretErrno(L, "siocgifconf", olderr); } if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)) == -1) { free(data); - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "siocgifconf", olderr); } conf.ifc_buf = data; conf.ifc_len = n; if (ioctl(fd, SIOCGIFCONF, &conf) == -1) { close(fd); free(data); - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "siocgifconf", olderr); } lua_newtable(L); i = 0; @@ -1127,7 +1283,9 @@ static int LuaUnixSiocgifconf(lua_State *L) { return 1; } -// unix.gethostname() → host:str, unix.Errno +// unix.gethostname() +// ├─→ host:str +// └─→ nil, unix.Errno static int LuaUnixGethostname(lua_State *L) { int rc, olderr; char buf[DNS_NAME_MAX + 1]; @@ -1138,15 +1296,14 @@ static int LuaUnixGethostname(lua_State *L) { return 1; } else { enomem(); - return SysretErrnoNil(L, olderr); } - } else { - return SysretErrnoNil(L, olderr); } + return SysretErrno(L, "gethostname", olderr); } // unix.accept(serverfd:int[, flags:int]) -// → clientfd:int, unix.Errno, ip:uint32, port:uint16 +// ├─→ clientfd:int, ip:uint32, port:uint16 +// └─→ nil, unix.Errno static int LuaUnixAccept(lua_State *L) { uint32_t addrsize; struct sockaddr_in sa; @@ -1158,28 +1315,37 @@ static int LuaUnixAccept(lua_State *L) { clientfd = accept4(serverfd, &sa, &addrsize, flags); if (clientfd != -1) { lua_pushinteger(L, clientfd); - lua_pushnil(L); lua_pushinteger(L, ntohl(sa.sin_addr.s_addr)); lua_pushinteger(L, ntohs(sa.sin_port)); - return 4; + return 3; } else { - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "accept", olderr); } } -// unix.poll({fd:int=events:int, ...}[, timeoutms:int]) -// → {fd:int=revents:int, ...}, unix.Errno +// 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; + struct pollfd *fds, *fds2; int i, fd, olderr, events, timeoutms; + olderr = errno; luaL_checktype(L, 1, LUA_TTABLE); lua_pushnil(L); for (fds = 0, nfds = 0; lua_next(L, 1);) { if (lua_isinteger(L, -2)) { - fds = xrealloc(fds, ++nfds * sizeof(*fds)); - fds[nfds - 1].fd = lua_tointeger(L, -2); - fds[nfds - 1].events = lua_tointeger(L, -1); + if ((fds2 = LuaUnixRealloc(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); } @@ -1197,56 +1363,60 @@ static int LuaUnixPoll(lua_State *L) { return 1; } else { free(fds); - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "poll", olderr); } } -// unix.recvfrom(fd:int[, bufsiz:str[, flags:int]]) -// → data:str, unix.Errno, ip:uint32, port:uint16 +// 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, bufsiz, olderr; + int fd, flags, olderr; olderr = errno; addrsize = sizeof(sa); fd = luaL_checkinteger(L, 1); bufsiz = luaL_optinteger(L, 2, 1500); flags = luaL_optinteger(L, 3, 0); bufsiz = MIN(bufsiz, 0x7ffff000); - if ((buf = malloc(bufsiz))) { + if ((buf = LuaUnixAllocRaw(L, bufsiz))) { rc = recvfrom(fd, buf, bufsiz, flags, &sa, &addrsize); if (rc != -1) { got = rc; lua_pushlstring(L, buf, got); - lua_pushnil(L); lua_pushinteger(L, ntohl(sa.sin_addr.s_addr)); lua_pushinteger(L, ntohs(sa.sin_port)); free(buf); - return 4; + return 3; } else { free(buf); - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "recvfrom", olderr); } } else { - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "recvfrom", olderr); } } -// unix.recv(fd:int[, bufsiz:int[, flags:int]]) → data:str, unix.Errno +// 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; - int fd, flags, bufsiz, olderr, pushed; + lua_Integer bufsiz; + int fd, flags, olderr, pushed; olderr = errno; fd = luaL_checkinteger(L, 1); bufsiz = luaL_optinteger(L, 2, 1500); flags = luaL_optinteger(L, 3, 0); bufsiz = MIN(bufsiz, 0x7ffff000); - if ((buf = malloc(bufsiz))) { + if ((buf = LuaUnixAllocRaw(L, bufsiz))) { rc = recv(fd, buf, bufsiz, flags); if (rc != -1) { got = rc; @@ -1255,34 +1425,39 @@ static int LuaUnixRecv(lua_State *L) { return 1; } else { free(buf); - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "recv", olderr); } } else { - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "recv", olderr); } } -// unix.send(fd:int, data:str[, flags:int]) → sent:int, unix.Errno +// 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, bufsiz, olderr; + lua_Integer bufsiz; + int fd, flags, olderr; olderr = errno; fd = luaL_checkinteger(L, 1); data = luaL_checklstring(L, 2, &size); size = MIN(size, 0x7ffff000); flags = luaL_optinteger(L, 5, 0); - return SysretInteger(L, send(fd, data, size, flags), olderr); + return SysretInteger(L, "send", olderr, send(fd, data, size, flags)); } // unix.sendto(fd:int, data:str, ip:uint32, port:uint16[, flags:int]) -// → sent:int, unix.Errno +// ├─→ sent:int +// └─→ nil, unix.Errno static int LuaUnixSendto(lua_State *L) { char *data; size_t sent, size; + lua_Integer bufsiz; + int fd, flags, olderr; struct sockaddr_in sa; - int fd, flags, bufsiz, olderr; olderr = errno; bzero(&sa, sizeof(sa)); fd = luaL_checkinteger(L, 1); @@ -1291,70 +1466,64 @@ static int LuaUnixSendto(lua_State *L) { 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(fd, data, size, flags, &sa, sizeof(sa)), - olderr); + return SysretInteger(L, "sendto", olderr, + sendto(fd, data, size, flags, &sa, sizeof(sa))); } -// unix.shutdown(fd:int, how:int) → ok:bool, unix.Errno +// unix.shutdown(fd:int, how:int) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixShutdown(lua_State *L) { int olderr = errno; - return SysretBool( - L, shutdown(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2)), olderr); + return SysretBool(L, "shutdown", olderr, + shutdown(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2))); } -// unix.sigprocmask(how:int[, mask:int]) → oldmask:int, unix.Errno +// unix.sigprocmask(how:int, newmask:unix.Sigset) +// ├─→ oldmask:unix.Sigset +// └─→ nil, unix.Errno static int LuaUnixSigprocmask(lua_State *L) { uint64_t imask; - int how, olderr; - sigset_t mask, oldmask, *maskptr; + int i, n, how, olderr; + struct sigset *newmask, oldmask; 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]); + newmask = luaL_checkudata(L, 2, "unix.Sigset"); + if (!sigprocmask(how, newmask, &oldmask)) { + LuaPushSigset(L, oldmask); return 1; } else { - return SysretErrnoNil(L, olderr); + 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(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 + 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:int]]]) -// → oldhandler:func|int, unix.Errno, flags:int, mask:int -// -// 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. +// 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; struct sigaction sa, oldsa, *saptr; saptr = &sa; @@ -1394,14 +1563,18 @@ static int LuaUnixSigaction(lua_State *L) { unreachable; } sa.sa_flags = luaL_optinteger(L, 3, SA_RESTART); - sa.sa_mask.__bits[0] |= luaL_optinteger(L, 4, 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_geti(L, -1, sig) != LUA_TFUNCTION) { + if (lua_rawgeti(L, -1, sig) != LUA_TFUNCTION) { lua_pop(L, 1); lua_pushinteger(L, (intptr_t)oldsa.sa_handler); } @@ -1412,29 +1585,36 @@ static int LuaUnixSigaction(lua_State *L) { } else { lua_pushnil(L); } - lua_seti(L, -3, sig); + lua_rawseti(L, -3, sig); } // remove the signal handler table from stack lua_remove(L, -2); - // finish pushing the last 3/4 results - lua_pushnil(L); + // finish pushing the last 2/3 results lua_pushinteger(L, oldsa.sa_flags); - lua_pushinteger(L, oldsa.sa_mask.__bits[0]); - return 4; + LuaPushSigset(L, oldsa.sa_mask); + return 3; } else { - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "sigaction", olderr); } } -// unix.sigsuspend([mask]) → false, unix.Errno +// unix.sigsuspend([mask:Sigmask]) +// └─→ nil, unix.Errno static int LuaUnixSigsuspend(lua_State *L) { int olderr = errno; - sigsuspend(&(struct sigset){{luaL_optinteger(L, 1, 0)}}); - return SysretErrnoBool(L, olderr); + struct sigset *set; + if (!lua_isnoneornil(L, 1)) { + set = luaL_checkudata(L, 1, "unix.Sigset"); + } else { + set = 0; + } + sigsuspend(set); + return SysretErrno(L, "sigsuspend", olderr); } -// unix.setitimer(which[, intsec, intmicros, valsec, valmicros]) -// → intsec:int, unix.Errno, intns:int, valsec:int, valns:int +// 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; struct itimerval it, oldit, *itptr; @@ -1443,21 +1623,20 @@ static int LuaUnixSetitimer(lua_State *L) { 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_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); + 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_pushnil(L); - lua_pushinteger(L, oldit.it_interval.tv_usec); + 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); - return 5; + lua_pushinteger(L, oldit.it_value.tv_usec * 1000); + return 4; } else { - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "setitimer", olderr); } } @@ -1465,46 +1644,104 @@ static dontinline int LuaUnixStr(lua_State *L, char *f(int)) { return ReturnString(L, f(luaL_checkinteger(L, 1))); } -// unix.strerdoc(errno) → str +// unix.strerdoc(errno:int) +// └─→ mediummessage:str static int LuaUnixStrerdoc(lua_State *L) { return LuaUnixStr(L, strerdoc); } -// unix.strsignal(sig) → str +// unix.strsignal(sig:int) +// └─→ symbol:str static int LuaUnixStrsignal(lua_State *L) { return LuaUnixStr(L, strsignal); } -// unix.strerror(errno) → str -static int LuaUnixStrerror(lua_State *L) { - return LuaUnixStr(L, strerror); -} - -// unix.strerrno(errno) → str|nil +// unix.strerrno(errno:int) +// └─→ symbol:int static int LuaUnixStrerrno(lua_State *L) { return LuaUnixStr(L, strerrno); } -// unix.WIFEXITED(wstatus) → int -static int LuaUnixWifexited(lua_State *L) { - return ReturnInteger(L, WIFEXITED(luaL_checkinteger(L, 1))); +// unix.strerror(errno:int) +// └─→ longmessage:str +static int LuaUnixStrerror(lua_State *L) { + return LuaUnixStr(L, strerror); } -// unix.WEXITSTATUS(wstatus) → int +// 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) → int +// unix.WIFSIGNALED(wstatus) +// └─→ bool static int LuaUnixWifsignaled(lua_State *L) { - return ReturnInteger(L, WIFSIGNALED(luaL_checkinteger(L, 1))); + return ReturnBoolean(L, WIFSIGNALED(luaL_checkinteger(L, 1))); } -// unix.WTERMSIG(wstatus) → int +// 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 @@ -1514,42 +1751,62 @@ static dontinline struct stat *GetUnixStat(lua_State *L) { return &(*ust)->st; } +// 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); } @@ -1560,22 +1817,42 @@ static dontinline int UnixStatTim(lua_State *L, struct timespec *ts) { return 2; } +// unix.Stat:atim() +// └─→ unixts:int, nanos:int static int LuaUnixStatAtim(lua_State *L) { return UnixStatTim(L, &GetUnixStat(L)->st_atim); } +// unix.Stat:mtim() +// └─→ unixts:int, nanos:int static int LuaUnixStatMtim(lua_State *L) { return UnixStatTim(L, &GetUnixStat(L)->st_mtim); } +// unix.Stat:ctim() +// └─→ unixts:int, nanos:int static int LuaUnixStatCtim(lua_State *L) { return UnixStatTim(L, &GetUnixStat(L)->st_ctim); } +// unix.Stat:birthtim() +// └─→ unixts:int, nanos:int static int LuaUnixStatBirthtim(lua_State *L) { return UnixStatTim(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 void FreeUnixStat(struct UnixStat *stat) { if (!--stat->refs) { free(stat); @@ -1613,6 +1890,8 @@ static const luaL_Reg kLuaUnixStatMeth[] = { {"rdev", LuaUnixStatRdev}, // {"size", LuaUnixStatSize}, // {"uid", LuaUnixStatUid}, // + {"flags", LuaUnixStatFlags}, // + {"gen", LuaUnixStatGen}, // {0}, // }; @@ -1635,32 +1914,37 @@ static void LuaUnixStatObj(lua_State *L) { // unix.Errno object static dontinline struct UnixErrno *GetUnixErrno(lua_State *L) { - struct UnixErrno **ue; - ue = luaL_checkudata(L, 1, "unix.Errno"); - return *ue; + struct UnixErrno **ep; + ep = luaL_checkudata(L, 1, "unix.Errno"); + return *ep; +} + +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) { - struct UnixErrno *e = GetUnixErrno(L); - lua_pushstring(L, strerrno(e->errno)); - return 1; + return ReturnString(L, strerrno(GetUnixErrno(L)->errno)); } static int LuaUnixErrnoDoc(lua_State *L) { - struct UnixErrno *e = GetUnixErrno(L); - lua_pushstring(L, strerdoc(e->errno)); - return 1; -} - -static int LuaUnixErrnoError(lua_State *L) { - struct UnixErrno *e = GetUnixErrno(L); - lua_pushstring(L, strerror(e->errno)); - return 1; + return ReturnString(L, strerdoc(GetUnixErrno(L)->errno)); } static int LuaUnixErrnoToString(lua_State *L) { - struct UnixErrno *e = GetUnixErrno(L); - lua_pushfstring(L, "error: system call failed: %s", strerror(e->errno)); + 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; } @@ -1681,10 +1965,12 @@ static int LuaUnixErrnoGc(lua_State *L) { } static const luaL_Reg kLuaUnixErrnoMeth[] = { - {"error", LuaUnixErrnoError}, // - {"name", LuaUnixErrnoName}, // - {"doc", LuaUnixErrnoDoc}, // - {0}, // + {"strerror", LuaUnixErrnoToString}, // + {"errno", LuaUnixErrnoErrno}, // + {"winerr", LuaUnixErrnoWinerr}, // + {"name", LuaUnixErrnoName}, // + {"doc", LuaUnixErrnoDoc}, // + {0}, // }; static const luaL_Reg kLuaUnixErrnoMeta[] = { @@ -1702,6 +1988,128 @@ static void LuaUnixErrnoObj(lua_State *L) { 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 @@ -1719,44 +2127,53 @@ static DIR *GetDirOrDie(lua_State *L) { } static int FreeUnixDir(struct UnixDir *dir) { + int rc; if (--dir->refs) return 0; - return closedir(dir->dir); + rc = closedir(dir->dir); + free(dir); + return rc; } -// unix.Dir:close() → ok:bool, unix.Errno -// may be called multiple times -// called by the garbage collector too +// unix.Dir:close() +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixDirClose(lua_State *L) { int rc, olderr; struct UnixDir **udir; udir = GetUnixDirSelf(L); - if (!*udir) return 0; - olderr = 0; - rc = FreeUnixDir(*udir); - *udir = 0; - return SysretBool(L, rc, olderr); -} - -// unix.Dir:read() → name:str, unix.Errno, kind:int, ino:int, off:int -static int LuaUnixDirRead(lua_State *L) { - int olderr = errno; - struct dirent *ent; - errno = 0; - if ((ent = readdir(GetDirOrDie(L)))) { - lua_pushlstring(L, ent->d_name, strnlen(ent->d_name, sizeof(ent->d_name))); - lua_pushnil(L); - lua_pushinteger(L, ent->d_type); - lua_pushinteger(L, ent->d_ino); - lua_pushinteger(L, ent->d_off); - return 5; - } else if (!ent && !errno) { - return 0; // end of listing + if (*udir) { + olderr = 0; + rc = FreeUnixDir(*udir); + *udir = 0; + return SysretBool(L, "closedir", olderr, rc); } else { - return SysretErrnoNil(L, olderr); + lua_pushboolean(L, true); + return 1; } } -// unix.Dir:fd() → fd:int, unix.Errno +// 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; olderr = errno; @@ -1765,16 +2182,16 @@ static int LuaUnixDirFd(lua_State *L) { lua_pushinteger(L, fd); return 1; } else { - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "dirfd", olderr); } } -// unix.Dir:tell() → off:int +// unix.Dir:tell() +// ├─→ off:int +// └─→ nil, unix.Errno static int LuaUnixDirTell(lua_State *L) { - long off; - off = telldir(GetDirOrDie(L)); - lua_pushinteger(L, off); - return 1; + int olderr = errno; + return SysretInteger(L, "telldir", olderr, telldir(GetDirOrDie(L))); } // unix.Dir:rewind() @@ -1783,6 +2200,48 @@ static int LuaUnixDirRewind(lua_State *L) { return 0; } +static int ReturnDir(lua_State *L, struct UnixDir *udir) { + struct UnixDir **udirp; + udir->refs = 1; + udirp = lua_newuserdatauv(L, sizeof(*udirp), 1); + luaL_setmetatable(L, "unix.Dir"); + *udirp = udir; + return 1; +} + +// unix.opendir(path:str) +// ├─→ state:unix.Dir +// └─→ nil, unix.Errno +static int LuaUnixOpendir(lua_State *L) { + int olderr = errno; + const char *path; + struct UnixDir *udir; + path = luaL_checkstring(L, 1); + if ((udir = LuaUnixAlloc(L, sizeof(*udir)))) { + if ((udir->dir = opendir(path))) { + return ReturnDir(L, udir); + } + free(udir); + } + return SysretErrno(L, "opendir", olderr); +} + +// unix.fdopendir(fd:int) +// ├─→ next:function, state:unix.Dir +// └─→ nil, unix.Errno +static int LuaUnixFdopendir(lua_State *L) { + int fd, olderr = errno; + struct UnixDir *udir; + fd = luaL_checkinteger(L, 1); + if ((udir = LuaUnixAlloc(L, sizeof(*udir)))) { + if ((udir->dir = fdopendir(fd))) { + return ReturnDir(L, udir); + } + free(udir); + } + return SysretErrno(L, "fdopendir", olderr); +} + static const luaL_Reg kLuaUnixDirMeth[] = { {"close", LuaUnixDirClose}, // {"read", LuaUnixDirRead}, // @@ -1793,8 +2252,9 @@ static const luaL_Reg kLuaUnixDirMeth[] = { }; static const luaL_Reg kLuaUnixDirMeta[] = { - {"__gc", LuaUnixDirClose}, // - {0}, // + {"__call", LuaUnixDirRead}, // + {"__gc", LuaUnixDirClose}, // + {0}, // }; static void LuaUnixDirObj(lua_State *L) { @@ -1810,6 +2270,7 @@ static void LuaUnixDirObj(lua_State *L) { // 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 @@ -1823,6 +2284,7 @@ static const luaL_Reg kLuaUnix[] = { {"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 @@ -1893,6 +2355,10 @@ static const luaL_Reg kLuaUnix[] = { {"sigprocmask", LuaUnixSigprocmask}, // change signal mask {"sigsuspend", LuaUnixSigsuspend}, // wait for signal {"setitimer", LuaUnixSetitimer}, // set alarm clock + {"gmtime", LuaUnixGmtime}, // destructure unix timestamp + {"localtime", LuaUnixLocaltime}, // localize unix timestamp + {"major", LuaUnixMajor}, // extract device info + {"minor", LuaUnixMinor}, // extract device info {"strerror", LuaUnixStrerror}, // turn errno into string {"strerrno", LuaUnixStrerrno}, // turn errno into string {"strerdoc", LuaUnixStrerdoc}, // turn errno into string @@ -1917,9 +2383,10 @@ static void LoadMagnums(lua_State *L, struct MagnumStr *ms, const char *pfx) { int LuaUnix(lua_State *L) { GL = L; luaL_newlib(L, kLuaUnix); + LuaUnixSigsetObj(L); + LuaUnixErrnoObj(L); LuaUnixStatObj(L); LuaUnixDirObj(L); - LuaUnixErrnoObj(L); lua_newtable(L); lua_setglobal(L, "__signal_handlers"); @@ -2008,7 +2475,7 @@ int LuaUnix(lua_State *L) { LuaSetIntField(L, "DT_FIFO", DT_FIFO); LuaSetIntField(L, "DT_SOCK", DT_SOCK); - // readdir() type + // poll() flags LuaSetIntField(L, "POLLIN", POLLIN); LuaSetIntField(L, "POLLPRI", POLLPRI); LuaSetIntField(L, "POLLOUT", POLLOUT); @@ -2023,6 +2490,7 @@ int LuaUnix(lua_State *L) { // 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 @@ -2055,5 +2523,21 @@ int LuaUnix(lua_State *L) { 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); + + LuaSetIntField(L, "NSIG", NSIG); + LuaSetIntField(L, "BUFSIZ", BUFSIZ); + LuaSetIntField(L, "ARG_MAX", ARG_MAX); + LuaSetIntField(L, "CLK_TCK", CLK_TCK); + LuaSetIntField(L, "PATH_MAX", PATH_MAX); + LuaSetIntField(L, "OPEN_MAX", OPEN_MAX); + LuaSetIntField(L, "PIPE_BUF", PIPE_BUF); + LuaSetIntField(L, "CHILD_MAX", CHILD_MAX); + return 1; } diff --git a/tool/net/net.mk b/tool/net/net.mk index b5d763938..5a0dc7c88 100644 --- a/tool/net/net.mk +++ b/tool/net/net.mk @@ -175,6 +175,7 @@ o/$(MODE)/tool/net/demo/sql.lua.zip.o \ o/$(MODE)/tool/net/demo/unix-rawsocket.lua.zip.o \ o/$(MODE)/tool/net/demo/unix-subprocess.lua.zip.o \ o/$(MODE)/tool/net/demo/unix-webserver.lua.zip.o \ +o/$(MODE)/tool/net/demo/unix-dir.lua.zip.o \ o/$(MODE)/tool/net/demo/unix-info.lua.zip.o \ o/$(MODE)/tool/net/demo/fetch.lua.zip.o \ o/$(MODE)/tool/net/demo/hello.lua.zip.o \ @@ -221,6 +222,7 @@ o/$(MODE)/tool/net/redbean-demo.com.dbg: \ o/$(MODE)/tool/net/demo/unix-rawsocket.lua.zip.o \ o/$(MODE)/tool/net/demo/unix-subprocess.lua.zip.o \ o/$(MODE)/tool/net/demo/unix-webserver.lua.zip.o \ + o/$(MODE)/tool/net/demo/unix-dir.lua.zip.o \ o/$(MODE)/tool/net/demo/unix-info.lua.zip.o \ o/$(MODE)/tool/net/demo/fetch.lua.zip.o \ o/$(MODE)/tool/net/demo/hello.lua.zip.o \ diff --git a/tool/net/redbean.c b/tool/net/redbean.c index b8413236e..dd53ed025 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -63,6 +63,7 @@ #include "libc/runtime/directmap.internal.h" #include "libc/runtime/gc.h" #include "libc/runtime/gc.internal.h" +#include "libc/runtime/internal.h" #include "libc/runtime/runtime.h" #include "libc/runtime/stack.h" #include "libc/runtime/symbols.internal.h" @@ -196,6 +197,8 @@ STATIC_YOINK("zip_uri_support"); // puncts not used: !"#$%&'()*+,-./;<=>@[\]^_`{|}~ #define GETOPTS "BSVZabdfghijkmsuvzA:C:D:F:G:H:K:L:M:P:R:T:U:c:e:l:p:r:t:" +extern unsigned long long __kbirth; + static const uint8_t kGzipHeader[] = { 0x1F, // MAGNUM 0x8B, // MAGNUM @@ -4956,6 +4959,7 @@ static const luaL_Reg kLuaFuncs[] = { {"GetComment", LuaGetAssetComment}, // {"GetCookie", LuaGetCookie}, // {"GetCpuCore", LuaGetCpuCore}, // + {"GetCpuCount", LuaGetCpuCount}, // {"GetCpuNode", LuaGetCpuNode}, // {"GetCryptoHash", LuaGetCryptoHash}, // {"GetDate", LuaGetDate}, // @@ -5192,6 +5196,7 @@ static void LuaPrint(lua_State *L) { static void LuaInterpreter(lua_State *L) { int i, n, sig, status; const char *script; + if (funtrace) ftrace_install(); if (optind < __argc) { script = __argv[optind]; if (!strcmp(script, "-")) script = 0; @@ -5201,7 +5206,11 @@ static void LuaInterpreter(lua_State *L) { luaL_checkstack(L, n + 3, "too many script args"); for (i = 1; i <= n; i++) lua_rawgeti(L, -i, i); lua_remove(L, -i); // remove arg table from stack + if (funtrace) ++g_ftrace; + if (systrace) ++__strace; status = lua_runchunk(L, n, LUA_MULTRET); + if (systrace) --__strace; + if (funtrace) --g_ftrace; } lua_report(L, status); } else { @@ -5225,7 +5234,9 @@ static void LuaInterpreter(lua_State *L) { exit(1); } if (status == LUA_OK) { + if (funtrace) ++g_ftrace; status = lua_runchunk(GL, 0, LUA_MULTRET); + if (funtrace) --g_ftrace; } if (status == LUA_OK) { LuaPrint(GL); @@ -6358,7 +6369,6 @@ static int HandleConnection(size_t i) { connectionclose = false; if (!IsTiny()) { if (systrace) { - extern unsigned long long __kbirth; __strace = 1; __kbirth = rdtsc(); } @@ -6474,18 +6484,20 @@ static void RestoreApe(void) { if (endswith(zpath, ".com.dbg")) return; if ((a = GetAssetZip("/.ape", 5)) && (p = LoadAsset(a, &n))) { close(zfd); - if ((zfd = OpenExecutable()) == -1 || WRITE(zfd, p, n) == -1) + if ((zfd = OpenExecutable()) == -1 || WRITE(zfd, p, n) == -1) { WARNF("(srvr) can't restore .ape"); + } free(p); } else { - INFOF("(srvr) /.ape not found"); + DEBUGF("(srvr) /.ape not found"); } } static int HandleReadline(void) { int status; + lua_State *L = GL; for (;;) { - status = lua_loadline(GL); + status = lua_loadline(L); if (status < 0) { if (status == -1) { OnTerm(SIGHUP); // eof @@ -6508,12 +6520,12 @@ static int HandleReadline(void) { linenoiseDisableRawMode(); LUA_REPL_LOCK; if (status == LUA_OK) { - status = lua_runchunk(GL, 0, LUA_MULTRET); + status = lua_runchunk(L, 0, LUA_MULTRET); } if (status == LUA_OK) { - LuaPrint(GL); + LuaPrint(L); } else { - lua_report(GL, status); + lua_report(L, status); } LUA_REPL_UNLOCK; if (lua_repl_isterminal) { @@ -6683,26 +6695,28 @@ static int EventLoop(int ms) { } static void ReplEventLoop(void) { + lua_State *L = GL; DEBUGF("ReplEventLoop()"); polls[0].fd = 0; lua_repl_completions_callback = HandleCompletions; - lua_initrepl(GL, "redbean"); + lua_initrepl(L, "redbean"); if (lua_repl_isterminal) { linenoiseEnableRawMode(0); } EventLoop(100); linenoiseDisableRawMode(); lua_freerepl(); - lua_settop(GL, 0); // clear stack + lua_settop(L, 0); // clear stack polls[0].fd = -1; } static uint32_t WindowsReplThread(void *arg) { int sig; + lua_State *L = GL; DEBUGF("WindowsReplThread()"); lua_repl_blocking = true; lua_repl_completions_callback = HandleCompletions; - lua_initrepl(GL, "redbean"); + lua_initrepl(L, "redbean"); if (lua_repl_isterminal) { linenoiseEnableRawMode(0); } @@ -6714,7 +6728,7 @@ static uint32_t WindowsReplThread(void *arg) { linenoiseDisableRawMode(); lua_freerepl(); LUA_REPL_LOCK; - lua_settop(GL, 0); // clear stack + lua_settop(L, 0); // clear stack LUA_REPL_UNLOCK; if ((sig = linenoiseGetInterrupt())) { raise(sig); @@ -6896,7 +6910,7 @@ void RedBean(int argc, char *argv[]) { (shared = mmap(NULL, ROUNDUP(sizeof(struct Shared), FRAMESIZE), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0))); - zpath = program_executable_name; + zpath = GetProgramExecutableName(); CHECK_NE(-1, (zfd = open(zpath, O_RDONLY))); CHECK_NE(-1, fstat(zfd, &zst)); OpenZip(true);