From 281a0f2730a081fea210c7aa00d03dffee68d384 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Wed, 13 Apr 2022 08:49:17 -0700 Subject: [PATCH] Implement raw system call for redbean lua code You can now call functions like fork() from Lua and it'll work across all supported platforms, including Windows. This gives you a level of control of the system that Lua traditionally hasn't been able to have due to its focus on old portable stdio rather modern POSIX APIs. Demo code has been added to redbean-demo.com to show how it works. This change also modifies Lua so that integer literals with a leading zero will be interpreted as octal. That should help avoid shooting in the foot with POSIX APIs that frequently use octal mode bits. This change fixes a bug in opendir(".") on New Technology. Lastly, redbean will now serve crash reports to private network IPs. This is consistent with other frameworks. However that isn't served to public IPs unless the -E flag is passed to redbean at startup. --- libc/calls/calls.h | 2 +- libc/calls/dup3.c | 2 +- libc/calls/getppid.c | 10 +- libc/calls/getpriority.c | 8 +- libc/calls/internal.h | 1 + libc/calls/setegid.c | 8 +- libc/calls/seteuid.c | 6 +- libc/calls/setsid.c | 7 +- libc/calls/umask.c | 40 + libc/fmt/kerrornames.internal.h | 16 + libc/fmt/strerror_long.greg.c | 3 +- libc/fmt/strerror_short.greg.c | 3 +- libc/intrin/describeflags.internal.h | 1 + libc/intrin/describentfiletypeflags.greg.c | 35 + libc/intrin/findfirstfile.greg.c | 52 + libc/intrin/findnextfile.greg.c | 52 + libc/intrin/intrin.mk | 2 + libc/nt/kernel32/FindFirstFileW.s | 4 +- libc/nt/kernel32/FindNextFileW.s | 4 +- libc/nt/master.sh | 4 +- libc/runtime/cxaatexit.internal.h | 2 +- libc/sock/bind.c | 2 +- libc/sock/socketpair.c | 4 +- libc/stdio/dirstream.c | 13 +- libc/str/strsignal.c | 3 +- libc/sysv/calls/{umask.s => sys_umask.s} | 2 +- libc/sysv/consts.sh | 6 +- libc/sysv/syscalls.sh | 2 +- third_party/infozip/zip/fileio.c | 18 - third_party/infozip/zip/unix/unix.c | 14 - third_party/lua/README.cosmo | 2 + third_party/lua/llex.c | 1 + third_party/lua/lobject.c | 12 + third_party/make/misc.c | 8 - tool/net/demo/unix.lua | 116 ++ tool/net/help.txt | 208 +++ tool/net/lunix.c | 1326 ++++++++++++++++++++ tool/net/net.mk | 6 + tool/net/redbean.c | 123 +- 39 files changed, 2044 insertions(+), 84 deletions(-) create mode 100644 libc/calls/umask.c create mode 100644 libc/fmt/kerrornames.internal.h create mode 100644 libc/intrin/describentfiletypeflags.greg.c create mode 100644 libc/intrin/findfirstfile.greg.c create mode 100644 libc/intrin/findnextfile.greg.c rename libc/sysv/calls/{umask.s => sys_umask.s} (50%) create mode 100644 tool/net/demo/unix.lua create mode 100644 tool/net/lunix.c diff --git a/libc/calls/calls.h b/libc/calls/calls.h index d7cfd7455..062fbb453 100644 --- a/libc/calls/calls.h +++ b/libc/calls/calls.h @@ -229,7 +229,7 @@ uint32_t getgid(void) nosideeffect; uint32_t getpgrp(void) nosideeffect; uint32_t getsid(int) nosideeffect; uint32_t getuid(void) nosideeffect; -uint32_t umask(int32_t); +uint32_t umask(uint32_t); void rewinddir(DIR *); void sync(void); int getloadavg(double *, int); diff --git a/libc/calls/dup3.c b/libc/calls/dup3.c index 717518fd7..be3898bb4 100644 --- a/libc/calls/dup3.c +++ b/libc/calls/dup3.c @@ -32,7 +32,7 @@ * @param oldfd isn't closed afterwards * @param newfd if already assigned, is silently closed beforehand; * unless it's equal to oldfd, in which case dup2() is a no-op - * @flags can have O_CLOEXEC + * @param flags can have O_CLOEXEC * @see dup(), dup2() */ int dup3(int oldfd, int newfd, int flags) { diff --git a/libc/calls/getppid.c b/libc/calls/getppid.c index 7ab0b7c45..d997f6134 100644 --- a/libc/calls/getppid.c +++ b/libc/calls/getppid.c @@ -17,19 +17,23 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" +#include "libc/calls/strace.internal.h" /** * Returns parent process id. * @asyncsignalsafe */ int getppid(void) { + int rc; if (!IsWindows()) { if (!IsNetbsd()) { - return sys_getppid(); + rc = sys_getppid(); } else { - return sys_getpid().dx; + rc = sys_getpid().dx; } } else { - return sys_getppid_nt(); + rc = sys_getppid_nt(); } + STRACE("getppid() → %d% m", rc); + return rc; } diff --git a/libc/calls/getpriority.c b/libc/calls/getpriority.c index 31a8df634..6a65f48d1 100644 --- a/libc/calls/getpriority.c +++ b/libc/calls/getpriority.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/calls/strace.internal.h" /** * Returns nice value of thing. @@ -28,9 +29,12 @@ * @see setpriority(), nice() */ int getpriority(int which, unsigned who) { + int rc; if (!IsWindows()) { - return sys_getpriority(which, who) - 20; + rc = sys_getpriority(which, who) - 20; } else { - return sys_getsetpriority_nt(which, who, 0, sys_getpriority_nt); + rc = sys_getsetpriority_nt(which, who, 0, sys_getpriority_nt); } + STRACE("getpriority(%d, %u) → %d% m", which, who, rc); + return rc; } diff --git a/libc/calls/internal.h b/libc/calls/internal.h index d694a6294..3cd6c360a 100644 --- a/libc/calls/internal.h +++ b/libc/calls/internal.h @@ -220,6 +220,7 @@ u32 sys_getgid(void) hidden; u32 sys_getsid(int) hidden; u32 sys_gettid(void) hidden; u32 sys_getuid(void) hidden; +u32 sys_umask(u32) hidden; void *__sys_mmap(void *, u64, u32, u32, i64, i64, i64) hidden; void *sys_mremap(void *, u64, u64, i32, void *) hidden; void sys_exit(int) hidden; diff --git a/libc/calls/setegid.c b/libc/calls/setegid.c index 55113a93d..315ea4cbd 100644 --- a/libc/calls/setegid.c +++ b/libc/calls/setegid.c @@ -17,10 +17,14 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" +#include "libc/calls/strace.internal.h" /** * Sets effective group ID. */ -int setegid(unsigned gid) { - return setregid(-1, gid); +int setegid(unsigned egid) { + int rc; + rc = setregid(-1, egid); + STRACE("%s(%u) → %d% m", "setegid", egid, rc); + return rc; } diff --git a/libc/calls/seteuid.c b/libc/calls/seteuid.c index b89b8f3c7..996ea7d98 100644 --- a/libc/calls/seteuid.c +++ b/libc/calls/seteuid.c @@ -17,10 +17,14 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" +#include "libc/calls/strace.internal.h" /** * Sets effective user ID. */ int seteuid(unsigned euid) { - return setreuid(-1, euid); + int rc; + rc = setreuid(-1, euid); + STRACE("%s(%u) → %d% m", "seteuid", euid, rc); + return rc; } diff --git a/libc/calls/setsid.c b/libc/calls/setsid.c index e4cde3710..adcfff21b 100644 --- a/libc/calls/setsid.c +++ b/libc/calls/setsid.c @@ -18,10 +18,15 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/calls/strace.internal.h" /** * Creates session and sets the process group id. + * @return new session id, or -1 w/ errno */ int setsid(void) { - return sys_setsid(); + int rc; + rc = sys_setsid(); + STRACE("setsid() → %d% m", rc); + return rc; } diff --git a/libc/calls/umask.c b/libc/calls/umask.c new file mode 100644 index 000000000..e0dec3276 --- /dev/null +++ b/libc/calls/umask.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 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/calls/internal.h" +#include "libc/calls/strace.internal.h" +#include "libc/dce.h" + +/** + * Sets file mode creation mask. + * + * @return previous mask + * @note always succeeds + */ +unsigned umask(unsigned newmask) { + unsigned oldmask; + if (!IsWindows()) { + oldmask = sys_umask(newmask); + } else { + // TODO(jart): what should we do with this? + oldmask = newmask; + } + STRACE("umask(%#o) → %#o", oldmask); + return oldmask; +} diff --git a/libc/fmt/kerrornames.internal.h b/libc/fmt/kerrornames.internal.h new file mode 100644 index 000000000..b8a2636c1 --- /dev/null +++ b/libc/fmt/kerrornames.internal.h @@ -0,0 +1,16 @@ +#ifndef COSMOPOLITAN_LIBC_FMT_KERRORNAMES_INTERNAL_H_ +#define COSMOPOLITAN_LIBC_FMT_KERRORNAMES_INTERNAL_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +struct ErrorName { + int x, s; +}; + +extern const struct ErrorName kStrSignal[]; +extern const struct ErrorName kErrorNames[]; +extern const struct ErrorName kErrorNamesLong[]; + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_FMT_KERRORNAMES_INTERNAL_H_ */ diff --git a/libc/fmt/strerror_long.greg.c b/libc/fmt/strerror_long.greg.c index 844a1569b..c5081970a 100644 --- a/libc/fmt/strerror_long.greg.c +++ b/libc/fmt/strerror_long.greg.c @@ -17,8 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/fmt/fmt.h" - -extern const struct { int x, s; } kErrorNamesLong[]; +#include "libc/fmt/kerrornames.internal.h" /** * Converts errno value to descriptive sentence. diff --git a/libc/fmt/strerror_short.greg.c b/libc/fmt/strerror_short.greg.c index d732958f9..5075af526 100644 --- a/libc/fmt/strerror_short.greg.c +++ b/libc/fmt/strerror_short.greg.c @@ -17,8 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/fmt/fmt.h" - -extern const struct { int x, s; } kErrorNames[]; +#include "libc/fmt/kerrornames.internal.h" /** * Converts errno value to symbolic name. diff --git a/libc/intrin/describeflags.internal.h b/libc/intrin/describeflags.internal.h index 8d843dacb..3beff09b8 100644 --- a/libc/intrin/describeflags.internal.h +++ b/libc/intrin/describeflags.internal.h @@ -17,6 +17,7 @@ const char *DescribeRemapFlags(int); const char *DescribeNtPageFlags(uint32_t); const char *DescribeNtFileMapFlags(uint32_t); +const char *DescribeNtFiletypeFlags(uint32_t); const char *DescribeNtFileFlagsAndAttributes(uint32_t); const char *DescribeNtFileShareFlags(uint32_t); const char *DescribeNtFileAccessFlags(uint32_t); diff --git a/libc/intrin/describentfiletypeflags.greg.c b/libc/intrin/describentfiletypeflags.greg.c new file mode 100644 index 000000000..764749167 --- /dev/null +++ b/libc/intrin/describentfiletypeflags.greg.c @@ -0,0 +1,35 @@ +/*-*- 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/intrin/describeflags.internal.h" +#include "libc/macros.internal.h" +#include "libc/nt/enum/filetype.h" +#include "libc/sysv/consts/mremap.h" + +static const struct DescribeFlags kFiletypeFlags[] = { + {kNtFileTypeRemote, "Remote"}, // + {kNtFileTypePipe, "Pipe"}, // order matters + {kNtFileTypeDisk, "Disk"}, // + {kNtFileTypeChar, "Char"}, // +}; + +const char *DescribeNtFiletypeFlags(uint32_t x) { + static char filetypeflags[64]; + return DescribeFlags(filetypeflags, sizeof(filetypeflags), kFiletypeFlags, + ARRAYLEN(kFiletypeFlags), "kNtFileType", x); +} diff --git a/libc/intrin/findfirstfile.greg.c b/libc/intrin/findfirstfile.greg.c new file mode 100644 index 000000000..fc7446085 --- /dev/null +++ b/libc/intrin/findfirstfile.greg.c @@ -0,0 +1,52 @@ +/*-*- 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/internal.h" +#include "libc/calls/strace.internal.h" +#include "libc/intrin/describeflags.internal.h" +#include "libc/nt/files.h" +#include "libc/nt/memory.h" +#include "libc/nt/struct/win32finddata.h" +#include "libc/nt/thunk/msabi.h" + +extern typeof(FindFirstFile) *const __imp_FindFirstFileW __msabi; + +/** + * Finds first file in directory. + * @note this wrapper takes care of ABI, STRACE(), and __winerr() + */ +textwindows int64_t FindFirstFile(const char16_t *lpFileName, + struct NtWin32FindData *out_lpFindFileData) { + int64_t hFindFile; + hFindFile = __imp_FindFirstFileW(lpFileName, out_lpFindFileData); + if (hFindFile != -1) { + STRACE( + "FindFirstFile(%#hs, [{" + ".cFileName=%#hs, " + ".dwFileAttributes=%s, " + ".dwFileType=%s" + "}]) → %ld% m", + lpFileName, out_lpFindFileData->cFileName, + DescribeNtFileFlagsAndAttributes(out_lpFindFileData->dwFileAttributes), + DescribeNtFiletypeFlags(out_lpFindFileData->dwFileType), hFindFile); + } else { + __winerr(); + STRACE("FindFirstFile(%#hs, [n/a]) → %ld% m", lpFileName, hFindFile); + } + return hFindFile; +} diff --git a/libc/intrin/findnextfile.greg.c b/libc/intrin/findnextfile.greg.c new file mode 100644 index 000000000..15942fc28 --- /dev/null +++ b/libc/intrin/findnextfile.greg.c @@ -0,0 +1,52 @@ +/*-*- 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/internal.h" +#include "libc/calls/strace.internal.h" +#include "libc/intrin/describeflags.internal.h" +#include "libc/nt/files.h" +#include "libc/nt/memory.h" +#include "libc/nt/struct/win32finddata.h" +#include "libc/nt/thunk/msabi.h" + +extern typeof(FindNextFile) *const __imp_FindNextFileW __msabi; + +/** + * Finds more files in directory. + * @note this wrapper takes care of ABI, STRACE(), and __winerr() + */ +textwindows bool32 FindNextFile(int64_t hFindFile, + struct NtWin32FindData *out_lpFindFileData) { + bool32 ok; + ok = __imp_FindNextFileW(hFindFile, out_lpFindFileData); + if (ok) { + STRACE( + "FindNextFile(%ld, [{" + ".cFileName=%#hs, " + ".dwFileAttributes=%s, " + ".dwFileType=%s" + "}]) → %hhhd% m", + hFindFile, out_lpFindFileData->cFileName, + DescribeNtFileFlagsAndAttributes(out_lpFindFileData->dwFileAttributes), + DescribeNtFiletypeFlags(out_lpFindFileData->dwFileType), ok); + } else { + __winerr(); + STRACE("FindNextFile(%ld, [n/a]) → %hhhd% m", hFindFile, ok); + } + return ok; +} diff --git a/libc/intrin/intrin.mk b/libc/intrin/intrin.mk index 939bef9e0..7a39d1ed9 100644 --- a/libc/intrin/intrin.mk +++ b/libc/intrin/intrin.mk @@ -70,7 +70,9 @@ o/$(MODE)/libc/intrin/createpipe.greg.o \ o/$(MODE)/libc/intrin/closehandle.greg.o \ o/$(MODE)/libc/intrin/openprocess.greg.o \ o/$(MODE)/libc/intrin/createthread.greg.o \ +o/$(MODE)/libc/intrin/findnextfile.greg.o \ o/$(MODE)/libc/intrin/createprocess.greg.o \ +o/$(MODE)/libc/intrin/findfirstfile.greg.o \ o/$(MODE)/libc/intrin/describeflags.greg.o \ o/$(MODE)/libc/intrin/removedirectory.greg.o \ o/$(MODE)/libc/intrin/createnamedpipe.greg.o \ diff --git a/libc/nt/kernel32/FindFirstFileW.s b/libc/nt/kernel32/FindFirstFileW.s index 80b3d9f59..30c263fe3 100644 --- a/libc/nt/kernel32/FindFirstFileW.s +++ b/libc/nt/kernel32/FindFirstFileW.s @@ -2,11 +2,11 @@ .imp kernel32,__imp_FindFirstFileW,FindFirstFileW,0 .text.windows -FindFirstFile: +__FindFirstFile: push %rbp mov %rsp,%rbp .profilable mov __imp_FindFirstFileW(%rip),%rax jmp __sysv2nt - .endfn FindFirstFile,globl + .endfn __FindFirstFile,globl .previous diff --git a/libc/nt/kernel32/FindNextFileW.s b/libc/nt/kernel32/FindNextFileW.s index a00fc206a..0a7bac505 100644 --- a/libc/nt/kernel32/FindNextFileW.s +++ b/libc/nt/kernel32/FindNextFileW.s @@ -2,11 +2,11 @@ .imp kernel32,__imp_FindNextFileW,FindNextFileW,0 .text.windows -FindNextFile: +__FindNextFile: push %rbp mov %rsp,%rbp .profilable mov __imp_FindNextFileW(%rip),%rax jmp __sysv2nt - .endfn FindNextFile,globl + .endfn __FindNextFile,globl .previous diff --git a/libc/nt/master.sh b/libc/nt/master.sh index cb4d9bdd1..456d91f94 100755 --- a/libc/nt/master.sh +++ b/libc/nt/master.sh @@ -327,7 +327,6 @@ imp 'FindClose' FindClose kernel32 0 1 imp 'FindCloseChangeNotification' FindCloseChangeNotification kernel32 0 imp 'FindFirstChangeNotification' FindFirstChangeNotificationW kernel32 0 imp 'FindFirstChangeNotificationA' FindFirstChangeNotificationA kernel32 0 -imp 'FindFirstFile' FindFirstFileW kernel32 0 2 imp 'FindFirstFileA' FindFirstFileA kernel32 0 2 imp 'FindFirstFileEx' FindFirstFileExW kernel32 0 6 imp 'FindFirstFileExA' FindFirstFileExA kernel32 0 6 @@ -344,7 +343,6 @@ imp 'FindFirstVolumeMountPointA' FindFirstVolumeMountPointA kernel32 393 imp 'FindNLSString' FindNLSString kernel32 0 imp 'FindNLSStringEx' FindNLSStringEx kernel32 0 imp 'FindNextChangeNotification' FindNextChangeNotification kernel32 0 -imp 'FindNextFile' FindNextFileW kernel32 0 2 imp 'FindNextFileA' FindNextFileA kernel32 0 2 imp 'FindNextFileName' FindNextFileNameW kernel32 0 imp 'FindNextStream' FindNextStreamW kernel32 0 @@ -1359,6 +1357,8 @@ imp '__CreateProcess' CreateProcessW kernel32 0 10 imp '__CreateThread' CreateThread kernel32 0 6 imp '__DeleteFile' DeleteFileW kernel32 0 1 imp '__DeviceIoControl' DeviceIoControl kernel32 0 8 +imp '__FindFirstFile' FindFirstFileW kernel32 0 2 +imp '__FindNextFile' FindNextFileW kernel32 0 2 imp '__FlushFileBuffers' FlushFileBuffers kernel32 0 1 imp '__FlushViewOfFile' FlushViewOfFile kernel32 0 2 imp '__GenerateConsoleCtrlEvent' GenerateConsoleCtrlEvent kernel32 0 2 diff --git a/libc/runtime/cxaatexit.internal.h b/libc/runtime/cxaatexit.internal.h index bb7c17254..9da432b53 100644 --- a/libc/runtime/cxaatexit.internal.h +++ b/libc/runtime/cxaatexit.internal.h @@ -1,8 +1,8 @@ #ifndef COSMOPOLITAN_LIBC_RUNTIME_CXAATEXIT_H_ #define COSMOPOLITAN_LIBC_RUNTIME_CXAATEXIT_H_ +#include "libc/stdio/stdio.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -#include "libc/stdio/stdio.h" struct CxaAtexitBlocks { struct CxaAtexitBlock { diff --git a/libc/sock/bind.c b/libc/sock/bind.c index a65c7bc6c..2188084db 100644 --- a/libc/sock/bind.c +++ b/libc/sock/bind.c @@ -33,7 +33,7 @@ * @param fd is the file descriptor returned by socket() * @param addr is usually the binary-encoded ip:port on which to listen * @param addrsize is the byte-length of addr's true polymorphic form - * @return socket file descriptor or -1 w/ errno + * @return 0 on success or -1 w/ errno * @error ENETDOWN, EPFNOSUPPORT, etc. * @asyncsignalsafe */ diff --git a/libc/sock/socketpair.c b/libc/sock/socketpair.c index c0b928bc8..8ea6dcc60 100644 --- a/libc/sock/socketpair.c +++ b/libc/sock/socketpair.c @@ -25,7 +25,9 @@ * Creates bidirectional pipe. * * @param family should be AF_UNIX or synonymously AF_LOCAL - * @param type may be or'd with SOCK_CLOEXEC + * @param type can be SOCK_STREAM (for TCP), SOCK_DGRAM (e.g. UDP), or + * SOCK_RAW (IP) so long as IP_HDRINCL was passed to setsockopt(); + * and additionally, may be or'd with SOCK_NONBLOCK, SOCK_CLOEXEC * @param sv a vector of 2 integers to store the created sockets * @return 0 if success, -1 in case of error * @error EFAULT, EPFNOSUPPORT, etc. diff --git a/libc/stdio/dirstream.c b/libc/stdio/dirstream.c index 927e381ee..21f0c0622 100644 --- a/libc/stdio/dirstream.c +++ b/libc/stdio/dirstream.c @@ -116,16 +116,18 @@ struct dirent_netbsd { static textwindows DIR *opendir_nt_impl(char16_t *name, size_t len) { DIR *res; if (len + 2 + 1 <= PATH_MAX) { - if (len > 1 && name[len - 1] != u'\\') { - name[len++] = u'\\'; + if (len == 1 && name[0] == '.') { + name[0] = '*'; + } else { + if (len > 1 && name[len - 1] != u'\\') { + name[len++] = u'\\'; + } + name[len++] = u'*'; } - name[len++] = u'*'; name[len] = u'\0'; if ((res = calloc(1, sizeof(DIR)))) { if ((res->fd = FindFirstFile(name, &res->windata)) != -1) { return res; - } else { - __winerr(); } free(res); } @@ -342,6 +344,7 @@ struct dirent *readdir(DIR *dir) { if (dir->buf_pos >= dir->buf_end) { basep = dir->tell; /* TODO(jart): what does xnu do */ rc = getdents(dir->fd, dir->buf, sizeof(dir->buf) - 256, &basep); + STRACE("getdents(%d) → %d% m", dir->fd, rc); if (!rc || rc == -1) return NULL; dir->buf_pos = 0; dir->buf_end = rc; diff --git a/libc/str/strsignal.c b/libc/str/strsignal.c index 1e2db5ef8..7b4283462 100644 --- a/libc/str/strsignal.c +++ b/libc/str/strsignal.c @@ -17,11 +17,10 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/bits.h" +#include "libc/fmt/kerrornames.internal.h" #include "libc/macros.internal.h" #include "libc/str/str.h" -extern const struct { int x, s; } kStrSignal[]; - static char g_strsignal[12]; /** diff --git a/libc/sysv/calls/umask.s b/libc/sysv/calls/sys_umask.s similarity index 50% rename from libc/sysv/calls/umask.s rename to libc/sysv/calls/sys_umask.s index 481f95f92..1ccadeb66 100644 --- a/libc/sysv/calls/umask.s +++ b/libc/sysv/calls/sys_umask.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall umask,0x03c03c03c203c05f,globl +.scall sys_umask,0x03c03c03c203c05f,globl diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh index b3f876527..d75678793 100755 --- a/libc/sysv/consts.sh +++ b/libc/sysv/consts.sh @@ -619,9 +619,9 @@ syscon clock CLOCK_REALTIME_COARSE 5 -1 -1 -1 -1 -1 # Linux 2.6.32 syscon clock CLOCK_MONOTONIC_COARSE 6 -1 -1 -1 -1 -1 # Linux 2.6.32+; bsd consensus; not available on RHEL5 syscon clock CLOCK_PROF -1 -1 2 -1 2 -1 # syscon clock CLOCK_BOOTTIME 7 -1 -1 6 6 -1 # -syscon clock CLOCK_REALTIME_ALARM 8 -1 -1 -1 -1 -1 # bsd consensus -syscon clock CLOCK_BOOTTIME_ALARM 9 -1 -1 -1 -1 -1 # bsd consensus -syscon clock CLOCK_TAI 11 -1 -1 -1 -1 -1 # bsd consensus +syscon clock CLOCK_REALTIME_ALARM 8 -1 -1 -1 -1 -1 # +syscon clock CLOCK_BOOTTIME_ALARM 9 -1 -1 -1 -1 -1 # +syscon clock CLOCK_TAI 11 -1 -1 -1 -1 -1 # # poll() # diff --git a/libc/sysv/syscalls.sh b/libc/sysv/syscalls.sh index 20ae378be..840a736c9 100755 --- a/libc/sysv/syscalls.sh +++ b/libc/sysv/syscalls.sh @@ -134,7 +134,7 @@ scall sys_fchmod 0x07c07c07c207c05b globl hidden scall sys_chown 0x010010010201005c globl hidden # impl. w/ fchownat() @asyncsignalsafe scall sys_fchown 0x07b07b07b207b05d globl hidden # @asyncsignalsafe scall sys_lchown 0x1130fe0fe216c05e globl hidden # impl. w/ fchownat() -scall umask 0x03c03c03c203c05f globl +scall sys_umask 0x03c03c03c203c05f globl scall sys_gettimeofday 0x1a20430742074060 globl hidden # xnu esi/edx=0 scall sys_getrlimit 0x0c20c20c220c2061 globl hidden scall __sys_getrusage 0x1bd0130752075062 globl hidden diff --git a/third_party/infozip/zip/fileio.c b/third_party/infozip/zip/fileio.c index 7e2cd63d9..92947dacb 100644 --- a/third_party/infozip/zip/fileio.c +++ b/third_party/infozip/zip/fileio.c @@ -1934,24 +1934,6 @@ int bfcopy(n) } - -#ifdef NO_RENAME -int rename(from, to) -ZCONST char *from; -ZCONST char *to; -{ - unlink(to); - if (link(from, to) == -1) - return -1; - if (unlink(from) == -1) - return -1; - return 0; -} - -#endif /* NO_RENAME */ - - - /*------------------------------------------------------------------ * Split archives */ diff --git a/third_party/infozip/zip/unix/unix.c b/third_party/infozip/zip/unix/unix.c index 63fbaa1b9..095cafbfe 100644 --- a/third_party/infozip/zip/unix/unix.c +++ b/third_party/infozip/zip/unix/unix.c @@ -56,20 +56,6 @@ typedef FILE DIR; ** Cleaned up and modified by James W. Birdsall. */ -struct dirent *readdir(dirp) -DIR *dirp; -{ - static struct dirent entry; - - if (dirp == NULL) - return NULL; - for (;;) - if (fread (&entry, sizeof (struct dirent), 1, dirp) == 0) - return NULL; - else if (entry.d_ino) - return (&entry); -} /* end of readdir() */ - #define closedir(dirp) fclose(dirp) #endif /* NO_DIR */ diff --git a/third_party/lua/README.cosmo b/third_party/lua/README.cosmo index 01b1651a0..daec0c54e 100644 --- a/third_party/lua/README.cosmo +++ b/third_party/lua/README.cosmo @@ -20,6 +20,8 @@ PROVENANCE LOCAL MODIFICATIONS + Integer literals such as `033` will now be interpreted as octal. + The `\e` string literal escape sequence has been added, which is equivalent to `\27` (the Lua version of `\033`) or the ASCII ESC character. It may be used for teletypewriter control like having diff --git a/third_party/lua/llex.c b/third_party/lua/llex.c index 644d4a09e..535f4cfee 100644 --- a/third_party/lua/llex.c +++ b/third_party/lua/llex.c @@ -414,6 +414,7 @@ static void read_string (LexState *ls, int del, SemInfo *seminfo) { goto no_save; } default: { + // TODO(jart): this is highly irregular it must become octal esccheck(ls, lisdigit(ls->current), "invalid escape sequence"); c = readdecesc(ls); /* digital escape '\ddd' */ goto only_save; diff --git a/third_party/lua/lobject.c b/third_party/lua/lobject.c index 84c416eef..ff67a677c 100644 --- a/third_party/lua/lobject.c +++ b/third_party/lua/lobject.c @@ -7,6 +7,7 @@ #define lobject_c #define LUA_CORE +#include "libc/intrin/kprintf.h" #include "third_party/lua/lctype.h" #include "third_party/lua/ldebug.h" #include "third_party/lua/ldo.h" @@ -242,7 +243,9 @@ static const char *l_str2d (const char *s, lua_Number *result) { #define MAXBY10 cast(lua_Unsigned, LUA_MAXINTEGER / 10) +#define MAXBY8 cast(lua_Unsigned, LUA_MAXINTEGER / 8) #define MAXLASTD cast_int(LUA_MAXINTEGER % 10) +#define MAXLASTD8 cast_int(LUA_MAXINTEGER % 8) static const char *l_str2int (const char *s, lua_Integer *result) { lua_Unsigned a = 0; @@ -258,6 +261,15 @@ static const char *l_str2int (const char *s, lua_Integer *result) { empty = 0; } } + else if (s[0] == '0') { /* [jart] octal is the best radix */ + for (s += 1; lisdigit(cast_uchar(*s)); s++) { + int d = *s - '0'; + if (a >= MAXBY8 && (a > MAXBY8 || d > MAXLASTD8 + neg)) /* overflow? */ + return NULL; /* do not accept it (as integer) */ + a = a * 8 + d; + empty = 0; + } + } else { /* decimal */ for (; lisdigit(cast_uchar(*s)); s++) { int d = *s - '0'; diff --git a/third_party/make/misc.c b/third_party/make/misc.c index 05dfdacf0..82dbd4b9e 100644 --- a/third_party/make/misc.c +++ b/third_party/make/misc.c @@ -360,14 +360,6 @@ char *mktemp (char *template); # endif #endif -#ifndef HAVE_UMASK -mode_t -umask (mode_t mask) -{ - return 0; -} -#endif - FILE * get_tmpfile (char **name, const char *template) { diff --git a/tool/net/demo/unix.lua b/tool/net/demo/unix.lua new file mode 100644 index 000000000..fc3c6fdcd --- /dev/null +++ b/tool/net/demo/unix.lua @@ -0,0 +1,116 @@ +local unix = require "unix" + +local function main() + if GetMethod() ~= 'GET' and GetMethod() ~= 'HEAD' then + ServeError(405) + SetHeader('Allow', 'GET, HEAD') + return + end + if IsClientUsingSsl() then + SetStatus(400) + SetHeader('Content-Type', 'text/html; charset=utf-8') + Write('\r\n') + Write('redbean unix module\r\n') + Write('

\r\n') + Write('\r\n') + Write('redbean unix demo\r\n') + Write(' error\r\n') + Write('

\r\n') + Write([[ +

+ The redbean unix module demo needs to intercept a raw unix + socket. It's hard to do that if your file descriptor is + encrypted with TLS. Please reload this page using http:// + rather than https://. +

+ ]]) + Write('\r\n') + return + end + + -- steal client from redbean + fd = GetClientFd() + rc = unix.fork() + if rc ~= 0 then + unix.close(fd) + return + end + + -- turn into a daemon + unix.umask(0) + unix.setsid() + if unix.fork() > 0 then unix.exit(0) end + unix.close(0) + unix.open('/dev/null', unix.O_RDONLY) + unix.close(1) + unix.open('/dev/null', unix.O_WRONLY) + efd = unix.open('/tmp/redbean-unix.log', unix.O_WRONLY | unix.O_CREAT | unix.O_TRUNC, 0600) + unix.dup(efd, 2) + unix.close(efd) + unix.sigaction(unix.SIGHUP, unix.SIG_DFL) + unix.sigaction(unix.SIGINT, unix.SIG_DFL) + unix.sigaction(unix.SIGQUIT, unix.SIG_DFL) + unix.sigaction(unix.SIGTERM, unix.SIG_DFL) + unix.sigaction(unix.SIGUSR1, unix.SIG_DFL) + unix.sigaction(unix.SIGUSR2, unix.SIG_DFL) + unix.sigaction(unix.SIGCHLD, unix.SIG_DFL) + + -- communicate with client + unix.write(fd, 'HTTP/1.0 200 OK\r\n' .. + 'Connection: close\r\n' .. + 'Date: '.. FormatHttpDateTime(GetDate()) ..'\r\n' .. + 'Content-Type: text/html; charset=utf-8\r\n' .. + 'Server: redbean unix\r\n' .. + '\r\n') + unix.write(fd, '\n') + unix.write(fd, 'redbean unix module') + unix.write(fd, '

\r\n') + unix.write(fd, '\r\n') + unix.write(fd, 'redbean unix demo\r\n') + unix.write(fd, ' success\r\n') + unix.write(fd, '

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

\r\n') + unix.write(fd, 'your lua code just stole control of your http client\r\n') + unix.write(fd, 'socket file descriptor from redbean server, and then\r\n') + unix.write(fd, 'became an autonomous daemon reparented on your init!\r\n') + unix.write(fd, '

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

listing of current directory

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

\r\n') + unix.write(fd, string.format('failed: %s\r\n', EscapeHtml(VisualizeControlCodes(unix:strerror(err))))) + unix.write(fd, '

\r\n') + end + + -- terminate + unix.close(fd) + unix.exit(0) + +end + +main() diff --git a/tool/net/help.txt b/tool/net/help.txt index 517bb814e..cd3f55d2f 100644 --- a/tool/net/help.txt +++ b/tool/net/help.txt @@ -35,6 +35,7 @@ FLAGS -a log resource usage -g log handler latency -e run specified Lua command + -E show crash reports to public ips -j enable ssl client verify -k disable ssl fetch verify -f log worker function calls @@ -585,6 +586,15 @@ FUNCTIONS instead, since the latter takes into consideration reverse proxy scenarios. + GetClientFd() → int + Returns file descriptor being used for client connection. + This is useful for scripts that want to use unix:fork(). + + IsClientUsingSsl() → bool + Returns true if client connection has begun being managed by + the MbedTLS security layer. This is an important thing to + consider if a script is taking control of GetClientFd() + GetServerAddr() → ip:uint32,port:uint16 Returns address to which listening server socket is bound, e.g. 0x01020304,8080 would represent 1.2.3.4:8080. If -p 0 was supplied @@ -734,6 +744,11 @@ FUNCTIONS Returns true if the client IP address (returned by GetRemoteAddr) is part of the localhost network (127.0.0.0/8). + IsPrivateClient() → bool + Returns true if the client IP address (returned by GetRemoteAddr) + is part of the localhost network (127.0.0.0/8) or a private network + (10.0.0.0/8, etc.) + IsLoopbackIp(uint32) → bool Returns true if IP address is part of the localhost network (127.0.0.0/8). @@ -1045,6 +1060,46 @@ FUNCTIONS Popcnt(x:int) → int Returns number of bits set in integer. + Rdtsc() → int + Returns CPU timestamp counter. + + Lemur64() → int + Returns fastest pseudorandom non-cryptographic random number. This + linear congruential generator passes practrand and bigcrush. + + Rand64() → int + Returns nondeterministic pseudorandom non-cryptographic number. This + linear congruential generator passes practrand and bigcrush. This + generator is safe across fork(), threads, and signal handlers. + + Rdrand() → int + Returns 64-bit hardware random integer from RDRND instruction, with + automatic fallback to getrandom() if not available. + + Rdseed() → int + Returns 64-bit hardware random integer from RDSEED instruction, with + automatic fallback to RDRND and getrandom() if not available. + + GetCpuCore() → int + Returns 0-indexed CPU core on which process is currently scheduled. + + GetCpuNode() → int + Returns 0-indexed NUMA node on which process is currently scheduled. + + Decimate(data) → int + Shrinks byte buffer in half using John Costella's magic kernel. + This downscales data 2x using an eight-tap convolution, e.g. + + >: Decimate(b'\\xff\\xff\\x00\\x00\\xff\\xff\\x00\\x00\\xff\\xff\\x00\\x00') + b'\\xff\\x00\\xff\\x00\\xff\\x00' + + This is very fast if SSSE3 is available (Intel 2004+ / AMD 2011+). + + MeasureEntropy(data) → float + Returns Shannon entropy of array. This gives you an idea of + the density of information. Cryptographic random should be in + the ballpark of 7.9 whereas plaintext will be more like 4.5. + LSQLITE3 MODULE Please refer to the LuaSQLite3 Documentation. @@ -1168,6 +1223,159 @@ MAXMIND MODULE For further details, please see maxmind.lua in redbean-demo.com. +UNIX MODULE + + This module exports the best raw system calls from Cosmopolitan Libc. + These UNIX APIs are supported across all supported operating systems, + and that includes Windows. + + fork exit stat open close seek read write access fcntl chdir chown + chmod getcwd kill raise wait pipe dup mkdir rmdir opendir rename + link unlink symlink sync fsync fdatasync truncate umask getppid + getpgid setpgid getsid setsid getpid getuid getgid gettime + nanosleep socket socketpair bind listen accept connect recvfrom + sendto shutdown getpeername getsockname sigaction sigprocmask + strerror + + This module also provides the following magic numbers: + + O_RDONLY O_WRONLY O_RDWR O_ACCMODE O_CREAT O_EXCL O_TRUNC + O_CLOEXEC O_APPEND O_TMPFILE O_NOFOLLOW O_SYNC O_ASYNC O_NOCTTY + O_NOATIME O_EXEC O_SEARCH O_DSYNC O_RSYNC O_PATH O_VERIFY O_SHLOCK + O_EXLOCK O_RANDOM O_SEQUENTIAL O_COMPRESSED O_INDEXED SEEK_SET + SEEK_CUR SEEK_END F_GETFD F_SETFD F_GETFL F_SETFL F_UNLCK F_RDLCK + F_WRLCK F_SETLK F_SETLKW FD_CLOEXEC R_OK W_OK X_OK F_OK WNOHANG + CLOCK_REALTIME CLOCK_MONOTONIC CLOCK_MONOTONIC_RAW + CLOCK_REALTIME_COARSE CLOCK_MONOTONIC_COARSE + CLOCK_PROCESS_CPUTIME_ID CLOCK_TAI CLOCK_PROF CLOCK_BOOTTIME + CLOCK_REALTIME_ALARM CLOCK_BOOTTIME_ALARM AF_UNSPEC AF_UNIX + AF_INET SOCK_STREAM SOCK_DGRAM SOCK_CLOEXEC IPPROTO_TCP + IPPROTO_UDP SHUT_RD SHUT_WR SHUT_RDWR MSG_WAITALL MSG_DONTROUTE + MSG_PEEK MSG_OOB MSG_NOSIGNAL DT_UNKNOWN DT_REG DT_DIR DT_BLK + DT_LNK DT_CHR DT_FIFO DT_SOCK AT_FDCWD AT_SYMLINK_NOFOLLOW + SIG_BLOCK SIG_UNBLOCK SIG_SETMASK SIG_DFL SIG_IGN + + Please see the Cosmopolitan Libc documentation for further details. + There's also a /unix.lua file in redbean-demo.com that provides a + glimpse of how these powerful APIs can be used. Here's a synopsis: + + unix.fork() → childpid|0, errno + unix.exit([exitcode]) → ⊥ + unix.access(path, mode) → rc, errno + mode can be: R_OK, W_OK, X_OK, F_OK + unix.mkdir(path, mode) → rc, errno + mode should be octal + unix.chdir(path) → rc, errno + unix.unlink(path) → rc, errno + unix.rmdir(path) → rc, errno + unix.rename(oldpath, newpath) → rc, errno + unix.link(existingpath, newpath) → rc, errno + unix.symlink(target, linkpath) → rc, errno + unix.chown(path, uid, gid) → rc, errno + unix.chmod(path, mode) → rc, errno + unix.getcwd(path, mode) → rc, errno + unix.getpid() → pid + unix.getppid() → pid + unix.kill(pid, sig) → rc, errno + unix.raise(sig) → rc, errno + unix.wait(pid[, options]) → pid, wstatus, nil, errno + unix.fcntl(fd, cmd[, arg]) → rc, errno + unix.dup(oldfd[, newfd[, flags]]) → newfd, errno + flags can have O_CLOEXEC + unix.pipe([flags]) → reader, writer, errno + flags can have O_CLOEXEC + unix.getsid(pid) → sid, errno + unix.getpgid(pid) → pgid, errno + unix.umask(mask) → rc, errno + unix.setpgid(pid, pgid) → pgid, errno + unix.setsid() → sid, errno + unix.getuid() → uid, errno + unix.getgid() → gid, errno + unix.gettime([clock]) → seconds, nanos, errno + unix.nanosleep(seconds, nanos) → remseconds, remnanos, errno + unix.sync(fd) + unix.fsync(fd) → rc, errno + unix.fdatasync(fd) → rc, errno + unix.open(path, flags[, mode]) → fd, errno + unix.close(fd) → rc, errno + unix.seek(fd, offset, whence) → newpos, errno + where whence ∈ {SEEK_SET, SEEK_CUR, SEEK_END} + whence defaults to SEEK_SET + unix.truncate(path, length) → rc, errno + unix.truncate(fd, length) → rc, errno + unix.read(fd[, bufsiz, offset]) → data, errno + unix.write(fd, data[, offset]) → rc, errno + unix.socket([family[, type[, protocol]]]) → fd, errno + SOCK_CLOEXEC may be or'd into type + family defaults to AF_INET + type defaults to SOCK_STREAM + protocol defaults to IPPROTO_TCP + unix.socketpair([family[, type[, protocol]]]) → fd1, fd2, errno + SOCK_CLOEXEC may be or'd into type + family defaults to AF_INET + type defaults to SOCK_STREAM + protocol defaults to IPPROTO_TCP + unix.bind(fd, ip, port) → rc, errno + SOCK_CLOEXEC may be or'd into type + family defaults to AF_INET + type defaults to SOCK_STREAM + protocol defaults to IPPROTO_TCP + unix.connect(fd, ip, port) → rc, errno + SOCK_CLOEXEC may be or'd into type + family defaults to AF_INET + type defaults to SOCK_STREAM + protocol defaults to IPPROTO_TCP + unix.listen(fd[, backlog]) → rc, errno + unix.getsockname(fd) → ip, port, errno + unix.getpeername(fd) → ip, port, errno + unix.accept(serverfd) → clientfd, ip, port, errno + unix.recvfrom(fd[, bufsiz[, flags]]) → data, ip, port, errno + flags can have MSG_{WAITALL,DONTROUTE,PEEK,OOB}, etc. + unix.sendto(fd, data, ip, port[, flags]) → sent, errno + flags MSG_OOB, MSG_DONTROUTE, MSG_NOSIGNAL, etc. + unix.shutdown(fd, how) → rc, errno + how can be SHUT_RD, SHUT_WR, or SHUT_RDWR + unix.sigprocmask(how, mask) → oldmask, errno + how can be SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK + unix.sigaction(sig, handler[, flags[, mask]]) → rc, errno + sig can be SIGINT, SIGQUIT, SIGTERM, SIGUSR1, etc. + handler can be SIG_IGN or SIG_DFL for time being + note: this api will be changed in the future + unix.strerror(errno) → str + + Here's your UnixStat* object. + + unix.stat(path) → UnixStat*, errno + unix.stat(fd) → UnixStat*, errno + UnixStat:size() → bytes:int + UnixStat:mode() → mode:int + UnixStat:dev() → int + UnixStat:ino() → int + UnixStat:rdev() → int + UnixStat:uid() → int + UnixStat:gid() → int + UnixStat:atim() → secs:int, nanosint + UnixStat:mtim() → secs:int, nanosint + UnixStat:ctim() → secs:int, nanosint + UnixStat:blocks() → int + UnixStat:blksize() → int + + Here's your UnixDir* object. + + unix.opendir(path) → UnixDir*, errno + unix.opendir(fd) → UnixDir*, errno + UnixDir:close() + may be called multiple times + called by the garbage collector too + UnixDir:read() → name, kind, ino, off + returns nil if no more entries + kind can be DT_UNKNOWN/REG/DIR/BLK/LNK/CHR/FIFO/SOCK + UnixDir:fd() → fd, errno + EOPNOTSUPP if using /zip/ + EOPNOTSUPP if IsWindows() + UnixDir:tell() → off + UnixDir:rewind() + CONSTANTS kLogDebug diff --git a/tool/net/lunix.c b/tool/net/lunix.c new file mode 100644 index 000000000..767216124 --- /dev/null +++ b/tool/net/lunix.c @@ -0,0 +1,1326 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" +#include "libc/calls/calls.h" +#include "libc/calls/struct/dirent.h" +#include "libc/calls/struct/sigset.h" +#include "libc/calls/struct/stat.h" +#include "libc/calls/struct/timespec.h" +#include "libc/errno.h" +#include "libc/fmt/fmt.h" +#include "libc/fmt/kerrornames.internal.h" +#include "libc/intrin/kprintf.h" +#include "libc/macros.internal.h" +#include "libc/mem/mem.h" +#include "libc/sock/sock.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/af.h" +#include "libc/sysv/consts/at.h" +#include "libc/sysv/consts/clock.h" +#include "libc/sysv/consts/dt.h" +#include "libc/sysv/consts/f.h" +#include "libc/sysv/consts/fd.h" +#include "libc/sysv/consts/ipproto.h" +#include "libc/sysv/consts/msg.h" +#include "libc/sysv/consts/o.h" +#include "libc/sysv/consts/ok.h" +#include "libc/sysv/consts/sa.h" +#include "libc/sysv/consts/shut.h" +#include "libc/sysv/consts/sig.h" +#include "libc/sysv/consts/sock.h" +#include "libc/sysv/consts/w.h" +#include "libc/time/time.h" +#include "libc/x/x.h" +#include "third_party/lua/lauxlib.h" +#include "third_party/lua/lua.h" +#include "third_party/lua/luaconf.h" + +/** + * @fileoverview UNIX system calls thinly wrapped for Lua + * @support Linux, Mac, Windows, FreeBSD, NetBSD, OpenBSD + */ + +struct UnixStat { + int refs; + struct stat st; +}; + +struct UnixDir { + int refs; + DIR *dir; +}; + +static void LuaSetIntField(lua_State *L, const char *k, lua_Integer v) { + lua_pushinteger(L, v); + lua_setfield(L, -2, k); +} + +static dontinline int ReturnInteger(lua_State *L, lua_Integer x) { + lua_pushinteger(L, x); + return 1; +} + +static dontinline int ReturnTimespec(lua_State *L, struct timespec *ts) { + lua_pushinteger(L, ts->tv_sec); + lua_pushinteger(L, ts->tv_nsec); + return 2; +} + +static int ReturnRc(lua_State *L, int64_t rc, int olderr) { + lua_pushinteger(L, rc); + if (rc != -1) return 1; + lua_pushinteger(L, errno); + errno = olderr; + return 2; +} + +//////////////////////////////////////////////////////////////////////////////// +// System Calls + +// unix.exit([exitcode]) → ⊥ +static int LuaUnixExit(lua_State *L) { + _Exit(luaL_optinteger(L, 1, 0)); + unreachable; +} + +// unix.access(path, mode) → rc, errno +// mode can be: R_OK, W_OK, X_OK, F_OK +static int LuaUnixAccess(lua_State *L) { + const char *file; + int rc, mode, olderr; + olderr = errno; + file = luaL_checklstring(L, 1, 0); + mode = luaL_checkinteger(L, 2); + rc = access(file, mode); + return ReturnRc(L, rc, olderr); +} + +// unix.mkdir(path, mode) → rc, errno +// mode should be octal +static int LuaUnixMkdir(lua_State *L) { + const char *file; + int rc, mode, olderr; + olderr = errno; + file = luaL_checklstring(L, 1, 0); + mode = luaL_checkinteger(L, 2); + rc = mkdir(file, mode); + return ReturnRc(L, rc, olderr); +} + +// unix.chdir(path) → rc, errno +static int LuaUnixChdir(lua_State *L) { + int rc, olderr; + const char *file; + olderr = errno; + file = luaL_checklstring(L, 1, 0); + rc = chdir(file); + return ReturnRc(L, rc, olderr); +} + +// unix.unlink(path) → rc, errno +static int LuaUnixUnlink(lua_State *L) { + int rc, olderr; + const char *file; + olderr = errno; + file = luaL_checklstring(L, 1, 0); + rc = unlink(file); + return ReturnRc(L, rc, olderr); +} + +// unix.rmdir(path) → rc, errno +static int LuaUnixRmdir(lua_State *L) { + const char *file; + int rc, olderr; + olderr = errno; + file = luaL_checklstring(L, 1, 0); + rc = rmdir(file); + return ReturnRc(L, rc, olderr); +} + +// unix.rename(oldpath, newpath) → rc, errno +static int LuaUnixRename(lua_State *L) { + const char *oldpath, *newpath; + int rc, olderr; + olderr = errno; + oldpath = luaL_checklstring(L, 1, 0); + newpath = luaL_checklstring(L, 2, 0); + rc = rename(oldpath, newpath); + return ReturnRc(L, rc, olderr); +} + +// unix.link(existingpath, newpath) → rc, errno +static int LuaUnixLink(lua_State *L) { + const char *existingpath, *newpath; + int rc, olderr; + olderr = errno; + existingpath = luaL_checklstring(L, 1, 0); + newpath = luaL_checklstring(L, 2, 0); + rc = link(existingpath, newpath); + return ReturnRc(L, rc, olderr); +} + +// unix.symlink(target, linkpath) → rc, errno +static int LuaUnixSymlink(lua_State *L) { + const char *target, *linkpath; + int rc, olderr; + olderr = errno; + target = luaL_checklstring(L, 1, 0); + linkpath = luaL_checklstring(L, 2, 0); + rc = symlink(target, linkpath); + return ReturnRc(L, rc, olderr); +} + +// unix.chown(path, uid, gid) → rc, errno +static int LuaUnixChown(lua_State *L) { + const char *file; + int rc, uid, gid, olderr; + olderr = errno; + file = luaL_checklstring(L, 1, 0); + uid = luaL_checkinteger(L, 2); + gid = luaL_checkinteger(L, 3); + rc = chown(file, uid, gid); + return ReturnRc(L, rc, olderr); +} + +// unix.chmod(path, mode) → rc, errno +static int LuaUnixChmod(lua_State *L) { + const char *file; + int rc, mode, olderr; + olderr = errno; + file = luaL_checklstring(L, 1, 0); + mode = luaL_checkinteger(L, 2); + rc = chmod(file, mode); + return ReturnRc(L, rc, olderr); +} + +// unix.getcwd(path, mode) → rc, errno +static int LuaUnixGetcwd(lua_State *L) { + char *path; + path = getcwd(0, 0); + assert(path); + lua_pushstring(L, path); + free(path); + return 1; +} + +// unix.fork() → childpid|0, errno +static int LuaUnixFork(lua_State *L) { + int rc, olderr; + olderr = errno; + rc = fork(); + return ReturnRc(L, rc, olderr); +} + +// unix.getpid() → pid +static int LuaUnixGetpid(lua_State *L) { + lua_pushinteger(L, getpid()); + return 1; +} + +// unix.getppid() → pid +static int LuaUnixGetppid(lua_State *L) { + lua_pushinteger(L, getppid()); + return 1; +} + +// unix.kill(pid, sig) → rc, errno +static int LuaUnixKill(lua_State *L) { + int rc, pid, sig, olderr; + olderr = errno; + pid = luaL_checkinteger(L, 1); + sig = luaL_checkinteger(L, 2); + rc = kill(pid, sig); + return ReturnRc(L, rc, olderr); +} + +// unix.raise(sig) → rc, errno +static int LuaUnixRaise(lua_State *L) { + int rc, sig, olderr; + olderr = errno; + sig = luaL_checkinteger(L, 1); + rc = raise(sig); + return ReturnRc(L, rc, olderr); +} + +// unix.wait(pid[, options]) → pid, wstatus, nil, errno +static int LuaUnixWait(lua_State *L) { + int rc, pid, olderr, options, wstatus; + olderr = errno; + pid = luaL_checkinteger(L, 1); + options = luaL_optinteger(L, 2, 0); + rc = wait4(pid, &wstatus, options, 0); + if (rc != -1) { + lua_pushinteger(L, rc); + lua_pushinteger(L, wstatus); + return 2; + } else { + lua_pushnil(L); + lua_pushnil(L); + lua_pushnil(L); // for future use + lua_pushinteger(L, errno); + errno = olderr; + return 4; + } +} + +// unix.fcntl(fd, cmd[, arg]) → rc, errno +static int LuaUnixFcntl(lua_State *L) { + intptr_t arg; + int rc, fd, cmd, olderr; + olderr = errno; + fd = luaL_checkinteger(L, 1); + cmd = luaL_checkinteger(L, 2); + arg = luaL_optinteger(L, 3, 0); + rc = fcntl(fd, cmd, arg); + return ReturnRc(L, rc, olderr); +} + +// unix.dup(oldfd[, newfd[, flags]]) → newfd, errno +// flags can have O_CLOEXEC +static int LuaUnixDup(lua_State *L) { + int rc, oldfd, newfd, flags, olderr; + olderr = errno; + oldfd = luaL_checkinteger(L, 1); + newfd = luaL_optinteger(L, 2, -1); + flags = luaL_optinteger(L, 3, 0); + if (newfd == -1) { + rc = dup(oldfd); + } else { + rc = dup3(oldfd, newfd, flags); + } + return ReturnRc(L, rc, olderr); +} + +// unix.pipe([flags]) → reader, writer, errno +// flags can have O_CLOEXEC +static int LuaUnixPipe(lua_State *L) { + int flags, olderr, pipefd[2]; + olderr = errno; + flags = luaL_optinteger(L, 1, 0); + if (!pipe2(pipefd, flags)) { + lua_pushinteger(L, pipefd[0]); + lua_pushinteger(L, pipefd[1]); + return 2; + } else { + lua_pushnil(L); + lua_pushnil(L); + lua_pushinteger(L, errno); + errno = olderr; + return 3; + } +} + +// unix.getsid(pid) → sid, errno +static int LuaUnixGetsid(lua_State *L) { + int rc, pid, olderr; + olderr = errno; + pid = luaL_checkinteger(L, 1); + rc = getsid(pid); + return ReturnRc(L, rc, olderr); +} + +// unix.getpgid(pid) → pgid, errno +static int LuaUnixGetpgid(lua_State *L) { + int rc, pid, olderr; + olderr = errno; + pid = luaL_checkinteger(L, 1); + rc = getpgid(pid); + return ReturnRc(L, rc, olderr); +} + +// unix.umask(mask) → rc, errno +static int LuaUnixUmask(lua_State *L) { + int rc, mask, olderr; + olderr = errno; + mask = luaL_checkinteger(L, 1); + rc = umask(mask); + return ReturnRc(L, rc, olderr); +} + +// unix.setpgid(pid, pgid) → pgid, errno +static int LuaUnixSetpgid(lua_State *L) { + int rc, pid, pgid, olderr; + olderr = errno; + pid = luaL_checkinteger(L, 1); + pgid = luaL_checkinteger(L, 2); + rc = setpgid(pid, pgid); + return ReturnRc(L, rc, olderr); +} + +// unix.setsid() → sid, errno +static int LuaUnixSetsid(lua_State *L) { + int rc, olderr; + olderr = errno; + rc = setsid(); + return ReturnRc(L, rc, olderr); +} + +// unix.getuid() → uid, errno +static int LuaUnixGetuid(lua_State *L) { + int rc, olderr; + olderr = errno; + rc = getuid(); + return ReturnRc(L, rc, olderr); +} + +// unix.getgid() → gid, errno +static int LuaUnixGetgid(lua_State *L) { + int rc, olderr; + olderr = errno; + rc = getgid(); + return ReturnRc(L, rc, olderr); +} + +// unix.gettime([clock]) → seconds, nanos, errno +static int LuaUnixGettime(lua_State *L) { + struct timespec ts; + int rc, clock, olderr; + olderr = errno; + clock = luaL_optinteger(L, 1, CLOCK_REALTIME); + rc = clock_gettime(clock, &ts); + if (rc != -1) { + lua_pushinteger(L, ts.tv_sec); + lua_pushinteger(L, ts.tv_nsec); + return 2; + } else { + lua_pushnil(L); + lua_pushnil(L); + lua_pushinteger(L, errno); + errno = olderr; + return 3; + } +} + +// unix.nanosleep(seconds, nanos) → remseconds, remnanos, errno +static int LuaUnixNanosleep(lua_State *L) { + int rc, olderr; + struct timespec req, rem; + olderr = errno; + req.tv_sec = luaL_checkinteger(L, 1); + req.tv_nsec = luaL_optinteger(L, 2, 0); + rc = nanosleep(&req, &rem); + if (rc != -1) { + lua_pushinteger(L, rem.tv_sec); + lua_pushinteger(L, rem.tv_nsec); + return 2; + } else { + lua_pushnil(L); + lua_pushnil(L); + lua_pushinteger(L, errno); + errno = olderr; + return 3; + } +} + +// unix.sync(fd) +static int LuaUnixSync(lua_State *L) { + sync(); + return 0; +} + +// unix.fsync(fd) → rc, errno +static int LuaUnixFsync(lua_State *L) { + int rc, fd, olderr; + olderr = errno; + fd = luaL_checkinteger(L, 1); + rc = fsync(fd); + return ReturnRc(L, rc, olderr); +} + +// unix.fdatasync(fd) → rc, errno +static int LuaUnixFdatasync(lua_State *L) { + int rc, fd, olderr; + olderr = errno; + fd = luaL_checkinteger(L, 1); + rc = fdatasync(fd); + return ReturnRc(L, rc, olderr); +} + +// unix.open(path, flags[, mode]) → fd, errno +static int LuaUnixOpen(lua_State *L) { + const char *file; + int rc, flags, mode, olderr; + olderr = errno; + file = luaL_checklstring(L, 1, 0); + flags = luaL_checkinteger(L, 2); + mode = luaL_optinteger(L, 3, 0); + rc = open(file, flags, mode); + return ReturnRc(L, rc, olderr); +} + +// unix.close(fd) → rc, errno +static int LuaUnixClose(lua_State *L) { + int rc, fd, olderr; + olderr = errno; + fd = luaL_checkinteger(L, 1); + rc = close(fd); + return ReturnRc(L, rc, olderr); +} + +// unix.seek(fd, offset, whence) → newpos, errno +// where whence ∈ {SEEK_SET, SEEK_CUR, SEEK_END} +// whence defaults to SEEK_SET +static int LuaUnixSeek(lua_State *L) { + int64_t newpos, offset; + int fd, olderr, whence; + olderr = errno; + fd = luaL_checkinteger(L, 1); + offset = luaL_checkinteger(L, 2); + whence = luaL_optinteger(L, 3, SEEK_SET); + newpos = lseek(fd, offset, whence); + return ReturnRc(L, newpos, olderr); +} + +// unix.truncate(path, length) → rc, errno +// unix.truncate(fd, length) → rc, errno +static int LuaUnixTruncate(lua_State *L) { + int64_t length; + const char *path; + int rc, fd, olderr, whence; + olderr = errno; + if (lua_isinteger(L, 1)) { + fd = luaL_checkinteger(L, 1); + length = luaL_checkinteger(L, 2); + rc = ftruncate(fd, length); + } else if (lua_isstring(L, 1)) { + path = luaL_checkstring(L, 1); + length = luaL_checkinteger(L, 2); + rc = truncate(path, length); + } else { + luaL_argerror(L, 1, "not integer or string"); + unreachable; + } + return ReturnRc(L, rc, olderr); +} + +// unix.read(fd[, bufsiz, offset]) → data, errno +static int LuaUnixRead(lua_State *L) { + char *buf; + size_t got; + int fd, olderr; + int64_t rc, bufsiz, offset; + olderr = errno; + fd = luaL_checkinteger(L, 1); + bufsiz = luaL_optinteger(L, 2, BUFSIZ); + offset = luaL_optinteger(L, 3, -1); + bufsiz = MIN(bufsiz, 0x7ffff000); + buf = xmalloc(bufsiz); + if (offset == -1) { + rc = read(fd, buf, bufsiz); + } else { + rc = pread(fd, buf, bufsiz, offset); + } + if (rc != -1) { + got = rc; + lua_pushlstring(L, buf, got); + lua_pushnil(L); + } else { + lua_pushnil(L); + lua_pushinteger(L, errno); + errno = olderr; + } + free(buf); + return 2; +} + +// unix.write(fd, data[, offset]) → rc, errno +static int LuaUnixWrite(lua_State *L) { + size_t size; + int fd, olderr; + const char *data; + int64_t rc, offset; + olderr = errno; + fd = luaL_checkinteger(L, 1); + data = luaL_checklstring(L, 2, &size); + offset = luaL_optinteger(L, 3, -1); + size = MIN(size, 0x7ffff000); + if (offset == -1) { + rc = write(fd, data, size); + } else { + rc = pwrite(fd, data, size, offset); + } + return ReturnRc(L, rc, olderr); +} + +// unix.stat(path) → UnixStat*, errno +// unix.stat(fd) → UnixStat*, errno +static int LuaUnixStat(lua_State *L) { + const char *path; + int rc, fd, olderr; + struct UnixStat **ust, *st; + olderr = errno; + st = xmalloc(sizeof(struct UnixStat)); + if (lua_isinteger(L, 1)) { + fd = luaL_checkinteger(L, 1); + rc = fstat(fd, &st->st); + } else if (lua_isstring(L, 1)) { + path = luaL_checkstring(L, 1); + rc = stat(path, &st->st); + } else { + luaL_argerror(L, 1, "not integer or string"); + unreachable; + } + if (rc == -1) { + lua_pushnil(L); + lua_pushinteger(L, errno); + errno = olderr; + free(st); + return 2; + } + st->refs = 1; + ust = lua_newuserdatauv(L, sizeof(st), 1); + luaL_setmetatable(L, "UnixStat*"); + *ust = st; + return 1; +} + +// unix.opendir(path) → UnixDir*, errno +// unix.opendir(fd) → UnixDir*, errno +static int LuaUnixOpendir(lua_State *L) { + DIR *rc; + int fd, olderr; + const char *path; + struct UnixDir **udir, *dir; + olderr = errno; + dir = xcalloc(1, sizeof(struct UnixDir)); + if (lua_isinteger(L, 1)) { + fd = luaL_checkinteger(L, 1); + rc = fdopendir(fd); + } else if (lua_isstring(L, 1)) { + path = luaL_checkstring(L, 1); + rc = opendir(path); + } else { + luaL_argerror(L, 1, "not integer or string"); + unreachable; + } + if (!rc) { + lua_pushnil(L); + lua_pushinteger(L, errno); + errno = olderr; + free(dir); + return 2; + } + dir->refs = 1; + dir->dir = rc; + udir = lua_newuserdatauv(L, sizeof(dir), 1); + luaL_setmetatable(L, "UnixDir*"); + *udir = dir; + return 1; +} + +// unix.socket([family[, type[, protocol]]]) → fd, errno +// SOCK_CLOEXEC may be or'd into type +// family defaults to AF_INET +// type defaults to SOCK_STREAM +// protocol defaults to IPPROTO_TCP +static int LuaUnixSocket(lua_State *L) { + const char *file; + int rc, olderr, family, type, protocol; + olderr = errno; + family = luaL_optinteger(L, 1, AF_INET); + type = luaL_optinteger(L, 2, SOCK_STREAM); + protocol = luaL_optinteger(L, 3, IPPROTO_TCP); + rc = socket(family, type, protocol); + return ReturnRc(L, rc, olderr); +} + +// unix.socketpair([family[, type[, protocol]]]) → fd1, fd2, errno +// SOCK_CLOEXEC may be or'd into type +// family defaults to AF_INET +// type defaults to SOCK_STREAM +// protocol defaults to IPPROTO_TCP +static int LuaUnixSocketpair(lua_State *L) { + int olderr, family, type, protocol, sv[2]; + olderr = errno; + family = luaL_optinteger(L, 1, AF_INET); + type = luaL_optinteger(L, 2, SOCK_STREAM); + protocol = luaL_optinteger(L, 3, IPPROTO_TCP); + if (!socketpair(family, type, protocol, sv)) { + lua_pushinteger(L, sv[0]); + lua_pushinteger(L, sv[1]); + return 2; + } else { + lua_pushnil(L); + lua_pushnil(L); + lua_pushinteger(L, errno); + errno = olderr; + return 3; + } +} + +// unix.bind(fd, ip, port) → rc, errno +// SOCK_CLOEXEC may be or'd into type +// family defaults to AF_INET +// type defaults to SOCK_STREAM +// protocol defaults to IPPROTO_TCP +static int LuaUnixBind(lua_State *L) { + int rc, olderr, fd; + struct sockaddr_in sa; + bzero(&sa, sizeof(sa)); + olderr = errno; + fd = luaL_checkinteger(L, 1); + sa.sin_addr.s_addr = htonl(luaL_checkinteger(L, 2)); + sa.sin_port = htons(luaL_checkinteger(L, 3)); + rc = bind(fd, &sa, sizeof(sa)); + return ReturnRc(L, rc, olderr); +} + +// unix.connect(fd, ip, port) → rc, errno +// SOCK_CLOEXEC may be or'd into type +// family defaults to AF_INET +// type defaults to SOCK_STREAM +// protocol defaults to IPPROTO_TCP +static int LuaUnixConnect(lua_State *L) { + int rc, olderr, fd; + struct sockaddr_in sa; + bzero(&sa, sizeof(sa)); + olderr = errno; + fd = luaL_checkinteger(L, 1); + sa.sin_addr.s_addr = htonl(luaL_checkinteger(L, 2)); + sa.sin_port = htons(luaL_checkinteger(L, 3)); + rc = connect(fd, &sa, sizeof(sa)); + return ReturnRc(L, rc, olderr); +} + +// unix.listen(fd[, backlog]) → rc, errno +static int LuaUnixListen(lua_State *L) { + int rc, fd, olderr, backlog; + olderr = errno; + fd = luaL_checkinteger(L, 1); + backlog = luaL_optinteger(L, 2, 10); + rc = listen(fd, backlog); + return ReturnRc(L, rc, olderr); +} + +// unix.getsockname(fd) → ip, port, errno +static int LuaUnixGetsockname(lua_State *L) { + int fd, olderr; + uint32_t addrsize; + struct sockaddr_in sa; + olderr = errno; + bzero(&sa, sizeof(sa)); + addrsize = sizeof(sa); + fd = luaL_checkinteger(L, 1); + if (!getsockname(fd, &sa, &addrsize)) { + lua_pushinteger(L, ntohl(sa.sin_addr.s_addr)); + lua_pushinteger(L, ntohs(sa.sin_port)); + return 2; + } else { + lua_pushnil(L); + lua_pushnil(L); + lua_pushinteger(L, errno); + errno = olderr; + return 3; + } +} + +// unix.getpeername(fd) → ip, port, errno +static int LuaUnixGetpeername(lua_State *L) { + int fd, olderr; + uint32_t addrsize; + struct sockaddr_in sa; + olderr = errno; + bzero(&sa, sizeof(sa)); + addrsize = sizeof(sa); + fd = luaL_checkinteger(L, 1); + if (!getpeername(fd, &sa, &addrsize)) { + lua_pushinteger(L, ntohl(sa.sin_addr.s_addr)); + lua_pushinteger(L, ntohs(sa.sin_port)); + return 2; + } else { + lua_pushnil(L); + lua_pushnil(L); + lua_pushinteger(L, errno); + errno = olderr; + return 3; + } +} + +// unix.accept(serverfd) → clientfd, ip, port, errno +static int LuaUnixAccept(lua_State *L) { + uint32_t addrsize; + struct sockaddr_in sa; + int clientfd, serverfd, olderr; + olderr = errno; + bzero(&sa, sizeof(sa)); + addrsize = sizeof(sa); + serverfd = luaL_checkinteger(L, 1); + clientfd = accept(serverfd, &sa, &addrsize); + if (clientfd != -1) { + lua_pushinteger(L, clientfd); + lua_pushinteger(L, ntohl(sa.sin_addr.s_addr)); + lua_pushinteger(L, ntohs(sa.sin_port)); + return 3; + } else { + lua_pushnil(L); + lua_pushnil(L); + lua_pushnil(L); + lua_pushinteger(L, errno); + errno = olderr; + return 4; + } +} + +// unix.recvfrom(fd[, bufsiz[, flags]]) → data, ip, port, errno +// flags can have MSG_{WAITALL,DONTROUTE,PEEK,OOB}, etc. +static int LuaUnixRecvfrom(lua_State *L) { + char *buf; + size_t got; + ssize_t rc; + uint32_t addrsize; + struct sockaddr_in sa; + int fd, flags, bufsiz, olderr; + olderr = errno; + bzero(&sa, sizeof(sa)); + addrsize = sizeof(sa); + fd = luaL_checkinteger(L, 1); + bufsiz = luaL_optinteger(L, 2, 1500); + flags = luaL_optinteger(L, 3, 0); + bufsiz = MIN(bufsiz, 0x7ffff000); + buf = xmalloc(bufsiz); + rc = recvfrom(fd, buf, bufsiz, flags, &sa, &addrsize); + if (rc != -1) { + got = rc; + lua_pushlstring(L, buf, got); + lua_pushinteger(L, ntohl(sa.sin_addr.s_addr)); + lua_pushinteger(L, ntohs(sa.sin_port)); + lua_pushnil(L); + } else { + lua_pushnil(L); + lua_pushnil(L); + lua_pushnil(L); + lua_pushinteger(L, errno); + errno = olderr; + } + free(buf); + return 4; +} + +// unix.sendto(fd, data, ip, port[, flags]) → sent, errno +// flags MSG_OOB, MSG_DONTROUTE, MSG_NOSIGNAL, etc. +static int LuaUnixSendto(lua_State *L) { + char *data; + ssize_t rc; + size_t sent, size; + struct sockaddr_in sa; + int fd, flags, bufsiz, olderr; + olderr = errno; + bzero(&sa, sizeof(sa)); + fd = luaL_checkinteger(L, 1); + data = luaL_checklstring(L, 2, &size); + size = MIN(size, 0x7ffff000); + sa.sin_addr.s_addr = htonl(luaL_checkinteger(L, 3)); + sa.sin_port = htons(luaL_checkinteger(L, 4)); + flags = luaL_optinteger(L, 5, 0); + rc = sendto(fd, data, size, flags, &sa, sizeof(sa)); + return ReturnRc(L, rc, olderr); +} + +// unix.shutdown(fd, how) → rc, errno +// how can be SHUT_RD, SHUT_WR, or SHUT_RDWR +static int LuaUnixShutdown(lua_State *L) { + int rc, fd, how, olderr; + olderr = errno; + fd = luaL_checkinteger(L, 1); + how = luaL_checkinteger(L, 2); + rc = shutdown(fd, how); + return ReturnRc(L, rc, olderr); +} + +// unix.sigprocmask(how, mask) → oldmask, errno +// how can be SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK +static int LuaUnixSigprocmask(lua_State *L) { + uint64_t imask; + int how, olderr; + sigset_t mask, oldmask; + olderr = errno; + how = luaL_checkinteger(L, 1); + imask = luaL_checkinteger(L, 2); + bzero(&mask, sizeof(mask)); + if (how == SIG_SETMASK) { + sigprocmask(how, 0, &mask); + } + mask.__bits[0] = imask; + if (!sigprocmask(how, &mask, &oldmask)) { + lua_pushinteger(L, oldmask.__bits[0]); + return 1; + } else { + lua_pushnil(L); + lua_pushinteger(L, errno); + errno = olderr; + return 2; + } +} + +// unix.sigaction(sig, handler[, flags[, mask]]) → rc, errno +// sig can be SIGINT, SIGQUIT, SIGTERM, SIGUSR1, etc. +// handler can be SIG_IGN or SIG_DFL for time being +// note: this api will be changed in the future +static int LuaUnixSigaction(lua_State *L) { + void *handler; + int sig, olderr; + struct sigaction sa; + uint64_t flags, imask; + olderr = errno; + sig = luaL_checkinteger(L, 1); + handler = (void *)luaL_checkinteger(L, 2); + flags = luaL_optinteger(L, 3, SA_RESTART); + imask = luaL_optinteger(L, 4, 0); + if (handler != SIG_DFL && handler != SIG_IGN) { + luaL_argerror(L, 2, "handler not SIG_DFL or SIG_IGN"); + unreachable; + } + sa.sa_handler = handler; + sa.sa_flags = flags; + sa.sa_mask.__bits[0] = imask; + sa.sa_mask.__bits[1] = 0; + if (!sigaction(sig, &sa, 0)) { + lua_pushinteger(L, 0); + return 1; + } else { + lua_pushnil(L); + lua_pushinteger(L, errno); + errno = olderr; + return 2; + } +} + +// unix.strerror(errno) → str +static int LuaUnixStrerror(lua_State *L) { + lua_pushstring(L, strerror(luaL_checkinteger(L, 1))); + return 1; +} + +//////////////////////////////////////////////////////////////////////////////// +// UnixStat* object + +static dontinline struct stat *GetUnixStat(lua_State *L) { + struct UnixStat **ust; + ust = luaL_checkudata(L, 1, "UnixStat*"); + return &(*ust)->st; +} + +static int LuaUnixStatSize(lua_State *L) { + return ReturnInteger(L, GetUnixStat(L)->st_size); +} + +static int LuaUnixStatMode(lua_State *L) { + return ReturnInteger(L, GetUnixStat(L)->st_mode); +} + +static int LuaUnixStatDev(lua_State *L) { + return ReturnInteger(L, GetUnixStat(L)->st_dev); +} + +static int LuaUnixStatIno(lua_State *L) { + return ReturnInteger(L, GetUnixStat(L)->st_ino); +} + +static int LuaUnixStatNlink(lua_State *L) { + return ReturnInteger(L, GetUnixStat(L)->st_nlink); +} + +static int LuaUnixStatRdev(lua_State *L) { + return ReturnInteger(L, GetUnixStat(L)->st_rdev); +} + +static int LuaUnixStatUid(lua_State *L) { + return ReturnInteger(L, GetUnixStat(L)->st_uid); +} + +static int LuaUnixStatGid(lua_State *L) { + return ReturnInteger(L, GetUnixStat(L)->st_gid); +} + +static int LuaUnixStatBlocks(lua_State *L) { + return ReturnInteger(L, GetUnixStat(L)->st_blocks); +} + +static int LuaUnixStatBlksize(lua_State *L) { + return ReturnInteger(L, GetUnixStat(L)->st_blksize); +} + +static int LuaUnixStatAtim(lua_State *L) { + return ReturnTimespec(L, &GetUnixStat(L)->st_atim); +} + +static int LuaUnixStatMtim(lua_State *L) { + return ReturnTimespec(L, &GetUnixStat(L)->st_mtim); +} + +static int LuaUnixStatCtim(lua_State *L) { + return ReturnTimespec(L, &GetUnixStat(L)->st_ctim); +} + +static void FreeUnixStat(struct UnixStat *stat) { + if (!--stat->refs) { + free(stat); + } +} + +static int LuaUnixStatGc(lua_State *L) { + struct UnixStat **ust; + ust = luaL_checkudata(L, 1, "UnixStat*"); + if (*ust) { + FreeUnixStat(*ust); + *ust = 0; + } + return 0; +} + +static const luaL_Reg kLuaUnixStatMeth[] = { + {"size", LuaUnixStatSize}, // + {"mode", LuaUnixStatMode}, // + {"dev", LuaUnixStatDev}, // + {"ino", LuaUnixStatIno}, // + {"nlink", LuaUnixStatNlink}, // + {"rdev", LuaUnixStatRdev}, // + {"uid", LuaUnixStatUid}, // + {"gid", LuaUnixStatGid}, // + {"atim", LuaUnixStatAtim}, // + {"mtim", LuaUnixStatMtim}, // + {"ctim", LuaUnixStatCtim}, // + {"blocks", LuaUnixStatBlocks}, // + {"blksize", LuaUnixStatBlksize}, // + {0}, // +}; + +static const luaL_Reg kLuaUnixStatMeta[] = { + {"__gc", LuaUnixStatGc}, // + {0}, // +}; + +static void LuaUnixStatObj(lua_State *L) { + luaL_newmetatable(L, "UnixStat*"); + luaL_setfuncs(L, kLuaUnixStatMeta, 0); + luaL_newlibtable(L, kLuaUnixStatMeth); + luaL_setfuncs(L, kLuaUnixStatMeth, 0); + lua_setfield(L, -2, "__index"); + lua_pop(L, 1); +} + +//////////////////////////////////////////////////////////////////////////////// +// UnixDir* object + +static struct UnixDir **GetUnixDirSelf(lua_State *L) { + return luaL_checkudata(L, 1, "UnixDir*"); +} + +static DIR *GetDirOrDie(lua_State *L) { + struct UnixDir **udir; + udir = GetUnixDirSelf(L); + assert((*udir)->dir); + if (*udir) return (*udir)->dir; + luaL_argerror(L, 1, "UnixDir* is closed"); + unreachable; +} + +static void FreeUnixDir(struct UnixDir *dir) { + if (!--dir->refs) { + closedir(dir->dir); + } +} + +// UnixDir:close() +// may be called multiple times +// called by the garbage collector too +static int LuaUnixDirClose(lua_State *L) { + struct UnixDir **udir; + udir = GetUnixDirSelf(L); + if (*udir) { + FreeUnixDir(*udir); + *udir = 0; + } + return 0; +} + +// UnixDir:read() → name, kind, ino, off +// returns nil if no more entries +// kind can be DT_UNKNOWN/REG/DIR/BLK/LNK/CHR/FIFO/SOCK +static int LuaUnixDirRead(lua_State *L) { + struct dirent *ent; + if ((ent = readdir(GetDirOrDie(L)))) { + lua_pushlstring(L, ent->d_name, strnlen(ent->d_name, sizeof(ent->d_name))); + lua_pushinteger(L, ent->d_type); + lua_pushinteger(L, ent->d_ino); + lua_pushinteger(L, ent->d_off); + return 4; + } else { + return 0; + } +} + +// UnixDir:fd() → fd, errno +// EOPNOTSUPP if using /zip/ +// EOPNOTSUPP if IsWindows() +static int LuaUnixDirFd(lua_State *L) { + int fd, olderr; + olderr = errno; + fd = dirfd(GetDirOrDie(L)); + if (fd != -1) { + lua_pushinteger(L, fd); + return 1; + } else { + lua_pushnil(L); + lua_pushinteger(L, errno); + errno = olderr; + return 2; + } +} + +// UnixDir:tell() → off +static int LuaUnixDirTell(lua_State *L) { + long off; + off = telldir(GetDirOrDie(L)); + lua_pushinteger(L, off); + return 1; +} + +// UnixDir:rewind() +static int LuaUnixDirRewind(lua_State *L) { + rewinddir(GetDirOrDie(L)); + return 0; +} + +static const luaL_Reg kLuaUnixDirMeth[] = { + {"close", LuaUnixDirClose}, // + {"read", LuaUnixDirRead}, // + {"fd", LuaUnixDirFd}, // + {"tell", LuaUnixDirTell}, // + {"rewind", LuaUnixDirRewind}, // + {0}, // +}; + +static const luaL_Reg kLuaUnixDirMeta[] = { + {"__gc", LuaUnixDirClose}, // + {0}, // +}; + +static void LuaUnixDirObj(lua_State *L) { + luaL_newmetatable(L, "UnixDir*"); + luaL_setfuncs(L, kLuaUnixDirMeta, 0); + luaL_newlibtable(L, kLuaUnixDirMeth); + luaL_setfuncs(L, kLuaUnixDirMeth, 0); + lua_setfield(L, -2, "__index"); + lua_pop(L, 1); +} + +//////////////////////////////////////////////////////////////////////////////// +// UNIX module + +static const luaL_Reg kLuaUnix[] = { + {"exit", LuaUnixExit}, // exit w/o atexit + {"stat", LuaUnixStat}, // get file info + {"open", LuaUnixOpen}, // open file fd at lowest slot + {"close", LuaUnixClose}, // close file or socket + {"seek", LuaUnixSeek}, // seek in file + {"read", LuaUnixRead}, // read from file or socket + {"write", LuaUnixWrite}, // write to file or socket + {"access", LuaUnixAccess}, // check my file authorization + {"fcntl", LuaUnixFcntl}, // manipulate file descriptor + {"chdir", LuaUnixChdir}, // change directory + {"chown", LuaUnixChown}, // change owner of file + {"chmod", LuaUnixChmod}, // change mode of file + {"getcwd", LuaUnixGetcwd}, // get current directory + {"fork", LuaUnixFork}, // make child process via mitosis + {"kill", LuaUnixKill}, // signal child process + {"raise", LuaUnixRaise}, // signal this process + {"wait", LuaUnixWait}, // wait for child to change status + {"pipe", LuaUnixPipe}, // create two anon fifo fds + {"dup", LuaUnixDup}, // copy fd to lowest empty slot + {"mkdir", LuaUnixMkdir}, // make directory + {"rmdir", LuaUnixRmdir}, // remove empty directory + {"opendir", LuaUnixOpendir}, // read directory entry list + {"rename", LuaUnixRename}, // rename file or directory + {"link", LuaUnixLink}, // create hard link + {"unlink", LuaUnixUnlink}, // remove file + {"symlink", LuaUnixSymlink}, // create symbolic link + {"sync", LuaUnixSync}, // flushes files and disks + {"fsync", LuaUnixFsync}, // flush open file + {"fdatasync", LuaUnixFdatasync}, // flush open file w/o metadata + {"truncate", LuaUnixTruncate}, // shrink or extend file medium + {"umask", LuaUnixUmask}, // set file mode creation mask + {"getppid", LuaUnixGetppid}, // get parent process id + {"getpgid", LuaUnixGetpgid}, // get process group id of pid + {"setpgid", LuaUnixSetpgid}, // set process group id for pid + {"getsid", LuaUnixGetsid}, // get session id of pid + {"setsid", LuaUnixSetsid}, // create a new session id + {"getpid", LuaUnixGetpid}, // get id of this process + {"getuid", LuaUnixGetuid}, // get real user id of process + {"getgid", LuaUnixGetgid}, // get real group id of process + {"gettime", LuaUnixGettime}, // get timestamp w/ nano precision + {"nanosleep", LuaUnixNanosleep}, // sleep w/ nano precision + {"socket", LuaUnixSocket}, // create network communication fd + {"socketpair", LuaUnixSocketpair}, // create bidirectional pipe + {"bind", LuaUnixBind}, // reserve network interface address + {"listen", LuaUnixListen}, // begin listening for clients + {"accept", LuaUnixAccept}, // create client fd for client + {"connect", LuaUnixConnect}, // connect to remote address + {"recvfrom", LuaUnixRecvfrom}, // receive udp from some address + {"sendto", LuaUnixSendto}, // send udp to some address + {"shutdown", LuaUnixShutdown}, // make socket half empty or full + {"getpeername", LuaUnixGetpeername}, // get address of remote end + {"getsockname", LuaUnixGetsockname}, // get address of local end + {"sigaction", LuaUnixSigaction}, // install signal handler + {"sigprocmask", LuaUnixSigprocmask}, // change signal mask + {"strerror", LuaUnixStrerror}, // turn errno into string + {0}, // +}; + +int LuaUnix(lua_State *L) { + int i; + char sigbuf[12]; + + luaL_newlib(L, kLuaUnix); + LuaUnixStatObj(L); + LuaUnixDirObj(L); + + // errnos + for (i = 0; kErrorNames[i].x; ++i) { + LuaSetIntField(L, (const char *)((uintptr_t)kErrorNames + kErrorNames[i].s), + *(const int *)((uintptr_t)kErrorNames + kErrorNames[i].x)); + } + + // signals + strcpy(sigbuf, "SIG"); + for (i = 0; kStrSignal[i].x; ++i) { + strcpy(sigbuf + 3, (const char *)((uintptr_t)kStrSignal + kStrSignal[i].s)); + LuaSetIntField(L, sigbuf, + *(const int *)((uintptr_t)kStrSignal + kStrSignal[i].x)); + } + + // open() flags + LuaSetIntField(L, "O_RDONLY", O_RDONLY); // + LuaSetIntField(L, "O_WRONLY", O_WRONLY); // + LuaSetIntField(L, "O_RDWR", O_RDWR); // + LuaSetIntField(L, "O_ACCMODE", O_ACCMODE); // mask of prev three + LuaSetIntField(L, "O_CREAT", O_CREAT); // + LuaSetIntField(L, "O_EXCL", O_EXCL); // + LuaSetIntField(L, "O_TRUNC", O_TRUNC); // + LuaSetIntField(L, "O_CLOEXEC", O_CLOEXEC); // + LuaSetIntField(L, "O_APPEND", O_APPEND); // weird on nt + LuaSetIntField(L, "O_TMPFILE", O_TMPFILE); // linux, windows + LuaSetIntField(L, "O_NOFOLLOW", O_NOFOLLOW); // unix + LuaSetIntField(L, "O_SYNC", O_SYNC); // unix + LuaSetIntField(L, "O_ASYNC", O_ASYNC); // unix + LuaSetIntField(L, "O_NOCTTY", O_NOCTTY); // unix + LuaSetIntField(L, "O_NOATIME", O_NOATIME); // linux + LuaSetIntField(L, "O_EXEC", O_EXEC); // free/openbsd + LuaSetIntField(L, "O_SEARCH", O_SEARCH); // free/netbsd + LuaSetIntField(L, "O_DSYNC", O_DSYNC); // linux/xnu/open/netbsd + LuaSetIntField(L, "O_RSYNC", O_RSYNC); // linux/open/netbsd + LuaSetIntField(L, "O_PATH", O_PATH); // linux + LuaSetIntField(L, "O_VERIFY", O_VERIFY); // freebsd + LuaSetIntField(L, "O_SHLOCK", O_SHLOCK); // bsd + LuaSetIntField(L, "O_EXLOCK", O_EXLOCK); // bsd + LuaSetIntField(L, "O_RANDOM", O_RANDOM); // windows + LuaSetIntField(L, "O_SEQUENTIAL", O_SEQUENTIAL); // windows + LuaSetIntField(L, "O_COMPRESSED", O_COMPRESSED); // windows + LuaSetIntField(L, "O_INDEXED", O_INDEXED); // windows + + // seek() whence + LuaSetIntField(L, "SEEK_SET", SEEK_SET); + LuaSetIntField(L, "SEEK_CUR", SEEK_CUR); + LuaSetIntField(L, "SEEK_END", SEEK_END); + + // fcntl() stuff + LuaSetIntField(L, "F_GETFD", F_GETFD); + LuaSetIntField(L, "F_SETFD", F_SETFD); + LuaSetIntField(L, "F_GETFL", F_GETFL); + LuaSetIntField(L, "F_SETFL", F_SETFL); + LuaSetIntField(L, "F_UNLCK", F_UNLCK); + LuaSetIntField(L, "F_RDLCK", F_RDLCK); + LuaSetIntField(L, "F_WRLCK", F_WRLCK); + LuaSetIntField(L, "F_SETLK", F_SETLK); + LuaSetIntField(L, "F_SETLKW", F_SETLKW); + LuaSetIntField(L, "FD_CLOEXEC", FD_CLOEXEC); + + // access() mode + LuaSetIntField(L, "R_OK", R_OK); + LuaSetIntField(L, "W_OK", W_OK); + LuaSetIntField(L, "X_OK", X_OK); + LuaSetIntField(L, "F_OK", F_OK); + + // wait() options + LuaSetIntField(L, "WNOHANG", WNOHANG); + + // gettime() clocks + LuaSetIntField(L, "CLOCK_REALTIME", CLOCK_REALTIME); // portable + LuaSetIntField(L, "CLOCK_MONOTONIC", CLOCK_MONOTONIC); // portable + LuaSetIntField(L, "CLOCK_MONOTONIC_RAW", CLOCK_MONOTONIC_RAW); // portable + LuaSetIntField(L, "CLOCK_REALTIME_COARSE", CLOCK_REALTIME_COARSE); + LuaSetIntField(L, "CLOCK_MONOTONIC_COARSE", CLOCK_MONOTONIC_COARSE); + LuaSetIntField(L, "CLOCK_PROCESS_CPUTIME_ID", CLOCK_PROCESS_CPUTIME_ID); + LuaSetIntField(L, "CLOCK_TAI", CLOCK_TAI); + LuaSetIntField(L, "CLOCK_PROF", CLOCK_PROF); + LuaSetIntField(L, "CLOCK_BOOTTIME", CLOCK_BOOTTIME); + LuaSetIntField(L, "CLOCK_REALTIME_ALARM", CLOCK_REALTIME_ALARM); + LuaSetIntField(L, "CLOCK_BOOTTIME_ALARM", CLOCK_BOOTTIME_ALARM); + + // socket() family + LuaSetIntField(L, "AF_UNSPEC", AF_UNSPEC); + LuaSetIntField(L, "AF_UNIX", AF_UNIX); + LuaSetIntField(L, "AF_INET", AF_INET); + + // socket() type + LuaSetIntField(L, "SOCK_STREAM", SOCK_STREAM); + LuaSetIntField(L, "SOCK_DGRAM", SOCK_DGRAM); + LuaSetIntField(L, "SOCK_CLOEXEC", SOCK_CLOEXEC); + + // socket() protocol + LuaSetIntField(L, "IPPROTO_TCP", IPPROTO_TCP); + LuaSetIntField(L, "IPPROTO_UDP", IPPROTO_UDP); + + // shutdown() how + LuaSetIntField(L, "SHUT_RD", SHUT_RD); + LuaSetIntField(L, "SHUT_WR", SHUT_WR); + LuaSetIntField(L, "SHUT_RDWR", SHUT_RDWR); + + // recvfrom() / sendto() flags + LuaSetIntField(L, "MSG_WAITALL", MSG_WAITALL); + LuaSetIntField(L, "MSG_DONTROUTE", MSG_DONTROUTE); + LuaSetIntField(L, "MSG_PEEK", MSG_PEEK); + LuaSetIntField(L, "MSG_OOB", MSG_OOB); + LuaSetIntField(L, "MSG_NOSIGNAL", MSG_NOSIGNAL); + + // readdir() type + LuaSetIntField(L, "DT_UNKNOWN", DT_UNKNOWN); + LuaSetIntField(L, "DT_REG", DT_REG); + LuaSetIntField(L, "DT_DIR", DT_DIR); + LuaSetIntField(L, "DT_BLK", DT_BLK); + LuaSetIntField(L, "DT_LNK", DT_LNK); + LuaSetIntField(L, "DT_CHR", DT_CHR); + LuaSetIntField(L, "DT_FIFO", DT_FIFO); + LuaSetIntField(L, "DT_SOCK", DT_SOCK); + + // i/o options + LuaSetIntField(L, "AT_FDCWD", AT_FDCWD); + LuaSetIntField(L, "AT_SYMLINK_NOFOLLOW", AT_SYMLINK_NOFOLLOW); + + // sigprocmask() handlers + LuaSetIntField(L, "SIG_BLOCK", SIG_BLOCK); + LuaSetIntField(L, "SIG_UNBLOCK", SIG_UNBLOCK); + LuaSetIntField(L, "SIG_SETMASK", SIG_SETMASK); + + // sigaction() handlers + LuaSetIntField(L, "SIG_DFL", (intptr_t)SIG_DFL); + LuaSetIntField(L, "SIG_IGN", (intptr_t)SIG_IGN); + + return 1; +} diff --git a/tool/net/net.mk b/tool/net/net.mk index 4e07fabce..f4280f6c2 100644 --- a/tool/net/net.mk +++ b/tool/net/net.mk @@ -27,6 +27,7 @@ TOOL_NET_COMS = \ o/$(MODE)/tool/net/wb.com TOOL_NET_DIRECTDEPS = \ + DSP_SCALE \ LIBC_ALG \ LIBC_BITS \ LIBC_CALLS \ @@ -88,6 +89,7 @@ o/$(MODE)/tool/net/redbean.com.dbg: \ $(TOOL_NET_DEPS) \ o/$(MODE)/tool/net/redbean.o \ o/$(MODE)/tool/net/lre.o \ + o/$(MODE)/tool/net/lunix.o \ o/$(MODE)/tool/net/lmaxmind.o \ o/$(MODE)/tool/net/lsqlite3.o \ o/$(MODE)/tool/net/largon2.o \ @@ -126,6 +128,7 @@ o/$(MODE)/tool/net/redbean.com: \ o/$(MODE)/tool/net/demo/.init.lua.zip.o \ o/$(MODE)/tool/net/demo/.reload.lua.zip.o \ o/$(MODE)/tool/net/demo/sql.lua.zip.o \ +o/$(MODE)/tool/net/demo/unix.lua.zip.o \ o/$(MODE)/tool/net/demo/fetch.lua.zip.o \ o/$(MODE)/tool/net/demo/hello.lua.zip.o \ o/$(MODE)/tool/net/demo/maxmind.lua.zip.o \ @@ -160,11 +163,13 @@ o/$(MODE)/tool/net/redbean-demo.com.dbg: \ $(TOOL_NET_DEPS) \ o/$(MODE)/tool/net/redbean.o \ o/$(MODE)/tool/net/lre.o \ + o/$(MODE)/tool/net/lunix.o \ o/$(MODE)/tool/net/lmaxmind.o \ o/$(MODE)/tool/net/lsqlite3.o \ o/$(MODE)/tool/net/largon2.o \ o/$(MODE)/tool/net/net.pkg \ o/$(MODE)/tool/net/demo/sql.lua.zip.o \ + o/$(MODE)/tool/net/demo/unix.lua.zip.o \ o/$(MODE)/tool/net/demo/fetch.lua.zip.o \ o/$(MODE)/tool/net/demo/hello.lua.zip.o \ o/$(MODE)/tool/net/demo/redbean.lua.zip.o \ @@ -271,6 +276,7 @@ o/$(MODE)/tool/net/redbean-unsecure.com.dbg: \ $(TOOL_NET_DEPS) \ o/$(MODE)/tool/net/redbean-unsecure.o \ o/$(MODE)/tool/net/lre.o \ + o/$(MODE)/tool/net/lunix.o \ o/$(MODE)/tool/net/lmaxmind.o \ o/$(MODE)/tool/net/lsqlite3.o \ o/$(MODE)/tool/net/net.pkg \ diff --git a/tool/net/redbean.c b/tool/net/redbean.c index 2960f21f7..1a2f7c8ce 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "dsp/scale/cdecimate2xuint8x8.h" #include "libc/bits/bits.h" #include "libc/bits/likely.h" #include "libc/bits/popcnt.h" @@ -46,6 +47,8 @@ #include "libc/nexgen32e/bsf.h" #include "libc/nexgen32e/bsr.h" #include "libc/nexgen32e/crc32.h" +#include "libc/nexgen32e/rdtsc.h" +#include "libc/nexgen32e/rdtscp.h" #include "libc/nt/enum/fileflagandattributes.h" #include "libc/rand/rand.h" #include "libc/runtime/clktck.h" @@ -360,12 +363,13 @@ static bool isinitialized; static bool checkedmethod; static bool sslinitialized; static bool sslfetchverify; +static bool hascontenttype; static bool sslclientverify; static bool connectionclose; static bool hasonworkerstop; static bool hasonworkerstart; +static bool leakcrashreports; static bool hasonhttprequest; -static bool hascontenttype; static bool ishandlingrequest; static bool listeningonport443; static bool hasonprocesscreate; @@ -2978,13 +2982,25 @@ static char *GetLuaResponse(void) { return luaheaderp ? luaheaderp : SetStatus(200, "OK"); } -static bool IsLoopbackClient() { +static bool IsLoopbackClient(void) { uint32_t ip; uint16_t port; GetRemoteAddr(&ip, &port); return IsLoopbackIp(ip); } +static bool IsPrivateClient(void) { + uint32_t ip; + uint16_t port; + GetRemoteAddr(&ip, &port); + return IsLoopbackIp(ip) || IsPrivateIp(ip); +} + +static bool ShouldServeCrashReportDetails(void) { + if (leakcrashreports) return true; + return IsPrivateClient(); +} + static char *LuaOnHttpRequest(void) { char *error; lua_State *L = GL; @@ -2996,9 +3012,9 @@ static char *LuaOnHttpRequest(void) { return CommitOutput(GetLuaResponse()); } else { LogLuaError("OnHttpRequest", lua_tostring(L, -1)); - error = - ServeErrorWithDetail(500, "Internal Server Error", - IsLoopbackClient() ? lua_tostring(L, -1) : NULL); + error = ServeErrorWithDetail( + 500, "Internal Server Error", + ShouldServeCrashReportDetails() ? lua_tostring(L, -1) : NULL); lua_pop(L, 1); // pop error AssertLuaStackIsEmpty(L); return error; @@ -3021,9 +3037,9 @@ static char *ServeLua(struct Asset *a, const char *s, size_t n) { } else { char *error; LogLuaError("lua code", lua_tostring(L, -1)); - error = - ServeErrorWithDetail(500, "Internal Server Error", - IsLoopbackClient() ? lua_tostring(L, -1) : NULL); + error = ServeErrorWithDetail( + 500, "Internal Server Error", + ShouldServeCrashReportDetails() ? lua_tostring(L, -1) : NULL); lua_pop(L, 1); // pop error return error; } @@ -4109,6 +4125,12 @@ static int LuaIsLoopbackClient(lua_State *L) { return 1; } +static int LuaIsPrivateClient(lua_State *L) { + OnlyCallDuringRequest(L, "IsPrivateClient"); + lua_pushboolean(L, IsPrivateClient()); + return 1; +} + static int LuaCategorizeIp(lua_State *L) { lua_pushstring(L, GetIpCategoryName(CategorizeIp(luaL_checkinteger(L, 1)))); return 1; @@ -5282,12 +5304,83 @@ static bool LuaRunAsset(const char *path, bool mandatory) { return !!a; } +static int LuaRdtsc(lua_State *L) { + lua_pushinteger(L, rdtsc()); + return 1; +} + +static int LuaGetCpuNode(lua_State *L) { + lua_pushinteger(L, TSC_AUX_NODE(rdpid())); + return 1; +} + +static int LuaGetCpuCore(lua_State *L) { + lua_pushinteger(L, TSC_AUX_CORE(rdpid())); + return 1; +} + +static int LuaRand(lua_State *L, uint64_t impl(void)) { + lua_pushinteger(L, impl()); + return 1; +} + +static int LuaLemur64(lua_State *L) { + return LuaRand(L, lemur64); +} + +static int LuaRand64(lua_State *L) { + return LuaRand(L, rand64); +} + +static int LuaRdrand(lua_State *L) { + return LuaRand(L, rdrand); +} + +static int LuaRdseed(lua_State *L) { + return LuaRand(L, rdseed); +} + +static int LuaDecimate(lua_State *L) { + size_t n, m; + const char *s; + unsigned char *p; + s = luaL_checklstring(L, 1, &n); + m = ROUNDUP(n, 16); + p = xmalloc(m); + bzero(p + n, m - n); + cDecimate2xUint8x8(m, p, (signed char[8]){-1, -3, 3, 17, 17, 3, -3, -1}); + lua_pushlstring(L, (char *)p, (n + 1) >> 1); + free(p); + return 1; +} + +static int LuaMeasureEntropy(lua_State *L) { + size_t n; + const char *s; + s = luaL_checklstring(L, 1, &n); + lua_pushnumber(L, MeasureEntropy(s, n)); + return 1; +} + +static int LuaGetClientFd(lua_State *L) { + OnlyCallDuringConnection(L, "GetClientFd"); + lua_pushinteger(L, client); + return 1; +} + +static int LuaIsClientUsingSsl(lua_State *L) { + OnlyCallDuringConnection(L, "IsClientUsingSsl"); + lua_pushboolean(L, usessl); + return 1; +} + static const luaL_Reg kLuaFuncs[] = { {"Bsf", LuaBsf}, // {"Bsr", LuaBsr}, // {"CategorizeIp", LuaCategorizeIp}, // {"Crc32", LuaCrc32}, // {"Crc32c", LuaCrc32c}, // + {"Decimate", LuaDecimate}, // {"DecodeBase64", LuaDecodeBase64}, // {"DecodeLatin1", LuaDecodeLatin1}, // {"EncodeBase64", LuaEncodeBase64}, // @@ -5312,8 +5405,11 @@ static const luaL_Reg kLuaFuncs[] = { {"GetAssetSize", LuaGetAssetSize}, // {"GetBody", LuaGetBody}, // {"GetClientAddr", LuaGetClientAddr}, // + {"GetClientFd", LuaGetClientFd}, // {"GetComment", LuaGetAssetComment}, // {"GetCookie", LuaGetCookie}, // + {"GetCpuCore", LuaGetCpuCore}, // + {"GetCpuNode", LuaGetCpuNode}, // {"GetCryptoHash", LuaGetCryptoHash}, // {"GetDate", LuaGetDate}, // {"GetEffectivePath", LuaGetEffectivePath}, // @@ -5352,20 +5448,24 @@ static const luaL_Reg kLuaFuncs[] = { {"IsAcceptableHost", LuaIsAcceptableHost}, // {"IsAcceptablePath", LuaIsAcceptablePath}, // {"IsAcceptablePort", LuaIsAcceptablePort}, // + {"IsClientUsingSsl", LuaIsClientUsingSsl}, // {"IsCompressed", LuaIsCompressed}, // {"IsDaemon", LuaIsDaemon}, // {"IsHeaderRepeatable", LuaIsHeaderRepeatable}, // {"IsHiddenPath", LuaIsHiddenPath}, // {"IsLoopbackClient", LuaIsLoopbackClient}, // {"IsLoopbackIp", LuaIsLoopbackIp}, // + {"IsPrivateClient", LuaIsPrivateClient}, // {"IsPrivateIp", LuaIsPrivateIp}, // {"IsPublicIp", LuaIsPublicIp}, // {"IsReasonablePath", LuaIsReasonablePath}, // {"IsValidHttpToken", LuaIsValidHttpToken}, // {"LaunchBrowser", LuaLaunchBrowser}, // + {"Lemur64", LuaLemur64}, // {"LoadAsset", LuaLoadAsset}, // {"Log", LuaLog}, // {"Md5", LuaMd5}, // + {"MeasureEntropy", LuaMeasureEntropy}, // {"ParseHost", LuaParseHost}, // {"ParseHttpDateTime", LuaParseHttpDateTime}, // {"ParseIp", LuaParseIp}, // @@ -5389,6 +5489,10 @@ static const luaL_Reg kLuaFuncs[] = { {"ProgramTimeout", LuaProgramTimeout}, // {"ProgramUid", LuaProgramUid}, // {"ProgramUniprocess", LuaProgramUniprocess}, // + {"Rand64", LuaRand64}, // + {"Rdrand", LuaRdrand}, // + {"Rdseed", LuaRdseed}, // + {"Rdtsc", LuaRdtsc}, // {"Route", LuaRoute}, // {"RouteHost", LuaRouteHost}, // {"RoutePath", LuaRoutePath}, // @@ -5434,11 +5538,13 @@ static const luaL_Reg kLuaFuncs[] = { int LuaMaxmind(lua_State *); int LuaRe(lua_State *); +int LuaUnix(lua_State *); int luaopen_argon2(lua_State *); int luaopen_lsqlite3(lua_State *); static const luaL_Reg kLuaLibs[] = { {"re", LuaRe}, // + {"unix", LuaUnix}, // {"maxmind", LuaMaxmind}, // {"lsqlite3", luaopen_lsqlite3}, // #ifndef UNSECURE @@ -6874,6 +6980,7 @@ static void GetOpts(int argc, char *argv[]) { CASE('M', ProgramMaxPayloadSize(ParseInt(optarg))); #ifndef STATIC CASE('e', LuaRunCode(optarg)); + CASE('E', leakcrashreports = true); CASE('A', storeasset = true; StorePath(optarg)); #endif #ifndef UNSECURE