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