From fc097ac2759d97f934441c5d8e507c8f889b9110 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Wed, 22 Jun 2022 03:04:25 -0700 Subject: [PATCH 1/4] Add unix domain socket support to redbean --- examples/greenbean.c | 4 +- libc/{time => calls}/alarm.c | 0 libc/calls/ioctl_siocgifconf.c | 11 ++- libc/sock/accept-sysv.c | 11 ++- libc/sock/accept4-sysv.c | 25 +++--- libc/sock/bind-sysv.c | 31 +++++++ libc/sock/bind.c | 14 +-- libc/sock/connect-sysv.c | 12 +-- libc/sock/getpeername-sysv.c | 13 ++- libc/sock/getsockname-sysv.c | 13 ++- libc/sock/internal.h | 44 ++++----- libc/sock/recvfrom.c | 21 +++-- libc/sock/recvmsg.c | 19 ++-- libc/sock/sendmsg.c | 19 ++-- libc/sock/sendto.c | 11 +-- libc/sock/sockaddr2bsd.c | 66 ++++++++++++++ libc/sock/sockaddr2linux.c | 60 +++++++++++++ libc/sysv/calls/__sys_bind.s | 2 + libc/sysv/calls/sys_bind.s | 2 - libc/sysv/syscalls.sh | 2 +- test/libc/sock/unix_test.c | 116 ++++++++++++++++++++++++ third_party/lua/lunix.c | 160 +++++++++++++++++++-------------- tool/net/demo/unix-unix.lua | 89 ++++++++++++++++++ tool/net/help.txt | 19 +++- tool/net/net.mk | 2 + 25 files changed, 594 insertions(+), 172 deletions(-) rename libc/{time => calls}/alarm.c (100%) create mode 100644 libc/sock/bind-sysv.c create mode 100644 libc/sock/sockaddr2bsd.c create mode 100644 libc/sock/sockaddr2linux.c create mode 100644 libc/sysv/calls/__sys_bind.s delete mode 100644 libc/sysv/calls/sys_bind.s create mode 100644 test/libc/sock/unix_test.c create mode 100644 tool/net/demo/unix-unix.lua diff --git a/examples/greenbean.c b/examples/greenbean.c index e92ceef8f..c5a0ff59f 100644 --- a/examples/greenbean.c +++ b/examples/greenbean.c @@ -71,8 +71,8 @@ * Like redbean, greenbean has superior performance too, with an * advantage on benchmarks biased towards high connection counts * - * $ wrk -c 300 -t 32 --latency http://10.10.10.124:8080/ - * Running 10s test @ http://10.10.10.124:8080/ + * $ wrk -c 300 -t 32 --latency http://127.0.0.1:8080/ + * Running 10s test @ http://127.0.0.1:8080/ * 32 threads and 300 connections * Thread Stats Avg Stdev Max +/- Stdev * Latency 661.06us 5.11ms 96.22ms 98.85% diff --git a/libc/time/alarm.c b/libc/calls/alarm.c similarity index 100% rename from libc/time/alarm.c rename to libc/calls/alarm.c diff --git a/libc/calls/ioctl_siocgifconf.c b/libc/calls/ioctl_siocgifconf.c index 4d4c178b1..82bf756a9 100644 --- a/libc/calls/ioctl_siocgifconf.c +++ b/libc/calls/ioctl_siocgifconf.c @@ -93,12 +93,21 @@ static int ioctl_siocgifconf_sysv(int fd, struct ifconf *ifc) { return rc; } +forceinline void Sockaddr2linux(void *saddr) { + char *p; + if (saddr) { + p = saddr; + p[0] = p[1]; + p[1] = 0; + } +} + /* Used for all the ioctl that returns sockaddr structure that * requires adjustment between Linux and XNU */ static int ioctl_siocgifaddr_sysv(int fd, uint64_t op, struct ifreq *ifr) { if (sys_ioctl(fd, op, ifr) == -1) return -1; - if (IsBsd()) sockaddr2linux(&ifr->ifr_addr); + if (IsBsd()) Sockaddr2linux(&ifr->ifr_addr); return 0; } diff --git a/libc/sock/accept-sysv.c b/libc/sock/accept-sysv.c index 3d8b0d854..084386275 100644 --- a/libc/sock/accept-sysv.c +++ b/libc/sock/accept-sysv.c @@ -21,8 +21,15 @@ int sys_accept(int server, void *addr, uint32_t *addrsize) { int client; - if ((client = __sys_accept(server, addr, addrsize, 0)) != -1 && IsBsd()) { - sockaddr2linux(addr); + uint32_t size; + union sockaddr_storage_bsd bsd; + if (!IsBsd()) { + client = __sys_accept(server, addr, addrsize, 0); + } else { + size = sizeof(bsd); + if ((client = __sys_accept(server, &bsd, &size, 0)) != -1) { + sockaddr2linux(&bsd, size, addr, addrsize); + } } return client; } diff --git a/libc/sock/accept4-sysv.c b/libc/sock/accept4-sysv.c index 84b9751f0..43c5c1e82 100644 --- a/libc/sock/accept4-sysv.c +++ b/libc/sock/accept4-sysv.c @@ -21,27 +21,26 @@ #include "libc/sock/internal.h" #include "libc/sock/sock.h" -#define __NR_accept4_linux 0x0120 /* rhel5:enosysevil */ - int sys_accept4(int server, void *addr, uint32_t *addrsize, int flags) { - static bool once, demodernize; + if (!flags) return sys_accept(server, addr, addrsize); int olderr, client; - if (!flags || demodernize) goto TriedAndTrue; + union sockaddr_storage_bsd bsd; + uint32_t size = sizeof(bsd); + void *out_addr = !IsBsd() ? addr : &bsd; + uint32_t *out_addrsize = !IsBsd() ? addrsize : &size; + static bool demodernize; + if (demodernize) goto TriedAndTrue; olderr = errno; - client = __sys_accept4(server, addr, addrsize, flags); + client = __sys_accept4(server, out_addr, out_addrsize, flags); if (client == -1 && errno == ENOSYS) { errno = olderr; + demodernize = true; TriedAndTrue: - client = __fixupnewsockfd(__sys_accept(server, addr, addrsize, 0), flags); - } else if (SupportsLinux() && !once) { - once = true; - if (client == __NR_accept4_linux) { - demodernize = true; - goto TriedAndTrue; - } + client = __fixupnewsockfd(__sys_accept(server, out_addr, out_addrsize, 0), + flags); } if (client != -1 && IsBsd()) { - sockaddr2linux(addr); + sockaddr2linux(&bsd, size, addr, addrsize); } return client; } diff --git a/libc/sock/bind-sysv.c b/libc/sock/bind-sysv.c new file mode 100644 index 000000000..553005e2c --- /dev/null +++ b/libc/sock/bind-sysv.c @@ -0,0 +1,31 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/dce.h" +#include "libc/sock/internal.h" + +int sys_bind(int fd, const void *addr, uint32_t addrsize) { + union sockaddr_storage_bsd bsd; + if (!IsBsd()) { + return __sys_bind(fd, addr, addrsize); + } else if (!sockaddr2bsd(addr, addrsize, &bsd, &addrsize)) { + return __sys_bind(fd, &bsd.sa, addrsize); + } else { + return -1; + } +} diff --git a/libc/sock/bind.c b/libc/sock/bind.c index 0a9074278..26726ce13 100644 --- a/libc/sock/bind.c +++ b/libc/sock/bind.c @@ -16,7 +16,6 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/assert.h" #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/dce.h" @@ -25,7 +24,6 @@ #include "libc/sock/sock.h" #include "libc/sock/sockdebug.h" #include "libc/sock/syscall_fd.internal.h" -#include "libc/str/str.h" #include "libc/sysv/errfuns.h" /** @@ -40,21 +38,11 @@ */ int bind(int fd, const void *addr, uint32_t addrsize) { int rc; - char addrbuf[72]; if (!addr || (IsAsan() && !__asan_is_valid(addr, addrsize))) { rc = efault(); } else if (addrsize >= sizeof(struct sockaddr_in)) { if (!IsWindows()) { - if (!IsBsd()) { - rc = sys_bind(fd, addr, addrsize); - } else { - char addr2[sizeof( - struct sockaddr_un_bsd)]; /* sockaddr_un_bsd is the largest */ - assert(addrsize <= sizeof(addr2)); - memcpy(&addr2, addr, addrsize); - sockaddr2bsd(&addr2[0]); - rc = sys_bind(fd, &addr2, addrsize); - } + rc = sys_bind(fd, addr, addrsize); } else if (__isfdkind(fd, kFdSocket)) { rc = sys_bind_nt(&g_fds.p[fd], addr, addrsize); } else { diff --git a/libc/sock/connect-sysv.c b/libc/sock/connect-sysv.c index cc8de8e72..24b0f62bc 100644 --- a/libc/sock/connect-sysv.c +++ b/libc/sock/connect-sysv.c @@ -16,20 +16,16 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/assert.h" #include "libc/dce.h" #include "libc/sock/internal.h" -#include "libc/str/str.h" -#include "libc/sysv/errfuns.h" int sys_connect(int fd, const void *addr, uint32_t addrsize) { + union sockaddr_storage_bsd bsd; if (!IsBsd()) { return __sys_connect(fd, addr, addrsize); + } else if (!sockaddr2bsd(addr, addrsize, &bsd, &addrsize)) { + return __sys_connect(fd, &bsd.sa, addrsize); } else { - char addr2[sizeof(struct sockaddr_un_bsd)]; /* sockaddr_un_bsd is the largest */ - assert(addrsize <= sizeof(addr2)); - memcpy(&addr2, addr, addrsize); - sockaddr2bsd(&addr2[0]); - return __sys_connect(fd, &addr2, addrsize); + return -1; } } diff --git a/libc/sock/getpeername-sysv.c b/libc/sock/getpeername-sysv.c index 61160801e..d7993a9bd 100644 --- a/libc/sock/getpeername-sysv.c +++ b/libc/sock/getpeername-sysv.c @@ -20,9 +20,16 @@ #include "libc/sock/internal.h" int sys_getpeername(int fd, void *out_addr, uint32_t *out_addrsize) { - int rc = __sys_getpeername(fd, out_addr, out_addrsize); - if (rc != -1 && IsBsd()) { - sockaddr2linux(out_addr); + int rc; + uint32_t size; + union sockaddr_storage_bsd bsd; + if (!IsBsd()) { + rc = __sys_getpeername(fd, out_addr, out_addrsize); + } else { + size = sizeof(bsd); + if ((rc = __sys_getpeername(fd, &bsd, &size)) != -1) { + sockaddr2linux(&bsd, size, out_addr, out_addrsize); + } } return rc; } diff --git a/libc/sock/getsockname-sysv.c b/libc/sock/getsockname-sysv.c index f62259e05..19715117a 100644 --- a/libc/sock/getsockname-sysv.c +++ b/libc/sock/getsockname-sysv.c @@ -20,9 +20,16 @@ #include "libc/sock/internal.h" int sys_getsockname(int fd, void *out_addr, uint32_t *out_addrsize) { - int rc = __sys_getsockname(fd, out_addr, out_addrsize); - if (rc != -1 && IsBsd()) { - sockaddr2linux(out_addr); + int rc; + uint32_t size; + union sockaddr_storage_bsd bsd; + if (!IsBsd()) { + rc = __sys_getsockname(fd, out_addr, out_addrsize); + } else { + size = sizeof(bsd); + if ((rc = __sys_getsockname(fd, &bsd, &size)) != -1) { + sockaddr2linux(&bsd, size, out_addr, out_addrsize); + } } return rc; } diff --git a/libc/sock/internal.h b/libc/sock/internal.h index 29f130f11..f22bd0b04 100644 --- a/libc/sock/internal.h +++ b/libc/sock/internal.h @@ -52,6 +52,18 @@ struct sockaddr_un_bsd { char sun_path[108]; }; +union sockaddr_storage_bsd { + struct sockaddr_bsd sa; + struct sockaddr_in_bsd sin; + struct sockaddr_un_bsd sun; +}; + +union sockaddr_storage_linux { + struct sockaddr sa; + struct sockaddr_in sin; + struct sockaddr_un sun; +}; + /* ------------------------------------------------------------------------------------*/ #define SOCKFD_OVERLAP_BUFSIZ 128 @@ -79,10 +91,11 @@ void _firewall(const void *, uint32_t) hidden; int32_t __sys_accept(int32_t, void *, uint32_t *, int) dontdiscard hidden; int32_t __sys_accept4(int32_t, void *, uint32_t *, int) dontdiscard hidden; +int32_t __sys_bind(int32_t, const void *, uint32_t) hidden; int32_t __sys_connect(int32_t, const void *, uint32_t) hidden; -int32_t __sys_socket(int32_t, int32_t, int32_t) hidden; -int32_t __sys_getsockname(int32_t, void *, uint32_t *) hidden; int32_t __sys_getpeername(int32_t, void *, uint32_t *) hidden; +int32_t __sys_getsockname(int32_t, void *, uint32_t *) hidden; +int32_t __sys_socket(int32_t, int32_t, int32_t) hidden; int32_t __sys_socketpair(int32_t, int32_t, int32_t, int32_t[2]) hidden; int32_t sys_accept4(int32_t, void *, uint32_t *, int) dontdiscard hidden; @@ -137,29 +150,10 @@ struct SockFd *_dupsockfd(struct SockFd *) hidden; int64_t GetNtBaseSocket(int64_t) hidden; int sys_close_epoll(int) hidden; -/** - * Converts sockaddr (Linux/Windows) → sockaddr_bsd (XNU/BSD). - */ -forceinline void sockaddr2bsd(void *saddr) { - char *p; - if (saddr) { - p = saddr; - p[1] = p[0]; - p[0] = sizeof(struct sockaddr_in_bsd); - } -} - -/** - * Converts sockaddr_in_bsd (XNU/BSD) → sockaddr (Linux/Windows). - */ -forceinline void sockaddr2linux(void *saddr) { - char *p; - if (saddr) { - p = saddr; - p[0] = p[1]; - p[1] = 0; - } -} +int sockaddr2bsd(const void *, uint32_t, union sockaddr_storage_bsd *, + uint32_t *); +void sockaddr2linux(const union sockaddr_storage_bsd *, uint32_t, + union sockaddr_storage_linux *, uint32_t *); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/sock/recvfrom.c b/libc/sock/recvfrom.c index 3ff010a06..dbad12374 100644 --- a/libc/sock/recvfrom.c +++ b/libc/sock/recvfrom.c @@ -45,19 +45,26 @@ */ ssize_t recvfrom(int fd, void *buf, size_t size, uint32_t flags, void *opt_out_srcaddr, uint32_t *opt_inout_srcaddrsize) { - ssize_t rc, got; + ssize_t rc; + uint32_t sz; + union sockaddr_storage_bsd bsd; if (IsAsan() && (!__asan_is_valid(buf, size) || (opt_out_srcaddr && - !__asan_is_valid(opt_out_srcaddr, *opt_inout_srcaddrsize)))) { + (!__asan_is_valid(opt_inout_srcaddrsize, + sizeof(*opt_inout_srcaddrsize)) || + !__asan_is_valid(opt_out_srcaddr, *opt_inout_srcaddrsize))))) { rc = efault(); } else if (!IsWindows()) { - got = sys_recvfrom(fd, buf, size, flags, opt_out_srcaddr, - opt_inout_srcaddrsize); - if (opt_out_srcaddr && IsBsd() && got != -1) { - sockaddr2linux(opt_out_srcaddr); + if (!IsBsd() || !opt_out_srcaddr) { + rc = sys_recvfrom(fd, buf, size, flags, opt_out_srcaddr, + opt_inout_srcaddrsize); + } else { + sz = sizeof(bsd); + if ((rc = sys_recvfrom(fd, buf, size, flags, &bsd, &sz)) != -1) { + sockaddr2linux(&bsd, sz, opt_out_srcaddr, opt_inout_srcaddrsize); + } } - rc = got; } else if (__isfdopen(fd)) { if (__isfdkind(fd, kFdSocket)) { rc = sys_recvfrom_nt(&g_fds.p[fd], (struct iovec[]){{buf, size}}, 1, diff --git a/libc/sock/recvmsg.c b/libc/sock/recvmsg.c index 644f61886..be4798a0a 100644 --- a/libc/sock/recvmsg.c +++ b/libc/sock/recvmsg.c @@ -41,15 +41,24 @@ */ ssize_t recvmsg(int fd, struct msghdr *msg, int flags) { ssize_t rc, got; + struct msghdr msg2; + union sockaddr_storage_bsd bsd; if (IsAsan() && !__asan_is_valid_msghdr(msg)) { rc = efault(); } else if (!IsWindows()) { - got = sys_recvmsg(fd, msg, flags); - // An address was provided, convert from BSD form - if (msg->msg_name && IsBsd() && got != -1) { - sockaddr2linux(msg->msg_name); + if (IsBsd() && msg->msg_name) { + memcpy(&msg2, msg, sizeof(msg2)); + if (!(rc = sockaddr2bsd(msg->msg_name, msg->msg_namelen, &bsd, + &msg2.msg_namelen))) { + msg2.msg_name = &bsd.sa; + if ((rc = sys_recvmsg(fd, &msg2, flags)) != -1) { + sockaddr2linux(msg2.msg_name, msg2.msg_namelen, msg->msg_name, + &msg->msg_namelen); + } + } + } else { + rc = sys_recvmsg(fd, msg, flags); } - rc = got; } else if (__isfdopen(fd)) { if (!msg->msg_control) { if (__isfdkind(fd, kFdSocket)) { diff --git a/libc/sock/sendmsg.c b/libc/sock/sendmsg.c index 9bd7a1ca7..2c65b9111 100644 --- a/libc/sock/sendmsg.c +++ b/libc/sock/sendmsg.c @@ -44,27 +44,22 @@ */ ssize_t sendmsg(int fd, const struct msghdr *msg, int flags) { int64_t rc; - char addr2[128]; struct msghdr msg2; + union sockaddr_storage_bsd bsd; if (IsAsan() && !__asan_is_valid_msghdr(msg)) { rc = efault(); } else if (!IsWindows()) { if (IsBsd() && msg->msg_name) { - /* An optional address is provided, convert it to the BSD form */ - if (msg->msg_namelen <= sizeof(addr2)) { - memcpy(&addr2[0], msg->msg_name, msg->msg_namelen); - sockaddr2bsd(&addr2[0]); - /* Copy all of msg (except for msg_name) into the new ephemeral local */ - memcpy(&msg2, msg, sizeof(msg2)); - msg2.msg_name = &addr2[0]; + memcpy(&msg2, msg, sizeof(msg2)); + if (!(rc = sockaddr2bsd(msg->msg_name, msg->msg_namelen, &bsd, + &msg2.msg_namelen))) { + msg2.msg_name = &bsd.sa; rc = sys_sendmsg(fd, &msg2, flags); - } else { - rc = einval(); } + } else { + rc = sys_sendmsg(fd, msg, flags); } - /* else do the syscall */ - rc = sys_sendmsg(fd, msg, flags); } else if (__isfdopen(fd)) { if (msg->msg_control) { rc = einval(); /* control msg not supported */ diff --git a/libc/sock/sendto.c b/libc/sock/sendto.c index 3fcfb0023..a90d4c696 100644 --- a/libc/sock/sendto.c +++ b/libc/sock/sendto.c @@ -52,7 +52,8 @@ ssize_t sendto(int fd, const void *buf, size_t size, uint32_t flags, const void *opt_addr, uint32_t addrsize) { ssize_t rc; - char addr2[sizeof(struct sockaddr_un_bsd)]; + uint32_t bsdaddrsize; + union sockaddr_storage_bsd bsd; if (IsAsan() && (!__asan_is_valid(buf, size) || (opt_addr && !__asan_is_valid(opt_addr, addrsize)))) { rc = efault(); @@ -61,12 +62,8 @@ ssize_t sendto(int fd, const void *buf, size_t size, uint32_t flags, if (!IsWindows()) { if (!IsBsd() || !opt_addr) { rc = sys_sendto(fd, buf, size, flags, opt_addr, addrsize); - } else if (addrsize > sizeof(addr2)) { - rc = einval(); - } else { - memcpy(&addr2, opt_addr, addrsize); - sockaddr2bsd(&addr2[0]); - rc = sys_sendto(fd, buf, size, flags, &addr2[0], addrsize); + } else if (!(rc = sockaddr2bsd(opt_addr, addrsize, &bsd, &bsdaddrsize))) { + rc = sys_sendto(fd, buf, size, flags, &bsd, bsdaddrsize); } } else if (__isfdopen(fd)) { if (__isfdkind(fd, kFdSocket)) { diff --git a/libc/sock/sockaddr2bsd.c b/libc/sock/sockaddr2bsd.c new file mode 100644 index 000000000..a6bb2ed53 --- /dev/null +++ b/libc/sock/sockaddr2bsd.c @@ -0,0 +1,66 @@ +/*-*- 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/sock/internal.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/af.h" +#include "libc/sysv/errfuns.h" + +/** + * Converts sockaddr (Linux/Windows) → sockaddr_bsd (XNU/BSD). + */ +int sockaddr2bsd(const void *addr, uint32_t addrsize, + union sockaddr_storage_bsd *out_addr, uint32_t *out_addrsize) { + uint32_t len, famsize; + if (addrsize >= sizeof(((struct sockaddr *)addr)->sa_family)) { + if (((struct sockaddr *)addr)->sa_family == AF_INET) { + if (addrsize >= sizeof(struct sockaddr_in)) { + out_addr->sin.sin_len = 0; + out_addr->sin.sin_family = AF_INET; + out_addr->sin.sin_port = ((struct sockaddr_in *)addr)->sin_port; + out_addr->sin.sin_addr = ((struct sockaddr_in *)addr)->sin_addr; + bzero(&out_addr->sin.sin_zero, sizeof(out_addr->sin.sin_zero)); + *out_addrsize = sizeof(struct sockaddr_in_bsd); + return 0; + } else { + return einval(); + } + } else if (((struct sockaddr *)addr)->sa_family == AF_UNIX) { + famsize = sizeof(((struct sockaddr_un *)addr)->sun_family); + if (addrsize >= famsize && + (len = strnlen(((struct sockaddr_un *)addr)->sun_path, + addrsize - famsize)) < + sizeof(out_addr->sun.sun_path)) { + out_addr->sun.sun_len = 0; + out_addr->sun.sun_family = AF_UNIX; + memcpy(out_addr->sun.sun_path, ((struct sockaddr_un *)addr)->sun_path, + len); + out_addr->sun.sun_path[len] = 0; + *out_addrsize = sizeof(out_addr->sun.sun_len) + + sizeof(out_addr->sun.sun_family) + len; + return 0; + } else { + return einval(); + } + } else { + return epfnosupport(); + } + } else { + return einval(); + } +} diff --git a/libc/sock/sockaddr2linux.c b/libc/sock/sockaddr2linux.c new file mode 100644 index 000000000..244dde623 --- /dev/null +++ b/libc/sock/sockaddr2linux.c @@ -0,0 +1,60 @@ +/*-*- 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/kprintf.h" +#include "libc/macros.internal.h" +#include "libc/sock/internal.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/af.h" +#include "libc/sysv/errfuns.h" + +/** + * Converts sockaddr_bsd (XNU/BSD) → sockaddr (Linux/Windows). + */ +void sockaddr2linux(const union sockaddr_storage_bsd *addr, uint32_t addrsize, + union sockaddr_storage_linux *out_addr, + uint32_t *inout_addrsize) { + uint32_t len, size; + if (out_addr && inout_addrsize) { + size = *inout_addrsize; + bzero(out_addr, size); + if (addrsize >= sizeof(addr->sa.sa_family)) { + if (addr->sa.sa_family == AF_INET) { + if (addrsize >= sizeof(struct sockaddr_in_bsd) && + size >= sizeof(struct sockaddr_in)) { + out_addr->sin.sin_family = AF_INET; + out_addr->sin.sin_port = addr->sin.sin_port; + out_addr->sin.sin_addr = addr->sin.sin_addr; + *inout_addrsize = sizeof(struct sockaddr_in); + } + } else if (addr->sa.sa_family == AF_UNIX) { + if (addrsize >= + sizeof(addr->sun.sun_len) + sizeof(addr->sun.sun_family) && + size >= sizeof(out_addr->sun.sun_family)) { + len = strnlen(((struct sockaddr_un *)addr)->sun_path, + MIN(addrsize - (sizeof(addr->sun.sun_len) + + sizeof(addr->sun.sun_family)), + size - sizeof(out_addr->sun.sun_family))); + out_addr->sun.sun_family = AF_UNIX; + if (len) memcpy(out_addr->sun.sun_path, addr->sun.sun_path, len); + *inout_addrsize = sizeof(out_addr->sun.sun_family) + len + 1; + } + } + } + } +} diff --git a/libc/sysv/calls/__sys_bind.s b/libc/sysv/calls/__sys_bind.s new file mode 100644 index 000000000..089d068ce --- /dev/null +++ b/libc/sysv/calls/__sys_bind.s @@ -0,0 +1,2 @@ +.include "o/libc/sysv/macros.internal.inc" +.scall __sys_bind,0x0680680682068031,globl,hidden diff --git a/libc/sysv/calls/sys_bind.s b/libc/sysv/calls/sys_bind.s deleted file mode 100644 index 7efd512dd..000000000 --- a/libc/sysv/calls/sys_bind.s +++ /dev/null @@ -1,2 +0,0 @@ -.include "o/libc/sysv/macros.internal.inc" -.scall sys_bind,0x0680680682068031,globl,hidden diff --git a/libc/sysv/syscalls.sh b/libc/sysv/syscalls.sh index 798200eca..e3cc90454 100755 --- a/libc/sysv/syscalls.sh +++ b/libc/sysv/syscalls.sh @@ -84,7 +84,7 @@ scall sys_recvfrom 0x01d01d01d201d02d globl hidden scall sys_sendmsg 0x01c01c01c201c02e globl hidden scall sys_recvmsg 0x01b01b01b201b02f globl hidden scall sys_shutdown 0x0860860862086030 globl hidden -scall sys_bind 0x0680680682068031 globl hidden +scall __sys_bind 0x0680680682068031 globl hidden scall sys_listen 0x06a06a06a206a032 globl hidden scall __sys_getsockname 0x0200200202020033 globl hidden scall __sys_getpeername 0x01f01f08d201f034 globl hidden diff --git a/test/libc/sock/unix_test.c b/test/libc/sock/unix_test.c new file mode 100644 index 000000000..491f0da66 --- /dev/null +++ b/test/libc/sock/unix_test.c @@ -0,0 +1,116 @@ +/*-*- 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/struct/timeval.h" +#include "libc/dce.h" +#include "libc/intrin/kprintf.h" +#include "libc/nt/version.h" +#include "libc/runtime/runtime.h" +#include "libc/sock/sock.h" +#include "libc/sysv/consts/af.h" +#include "libc/sysv/consts/so.h" +#include "libc/sysv/consts/sock.h" +#include "libc/sysv/consts/sol.h" +#include "libc/testlib/testlib.h" +#include "libc/time/time.h" + +char testlib_enable_tmp_setup_teardown; + +void DatagramServer(void) { + char buf[256] = {0}; + uint32_t len = sizeof(struct sockaddr_un); + struct sockaddr_un addr = {AF_UNIX, "foo.sock"}; + alarm(3); + ASSERT_SYS(0, 3, socket(AF_UNIX, SOCK_DGRAM, 0)); + ASSERT_SYS(0, 0, bind(3, (void *)&addr, len)); + bzero(&addr, sizeof(addr)); + ASSERT_SYS(0, 0, getsockname(3, (void *)&addr, &len)); + ASSERT_EQ(11, len); + ASSERT_STREQ("foo.sock", addr.sun_path); + ASSERT_SYS(0, 5, read(3, buf, 256)); + EXPECT_STREQ("hello", buf); + ASSERT_SYS(0, 0, close(3)); +} + +TEST(unix, datagram) { + if (IsWindows()) return; // no unix datagram on windows :'( + int ws; + uint32_t len = sizeof(struct sockaddr_un); + struct sockaddr_un addr = {AF_UNIX, "foo.sock"}; + if (!fork()) { + DatagramServer(); + _Exit(0); + } + alarm(3); + while (!fileexists(addr.sun_path)) usleep(10000); + ASSERT_SYS(0, 3, socket(AF_UNIX, SOCK_DGRAM, 0)); + ASSERT_SYS(0, 5, sendto(3, "hello", 5, 0, (void *)&addr, len)); + ASSERT_SYS(0, 0, close(3)); + ASSERT_NE(-1, wait(&ws)); + EXPECT_TRUE(WIFEXITED(ws)); + EXPECT_EQ(0, WEXITSTATUS(ws)); + alarm(0); +} + +void StreamServer(void) { + char buf[256] = {0}; + uint32_t len = sizeof(struct sockaddr_un); + struct sockaddr_un addr = {AF_UNIX, "foo.sock"}; + alarm(3); + ASSERT_SYS(0, 3, socket(AF_UNIX, SOCK_STREAM, 0)); + ASSERT_SYS(0, 0, bind(3, (void *)&addr, len)); + bzero(&addr, sizeof(addr)); + ASSERT_SYS(0, 0, getsockname(3, (void *)&addr, &len)); + ASSERT_EQ(2 + 8 + 1, len); + ASSERT_EQ(AF_UNIX, addr.sun_family); + ASSERT_STREQ("foo.sock", addr.sun_path); + ASSERT_SYS(0, 0, listen(3, 10)); + bzero(&addr, sizeof(addr)); + len = sizeof(addr); + ASSERT_SYS(0, 4, accept(3, &addr, &len)); + ASSERT_EQ(AF_UNIX, addr.sun_family); + EXPECT_STREQ("", addr.sun_path); + ASSERT_SYS(0, 5, read(4, buf, 256)); + EXPECT_STREQ("hello", buf); + ASSERT_SYS(0, 0, close(3)); +} + +TEST(unix, stream) { + if (IsWindows() && !IsAtLeastWindows10()) return; + int ws; + uint32_t len = sizeof(struct sockaddr_un); + struct sockaddr_un addr = {AF_UNIX, "foo.sock"}; + // TODO(jart): move this line down when kFdProcess is gone + ASSERT_SYS(0, 3, socket(AF_UNIX, SOCK_STREAM, 0)); + if (!fork()) { + close(3); + StreamServer(); + _Exit(0); + } + alarm(3); + while (!fileexists(addr.sun_path)) usleep(10000); + ASSERT_SYS(0, 0, connect(3, (void *)&addr, len)); + ASSERT_SYS(0, 5, write(3, "hello", 5)); + ASSERT_SYS(0, 0, close(3)); + ASSERT_NE(-1, wait(&ws)); + EXPECT_TRUE(WIFEXITED(ws)); + EXPECT_EQ(0, WEXITSTATUS(ws)); + alarm(0); +} diff --git a/third_party/lua/lunix.c b/third_party/lua/lunix.c index 999551dde..497b8c286 100644 --- a/third_party/lua/lunix.c +++ b/third_party/lua/lunix.c @@ -101,6 +101,12 @@ struct UnixErrno { const char *call; }; +union SockAddr { + struct sockaddr s; + struct sockaddr_in i; + struct sockaddr_un u; +}; + static lua_State *GL; static void *LuaRealloc(lua_State *L, void *p, size_t n) { @@ -219,6 +225,42 @@ static int SysretInteger(lua_State *L, const char *call, int olderr, } } +static int MakeSockaddr(lua_State *L, int i, union SockAddr *sa, + uint32_t *salen) { + bzero(sa, sizeof(*sa)); + if (lua_isstring(L, i)) { + sa->u.sun_family = AF_UNIX; + if (!memccpy(sa->u.sun_path, luaL_checkstring(L, i), 0, + sizeof(sa->u.sun_path))) { + luaL_error(L, "unix path too long"); + unreachable; + } + *salen = sizeof(struct sockaddr_un); + return i + 1; + } else { + sa->i.sin_family = AF_INET; + sa->i.sin_addr.s_addr = htonl(luaL_optinteger(L, i, 0)); + sa->i.sin_port = htons(luaL_optinteger(L, i + 1, 0)); + *salen = sizeof(struct sockaddr_in); + return i + 2; + } +} + +static int PushSockaddr(lua_State *L, const struct sockaddr_storage *ss) { + if (ss->ss_family == AF_INET) { + lua_pushinteger(L, + ntohl(((const struct sockaddr_in *)ss)->sin_addr.s_addr)); + lua_pushinteger(L, ntohs(((const struct sockaddr_in *)ss)->sin_port)); + return 2; + } else if (ss->ss_family == AF_UNIX) { + lua_pushstring(L, ((const struct sockaddr_un *)ss)->sun_path); + return 1; + } else { + luaL_error(L, "bad family"); + unreachable; + } +} + static void CheckOptvalsize(lua_State *L, uint32_t want, uint32_t got) { if (!IsTiny()) { if (want == got) return; @@ -1095,6 +1137,7 @@ static int LuaUnixSetsockopt(lua_State *L) { // ├─→ true // └─→ nil, unix.Errno tv.tv_sec = luaL_checkinteger(L, 4); + tv.tv_usec = luaL_optinteger(L, 5, 0) / 1000; optval = &tv; optsize = sizeof(tv); } else if (level == SOL_SOCKET && optname == SO_LINGER) { @@ -1164,10 +1207,11 @@ static int LuaUnixGetsockopt(lua_State *L) { // └─→ nil, unix.Errno static int LuaUnixSocket(lua_State *L) { int olderr = errno; + int family = luaL_optinteger(L, 1, AF_INET); return SysretInteger( L, "socket", olderr, - socket(luaL_optinteger(L, 1, AF_INET), luaL_optinteger(L, 2, SOCK_STREAM), - luaL_optinteger(L, 3, IPPROTO_TCP))); + socket(family, luaL_optinteger(L, 2, SOCK_STREAM), + luaL_optinteger(L, 3, family == AF_INET ? IPPROTO_TCP : 0))); } // unix.socketpair([family:int[, type:int[, protocol:int]]]) @@ -1187,33 +1231,29 @@ static int LuaUnixSocketpair(lua_State *L) { } // unix.bind(fd:int[, ip:uint32, port:uint16]) +// unix.bind(fd:int[, unixpath:str]) // ├─→ true // └─→ nil, unix.Errno static int LuaUnixBind(lua_State *L) { + uint32_t salen; + union SockAddr sa; int olderr = errno; + MakeSockaddr(L, 2, &sa, &salen); return SysretBool(L, "bind", olderr, - bind(luaL_checkinteger(L, 1), - &(struct sockaddr_in){ - .sin_family = AF_INET, - .sin_addr.s_addr = htonl(luaL_optinteger(L, 2, 0)), - .sin_port = htons(luaL_optinteger(L, 3, 0)), - }, - sizeof(struct sockaddr_in))); + bind(luaL_checkinteger(L, 1), &sa.s, salen)); } // unix.connect(fd:int, ip:uint32, port:uint16) +// unix.connect(fd:int, unixpath:str) // ├─→ true // └─→ nil, unix.Errno static int LuaUnixConnect(lua_State *L) { + uint32_t salen; + union SockAddr sa; int olderr = errno; - return SysretBool( - L, "connect", olderr, - connect(luaL_checkinteger(L, 1), - &(struct sockaddr_in){ - .sin_addr.s_addr = htonl(luaL_checkinteger(L, 2)), - .sin_port = htons(luaL_checkinteger(L, 3)), - }, - sizeof(struct sockaddr_in))); + MakeSockaddr(L, 2, &sa, &salen); + return SysretBool(L, "connect", olderr, + connect(luaL_checkinteger(L, 1), &sa.s, salen)); } // unix.listen(fd:int[, backlog:int]) @@ -1225,42 +1265,34 @@ static int LuaUnixListen(lua_State *L) { listen(luaL_checkinteger(L, 1), luaL_optinteger(L, 2, 10))); } +static int LuaUnixGetname(lua_State *L, const char *name, + int func(int, void *, uint32_t *)) { + int olderr; + uint32_t addrsize; + struct sockaddr_storage ss = {0}; + olderr = errno; + addrsize = sizeof(ss) - 1; + if (!func(luaL_checkinteger(L, 1), &ss, &addrsize)) { + return PushSockaddr(L, &ss); + } else { + return SysretErrno(L, name, olderr); + } +} + // unix.getsockname(fd:int) // ├─→ ip:uint32, port:uint16 +// ├─→ unixpath:str // └─→ nil, unix.Errno static int LuaUnixGetsockname(lua_State *L) { - int fd, olderr; - uint32_t addrsize; - struct sockaddr_in sa; - olderr = errno; - 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 { - return SysretErrno(L, "getsockname", olderr); - } + return LuaUnixGetname(L, "getsockname", getsockname); } // unix.getpeername(fd:int) // ├─→ ip:uint32, port:uint16 +// ├─→ unixpath:str // └─→ nil, unix.Errno static int LuaUnixGetpeername(lua_State *L) { - int fd, olderr; - uint32_t addrsize; - struct sockaddr_in sa; - olderr = errno; - 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 { - return SysretErrno(L, "getpeername", olderr); - } + return LuaUnixGetname(L, "getpeername", getpeername); } // unix.siocgifconf() @@ -1338,21 +1370,20 @@ static int LuaUnixGethostname(lua_State *L) { // unix.accept(serverfd:int[, flags:int]) // ├─→ clientfd:int, ip:uint32, port:uint16 +// ├─→ clientfd:int, unixpath:str // └─→ nil, unix.Errno static int LuaUnixAccept(lua_State *L) { uint32_t addrsize; - struct sockaddr_in sa; + struct sockaddr_storage ss; int clientfd, serverfd, olderr, flags; olderr = errno; - addrsize = sizeof(sa); + addrsize = sizeof(ss); serverfd = luaL_checkinteger(L, 1); flags = luaL_optinteger(L, 2, 0); - clientfd = accept4(serverfd, &sa, &addrsize, flags); + clientfd = accept4(serverfd, &ss, &addrsize, flags); 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; + return 1 + PushSockaddr(L, &ss); } else { return SysretErrno(L, "accept", olderr); } @@ -1403,6 +1434,7 @@ static int LuaUnixPoll(lua_State *L) { // unix.recvfrom(fd:int[, bufsiz:int[, flags:int]]) // ├─→ data:str, ip:uint32, port:uint16 +// ├─→ data:str, unixpath:str // └─→ nil, unix.Errno static int LuaUnixRecvfrom(lua_State *L) { char *buf; @@ -1410,22 +1442,20 @@ static int LuaUnixRecvfrom(lua_State *L) { ssize_t rc; uint32_t addrsize; lua_Integer bufsiz; - struct sockaddr_in sa; - int fd, flags, olderr = errno; - addrsize = sizeof(sa); + struct sockaddr_storage ss; + int fd, flags, pushed, olderr = errno; + addrsize = sizeof(ss); fd = luaL_checkinteger(L, 1); bufsiz = luaL_optinteger(L, 2, 1500); bufsiz = MIN(bufsiz, 0x7ffff000); flags = luaL_optinteger(L, 3, 0); buf = LuaAllocOrDie(L, bufsiz); - rc = recvfrom(fd, buf, bufsiz, flags, &sa, &addrsize); - if (rc != -1) { + if ((rc = recvfrom(fd, buf, bufsiz, flags, &ss, &addrsize)) != -1) { got = rc; lua_pushlstring(L, buf, got); - lua_pushinteger(L, ntohl(sa.sin_addr.s_addr)); - lua_pushinteger(L, ntohs(sa.sin_port)); + pushed = 1 + PushSockaddr(L, &ss); free(buf); - return 3; + return pushed; } else { free(buf); return SysretErrno(L, "recvfrom", olderr); @@ -1473,21 +1503,21 @@ static int LuaUnixSend(lua_State *L) { } // unix.sendto(fd:int, data:str, ip:uint32, port:uint16[, flags:int]) +// unix.sendto(fd:int, data:str, unixpath:str[, flags:int]) // ├─→ sent:int // └─→ nil, unix.Errno static int LuaUnixSendto(lua_State *L) { char *data; - size_t sent, size; - struct sockaddr_in sa; - int fd, flags, olderr = errno; + size_t size; + uint32_t salen; + union SockAddr sa; + int i, fd, flags, olderr = errno; fd = luaL_checkinteger(L, 1); data = luaL_checklstring(L, 2, &size); - bzero(&sa, sizeof(sa)); - sa.sin_addr.s_addr = htonl(luaL_checkinteger(L, 3)); - sa.sin_port = htons(luaL_checkinteger(L, 4)); - flags = luaL_optinteger(L, 5, 0); + i = MakeSockaddr(L, 3, &sa, &salen); + flags = luaL_optinteger(L, i, 0); return SysretInteger(L, "sendto", olderr, - sendto(fd, data, size, flags, &sa, sizeof(sa))); + sendto(fd, data, size, flags, &sa.s, salen)); } // unix.shutdown(fd:int, how:int) diff --git a/tool/net/demo/unix-unix.lua b/tool/net/demo/unix-unix.lua new file mode 100644 index 000000000..c96dc7349 --- /dev/null +++ b/tool/net/demo/unix-unix.lua @@ -0,0 +1,89 @@ +-- UNIX Domain Sockets Example + +-- So we can detect that child died. +died = false +function OnSigchld(sig) + died = true + unix.wait() -- Prevent it from becoming a zombie +end +assert(unix.sigaction(unix.SIGCHLD, OnSigchld)) + +-- So keyboard interurpts only go to subprocess. +oldint = assert(unix.sigaction(unix.SIGINT, unix.SIG_IGN)) +oldquit = assert(unix.sigaction(unix.SIGQUIT, unix.SIG_IGN)) + +-- UNIX domain sockets need a file +tmpdir = os.getenv('TMPDIR') or '/tmp' +unixpath = '%s/redbean-unix.sock.%d' % {tmpdir, unix.getpid()} + +-- Create child process which is the server. +child = assert(unix.fork()) +if child == 0 then + server = assert(unix.socket(unix.AF_UNIX, unix.SOCK_STREAM)) + assert(unix.setsockopt(server, unix.SOL_SOCKET, unix.SO_RCVTIMEO, 2)) + assert(unix.bind(server, unixpath)) + assert(unix.listen(server)) + client = assert(unix.accept(server)) + data = assert(unix.read(client)) + assert(data == 'ping!') + assert(assert(unix.write(client, 'pong!')) == 5) + assert(unix.close(client)) + unix.exit(0) +end + +-- Wait for the child to create the the socket file. +function FileExists(path) + st, err = unix.stat(path) + return not err +end +expobackoff = 1 +while not died do + if FileExists(unixpath) then + break + else + expobackoff = expobackoff << 1 + unix.nanosleep(expobackoff // 1000000000, + expobackoff % 1000000000) + end +end + +-- Now connect to the socket. +if not died then + client = assert(unix.socket(unix.AF_UNIX, unix.SOCK_STREAM)) + assert(unix.connect(client, unixpath)) + assert(assert(unix.write(client, 'ping!')) == 5) + data = assert(unix.read(client)) + assert(data == 'pong!') + itworked = true +else + itworked = false +end + +-- Wait for client to terminate. We don't check error here because if +-- the child already died and the signal handler reaped it, then this +-- returns a ECHILD error which is fine. +unix.wait() +assert(itworked) + +-- Now clean up the socket file. +unix.unlink(unixpath) + +SetStatus(200) +SetHeader('Connection', 'close') -- be lazy and let _Exit() clean up signal handlers +SetHeader('Content-Type', 'text/html; charset=utf-8') +Write('\r\n') +Write('redbean unix domain sockets example\r\n') +Write('

