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('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(' |
\r\n')
+
+dir = '.'
+for name, kind, ino, off in assert(unix.opendir(dir)) do
+ Write('\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')
- while true do
- name, errno, kind, ino, off = dir:read()
- if not name then
- break
- end
- unix.write(fd, '- ')
- unix.write(fd, EscapeHtml(VisualizeControlCodes(name)))
- if kind == unix.DT_DIR then
- unix.write(fd, '/')
- else
- st, err = unix.stat(name)
- if st then
- unix.write(fd, ' (%d bytes)' % {st:size()})
- end
- end
- unix.write(fd, '\r\n')
- end
- 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')
+ for name, kind, ino, off in assert(unix.opendir(dir)) do
+ if name ~= '.' and name ~= '..' then
+ Write('- %s\r\n' % {EscapeHtml(name)})
+ end
+ end
+ 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);