\r\n') +Write('\r\n') +Write('redbean unix domain sockets example\r\n') +Write('

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

+ It worked! We successfully sent a ping + pong via UNIX local sockets. Please check out the source + code to this example inside your redbean at unix-unix.lua. +

+]]) +Write('\r\n') diff --git a/tool/net/help.txt b/tool/net/help.txt index f01f7894f..c6941cf31 100644 --- a/tool/net/help.txt +++ b/tool/net/help.txt @@ -2546,8 +2546,10 @@ UNIX MODULE `family` defaults to `AF_INET` and can be: - - `AF_UNIX` - - `AF_INET` + - `AF_INET`: Creates Internet Protocol Version 4 (IPv4) socket. + + - `AF_UNIX`: Creates local UNIX domain socket. On the New Technology + this requires Windows 10 and only works with `SOCK_STREAM`. `type` defaults to `SOCK_STREAM` and can be: @@ -2562,7 +2564,8 @@ UNIX MODULE - `SOCK_CLOEXEC` - `SOCK_NONBLOCK` - `protocol` defaults to `IPPROTO_TCP` and can be: + `protocol` defaults to `IPPROTO_TCP` for AF_INET` and `0` for + `AF_UNIX`. It can also be: - `IPPROTO_IP` - `IPPROTO_ICMP` @@ -2592,6 +2595,7 @@ UNIX MODULE `protocol` defaults to `0`. unix.bind(fd:int[, ip:uint32, port:uint16]) + unix.bind(fd:int[, unixpath:str]) ├─→ true └─→ nil, unix.Errno @@ -2743,6 +2747,7 @@ UNIX MODULE unix.accept(serverfd:int[, flags:int]) ├─→ clientfd:int, ip:uint32, port:uint16 + ├─→ clientfd:int, unixpath:str └─→ nil, unix.Errno Accepts new client socket descriptor for a listening tcp socket. @@ -2753,6 +2758,7 @@ UNIX MODULE - `SOCK_NONBLOCK` unix.connect(fd:int, ip:uint32, port:uint16) + unix.connect(fd:int, unixpath:str) ├─→ true └─→ nil, unix.Errno @@ -2764,16 +2770,21 @@ UNIX MODULE unix.getsockname(fd:int) ├─→ ip:uint32, port:uint16 + ├─→ unixpath:str └─→ nil, unix.Errno Retrieves the local address of a socket. unix.getpeername(fd:int) ├─→ ip:uint32, port:uint16 + ├─→ unixpath:str └─→ nil, unix.Errno Retrieves the remote address of a socket. + This operation will either fail on `AF_UNIX` sockets or return an + empty string. + unix.recv(fd:int[, bufsiz:int[, flags:int]]) ├─→ data:str └─→ nil, unix.Errno @@ -2787,6 +2798,7 @@ UNIX MODULE unix.recvfrom(fd:int[, bufsiz:int[, flags:int]]) ├─→ data:str, ip:uint32, port:uint16 + ├─→ data:str, unixpath:str └─→ nil, unix.Errno `flags` may have any combination (using bitwise OR) of: @@ -2810,6 +2822,7 @@ UNIX MODULE - `MSG_NOSIGNAL` unix.sendto(fd:int, data:str, ip:uint32, port:uint16[, flags:int]) + unix.sendto(fd:int, data:str, unixpath:str[, flags:int]) ├─→ sent:int └─→ nil, unix.Errno diff --git a/tool/net/net.mk b/tool/net/net.mk index e6460106e..0c60129ca 100644 --- a/tool/net/net.mk +++ b/tool/net/net.mk @@ -167,6 +167,7 @@ o/tinylinux/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-unix.lua.zip.o \ o/$(MODE)/tool/net/demo/unix-rawsocket.lua.zip.o \ o/$(MODE)/tool/net/demo/unix-subprocess.lua.zip.o \ o/$(MODE)/tool/net/demo/unix-webserver.lua.zip.o \ @@ -215,6 +216,7 @@ o/$(MODE)/tool/net/redbean-demo.com.dbg: \ 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-unix.lua.zip.o \ o/$(MODE)/tool/net/demo/unix-rawsocket.lua.zip.o \ o/$(MODE)/tool/net/demo/unix-subprocess.lua.zip.o \ o/$(MODE)/tool/net/demo/unix-webserver.lua.zip.o \ From 0e773e23ad97a8bfc7f19b39f3156ca329e17724 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Wed, 22 Jun 2022 04:35:43 -0700 Subject: [PATCH 2/4] Add special thanks section to readme --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index f34bdac42..84ebfe6eb 100644 --- a/README.md +++ b/README.md @@ -226,3 +226,15 @@ gdb foo.com -ex 'add-symbol-file foo.com.dbg 0x401000' | FreeBSD | 13 | 2020 | | OpenBSD | 6.4 | 2018 | | NetBSD | 9.2 | 2021 | + +## Special Thanks + +Funding for this project is crowdsourced using +[GitHub Sponsors](https://github.com/sponsors/jart) and +[Patreon](https://www.patreon.com/jart). Your support is what makes this +project possible. Thank you! We'd also like to give special thanks to +the following individuals: + +- [Joe Drumgoole](https://github.com/jdrumgoole) + +For publicly sponsoring our work at the highest tier. From 9545062175e14dcb471c33af98783392051cc7e0 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Wed, 22 Jun 2022 06:37:45 -0700 Subject: [PATCH 3/4] Add pthread tls api --- libc/fmt/__xpg_strerror_r.c | 23 ++++++++++ libc/intrin/pthread.h | 8 ++++ libc/str/str.h | 1 + libc/thread/create.c | 2 + libc/thread/ctor.S | 27 ++++++++++++ libc/thread/init.c | 8 +--- libc/thread/internal.h | 14 ++++++ libc/thread/key.c | 22 ++++++++++ libc/thread/pthread_getspecific.c | 34 +++++++++++++++ libc/thread/pthread_key_create.c | 51 ++++++++++++++++++++++ libc/thread/pthread_key_delete.c | 34 +++++++++++++++ libc/thread/pthread_key_destruct.c | 41 +++++++++++++++++ libc/thread/pthread_setspecific.c | 35 +++++++++++++++ libc/thread/thread.h | 2 + test/libc/thread/pthread_key_create_test.c | 38 ++++++++++++++++ 15 files changed, 333 insertions(+), 7 deletions(-) create mode 100644 libc/fmt/__xpg_strerror_r.c create mode 100644 libc/thread/ctor.S create mode 100644 libc/thread/internal.h create mode 100644 libc/thread/key.c create mode 100644 libc/thread/pthread_getspecific.c create mode 100644 libc/thread/pthread_key_create.c create mode 100644 libc/thread/pthread_key_delete.c create mode 100644 libc/thread/pthread_key_destruct.c create mode 100644 libc/thread/pthread_setspecific.c create mode 100644 test/libc/thread/pthread_key_create_test.c diff --git a/libc/fmt/__xpg_strerror_r.c b/libc/fmt/__xpg_strerror_r.c new file mode 100644 index 000000000..888619bc5 --- /dev/null +++ b/libc/fmt/__xpg_strerror_r.c @@ -0,0 +1,23 @@ +/*-*- 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/fmt/fmt.h" + +int __xpg_strerror_r(int a, char *b, size_t c) { + return strerror_r(a, b, c); +} diff --git a/libc/intrin/pthread.h b/libc/intrin/pthread.h index 258564574..487b214a1 100644 --- a/libc/intrin/pthread.h +++ b/libc/intrin/pthread.h @@ -4,6 +4,8 @@ #include "libc/calls/struct/timespec.h" #include "libc/dce.h" +#define PTHREAD_KEYS_MAX 64 + #define PTHREAD_ONCE_INIT 0 #define PTHREAD_MUTEX_DEFAULT PTHREAD_MUTEX_NORMAL @@ -24,6 +26,8 @@ COSMOPOLITAN_C_START_ typedef unsigned long *pthread_t; typedef int pthread_once_t; +typedef unsigned pthread_key_t; +typedef void (*pthread_key_dtor)(void *); typedef struct { int attr; @@ -105,6 +109,10 @@ int pthread_rwlock_wrlock(pthread_rwlock_t *); int pthread_rwlock_trywrlock(pthread_rwlock_t *); int pthread_rwlock_timedwrlock(pthread_rwlock_t *, const struct timespec *); int pthread_rwlock_unlock(pthread_rwlock_t *); +int pthread_key_create(pthread_key_t *, pthread_key_dtor); +int pthread_key_delete(pthread_key_t); +int pthread_setspecific(pthread_key_t, void *); +void *pthread_getspecific(pthread_key_t); #define pthread_mutexattr_init(pAttr) ((pAttr)->attr = PTHREAD_MUTEX_DEFAULT, 0) #define pthread_mutexattr_destroy(pAttr) ((pAttr)->attr = 0) diff --git a/libc/str/str.h b/libc/str/str.h index fbf461e9a..d91a19ef0 100644 --- a/libc/str/str.h +++ b/libc/str/str.h @@ -266,6 +266,7 @@ char *strerrno(int) nosideeffect libcesque; char *strerdoc(int) nosideeffect libcesque; int strerror_r(int, char *, size_t) dontthrow nocallback; int strerror_wr(int, uint32_t, char *, size_t) dontthrow nocallback; +int __xpg_strerror_r(int, char *, size_t) dontthrow nocallback; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/thread/create.c b/libc/thread/create.c index 7e1518f84..00513dc35 100644 --- a/libc/thread/create.c +++ b/libc/thread/create.c @@ -22,6 +22,7 @@ #include "libc/errno.h" #include "libc/intrin/setjmp.internal.h" #include "libc/macros.internal.h" +#include "libc/nexgen32e/threaded.h" #include "libc/runtime/internal.h" #include "libc/runtime/runtime.h" #include "libc/str/str.h" @@ -101,6 +102,7 @@ int cthread_create(cthread_t *ptd, const cthread_attr_t *attr, int rc, tid; cthread_t td; cthread_attr_t default_attr; + __threaded = true; cthread_zombies_reap(); cthread_attr_init(&default_attr); if ((td = cthread_allocate(attr ? attr : &default_attr))) { diff --git a/libc/thread/ctor.S b/libc/thread/ctor.S new file mode 100644 index 000000000..d8fddefc8 --- /dev/null +++ b/libc/thread/ctor.S @@ -0,0 +1,27 @@ +/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ +│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ 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/macros.internal.h" + + .init.start 400,_main_thread_ctor + push %rdi + push %rsi + call _main_thread_init + pop %rsi + pop %rdi + .init.end 400,_main_thread_ctor diff --git a/libc/thread/init.c b/libc/thread/init.c index 51262c110..c4da0ceff 100644 --- a/libc/thread/init.c +++ b/libc/thread/init.c @@ -29,7 +29,7 @@ #include "libc/sysv/consts/prot.h" #include "libc/thread/thread.h" -static textstartup void _main_thread_init(void) { +textstartup void _main_thread_init(void) { _Static_assert(offsetof(struct cthread_descriptor_t, self) == 0x00, ""); _Static_assert(offsetof(struct cthread_descriptor_t, self2) == 0x30, ""); _Static_assert(offsetof(struct cthread_descriptor_t, tid) == 0x38, ""); @@ -65,10 +65,4 @@ static textstartup void _main_thread_init(void) { // Set FS __install_tls((char *)td); - assert(cthread_self()->tid == gettid()); - __threaded = true; } - -const void *const _main_thread_ctor[] initarray = { - _main_thread_init, -}; diff --git a/libc/thread/internal.h b/libc/thread/internal.h new file mode 100644 index 000000000..346c11399 --- /dev/null +++ b/libc/thread/internal.h @@ -0,0 +1,14 @@ +#ifndef COSMOPOLITAN_LIBC_THREAD_INTERNAL_H_ +#define COSMOPOLITAN_LIBC_THREAD_INTERNAL_H_ +#include "libc/intrin/pthread.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +extern uint64_t _pthread_key_usage[(PTHREAD_KEYS_MAX + 63) / 64]; +extern pthread_key_dtor _pthread_key_dtor[PTHREAD_KEYS_MAX]; + +void _pthread_key_destruct(void *[PTHREAD_KEYS_MAX]); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_THREAD_INTERNAL_H_ */ diff --git a/libc/thread/key.c b/libc/thread/key.c new file mode 100644 index 000000000..954369123 --- /dev/null +++ b/libc/thread/key.c @@ -0,0 +1,22 @@ +/*-*- 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/thread/internal.h" + +uint64_t _pthread_key_usage[(PTHREAD_KEYS_MAX + 63) / 64]; +pthread_key_dtor _pthread_key_dtor[PTHREAD_KEYS_MAX]; diff --git a/libc/thread/pthread_getspecific.c b/libc/thread/pthread_getspecific.c new file mode 100644 index 000000000..4ee71c174 --- /dev/null +++ b/libc/thread/pthread_getspecific.c @@ -0,0 +1,34 @@ +/*-*- 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/errno.h" +#include "libc/nexgen32e/gettls.h" +#include "libc/thread/thread.h" + +STATIC_YOINK("_main_thread_ctor"); + +/** + * Gets value of TLS slot for current thread. + */ +void *pthread_getspecific(pthread_key_t key) { + if (key < PTHREAD_KEYS_MAX) { + return ((cthread_t)__get_tls_inline())->key[key]; + } else { + return 0; + } +} diff --git a/libc/thread/pthread_key_create.c b/libc/thread/pthread_key_create.c new file mode 100644 index 000000000..88981bb97 --- /dev/null +++ b/libc/thread/pthread_key_create.c @@ -0,0 +1,51 @@ +/*-*- 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/errno.h" +#include "libc/nexgen32e/bsr.h" +#include "libc/nexgen32e/gettls.h" +#include "libc/runtime/runtime.h" +#include "libc/thread/internal.h" +#include "libc/thread/thread.h" + +STATIC_YOINK("_main_thread_ctor"); + +/** + * Allocates TLS slot. + */ +int pthread_key_create(pthread_key_t *key, pthread_key_dtor dtor) { + int i, j; + for (i = 0; i < (PTHREAD_KEYS_MAX + 63) / 64; ++i) { + if (~_pthread_key_usage[i]) { + j = bsrl(~_pthread_key_usage[i]); + _pthread_key_usage[i] |= 1ul << j; + _pthread_key_dtor[i * 64 + j] = dtor; + *key = i * 64 + j; + return 0; + } + } + return EAGAIN; +} + +static textexit void _pthread_key_atexit(void) { + _pthread_key_destruct(((cthread_t)__get_tls())->key); +} + +__attribute__((__constructor__)) static textstartup void _pthread_key_init() { + atexit(_pthread_key_atexit); +} diff --git a/libc/thread/pthread_key_delete.c b/libc/thread/pthread_key_delete.c new file mode 100644 index 000000000..998e78ac0 --- /dev/null +++ b/libc/thread/pthread_key_delete.c @@ -0,0 +1,34 @@ +/*-*- 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/errno.h" +#include "libc/thread/internal.h" +#include "libc/thread/thread.h" + +/** + * Deletes TLS slot. + */ +int pthread_key_delete(pthread_key_t key) { + if (key < PTHREAD_KEYS_MAX) { + _pthread_key_usage[key / 64] &= ~(1ul << (key % 64)); + _pthread_key_dtor[key] = 0; + return 0; + } else { + return EINVAL; + } +} diff --git a/libc/thread/pthread_key_destruct.c b/libc/thread/pthread_key_destruct.c new file mode 100644 index 000000000..3c5a913fe --- /dev/null +++ b/libc/thread/pthread_key_destruct.c @@ -0,0 +1,41 @@ +/*-*- 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/nexgen32e/bsr.h" +#include "libc/thread/internal.h" +#include "libc/thread/thread.h" + +void _pthread_key_destruct(void *key[PTHREAD_KEYS_MAX]) { + int i, j; + uint64_t x; + void *value; + pthread_key_dtor dtor; +StartOver: + for (i = 0; i < (PTHREAD_KEYS_MAX + 63) / 64; ++i) { + x = _pthread_key_usage[i]; + while (x) { + j = bsrl(x); + if ((dtor = _pthread_key_dtor[i * 64 + j]) && (value = key[i * 64 + j])) { + key[i * 64 + j] = 0; + dtor(value); + goto StartOver; + } + x &= ~(1ul << j); + } + } +} diff --git a/libc/thread/pthread_setspecific.c b/libc/thread/pthread_setspecific.c new file mode 100644 index 000000000..da8a77409 --- /dev/null +++ b/libc/thread/pthread_setspecific.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/errno.h" +#include "libc/nexgen32e/gettls.h" +#include "libc/thread/thread.h" + +STATIC_YOINK("_main_thread_ctor"); + +/** + * Sets value of TLS slot for current thread. + */ +int pthread_setspecific(pthread_key_t key, void *val) { + if (key < PTHREAD_KEYS_MAX) { + ((cthread_t)__get_tls_inline())->key[key] = val; + return 0; + } else { + return EINVAL; + } +} diff --git a/libc/thread/thread.h b/libc/thread/thread.h index 2fbd9a449..757204a57 100644 --- a/libc/thread/thread.h +++ b/libc/thread/thread.h @@ -1,6 +1,7 @@ #ifndef COSMOPOLITAN_LIBC_THREAD_THREAD_H_ #define COSMOPOLITAN_LIBC_THREAD_THREAD_H_ #include "libc/calls/struct/timespec.h" +#include "libc/intrin/pthread.h" #include "libc/runtime/runtime.h" #define CTHREAD_CREATE_DETACHED 1 @@ -33,6 +34,7 @@ struct cthread_descriptor_t { char *top, *bottom; } stack, alloc; jmp_buf exiter; + void *key[PTHREAD_KEYS_MAX]; }; typedef struct cthread_descriptor_t *cthread_t; diff --git a/test/libc/thread/pthread_key_create_test.c b/test/libc/thread/pthread_key_create_test.c new file mode 100644 index 000000000..ced6cf725 --- /dev/null +++ b/test/libc/thread/pthread_key_create_test.c @@ -0,0 +1,38 @@ +/*-*- 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/mem/mem.h" +#include "libc/testlib/testlib.h" +#include "libc/thread/thread.h" + +TEST(pthread_key_create, testRunsDtors_becauseNoLeakReport) { + void *x; + pthread_key_t key; + x = malloc(123); + EXPECT_EQ(0, pthread_key_create(&key, free)); + EXPECT_EQ(0, pthread_setspecific(key, x)); + EXPECT_EQ(x, pthread_getspecific(key)); + x = malloc(123); + EXPECT_EQ(0, pthread_key_create(&key, free)); + EXPECT_EQ(0, pthread_setspecific(key, x)); + EXPECT_EQ(x, pthread_getspecific(key)); + x = malloc(123); + EXPECT_EQ(0, pthread_key_create(&key, free)); + EXPECT_EQ(0, pthread_setspecific(key, x)); + EXPECT_EQ(x, pthread_getspecific(key)); +} From 1235ad6006737e143d5c200b62ba66891fd3b7ec Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Wed, 22 Jun 2022 07:11:08 -0700 Subject: [PATCH 4/4] Add temporary stubs for libunwind --- test/libc/runtime/unwind.c | 37 +++++++++++++++++++++++++++++++++ test/libc/runtime/unwind.h | 42 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 test/libc/runtime/unwind.c create mode 100644 test/libc/runtime/unwind.h diff --git a/test/libc/runtime/unwind.c b/test/libc/runtime/unwind.c new file mode 100644 index 000000000..ebd6164bc --- /dev/null +++ b/test/libc/runtime/unwind.c @@ -0,0 +1,37 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 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 "test/libc/runtime/unwind.h" + +// temporary stubs for libunwind + +uintptr_t _Unwind_GetCFA(struct _Unwind_Context *ctx) { + return 0; +} + +uintptr_t _Unwind_GetIP(struct _Unwind_Context *ctx) { + return 0; +} + +_Unwind_Reason_Code _Unwind_Backtrace(_Unwind_Trace_Fn fn, void *arg) { + return _URC_NORMAL_STOP; +} + +void *_Unwind_FindEnclosingFunction(void *arg) { + return NULL; +} diff --git a/test/libc/runtime/unwind.h b/test/libc/runtime/unwind.h new file mode 100644 index 000000000..d21568f79 --- /dev/null +++ b/test/libc/runtime/unwind.h @@ -0,0 +1,42 @@ +#ifndef COSMOPOLITAN_TEST_LIBC_RUNTIME_UNWIND_H_ +#define COSMOPOLITAN_TEST_LIBC_RUNTIME_UNWIND_H_ + +#define UNW_TDEP_CURSOR_LEN 127 + +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +typedef enum { + _URC_NO_REASON = 0, + _URC_FOREIGN_EXCEPTION_CAUGHT = 1, + _URC_FATAL_PHASE2_ERROR = 2, + _URC_FATAL_PHASE1_ERROR = 3, + _URC_NORMAL_STOP = 4, + _URC_END_OF_STACK = 5, + _URC_HANDLER_FOUND = 6, + _URC_INSTALL_CONTEXT = 7, + _URC_CONTINUE_UNWIND = 8 +} _Unwind_Reason_Code; + +typedef uint64_t unw_word_t; + +typedef struct unw_cursor { + unw_word_t opaque[UNW_TDEP_CURSOR_LEN]; +} unw_cursor_t; + +struct _Unwind_Context { + unw_cursor_t cursor; + int end_of_stack; +}; + +typedef _Unwind_Reason_Code (*_Unwind_Trace_Fn)(struct _Unwind_Context *, + void *); + +uintptr_t _Unwind_GetCFA(struct _Unwind_Context *); +uintptr_t _Unwind_GetIP(struct _Unwind_Context *); +_Unwind_Reason_Code _Unwind_Backtrace(_Unwind_Trace_Fn, void *); +void *_Unwind_FindEnclosingFunction(void *); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_TEST_LIBC_RUNTIME_UNWIND_H_ */