Merge branch 'jart:master' into issues-431-gh-actions

This commit is contained in:
Theta Nil 2022-06-22 12:25:59 -04:00 committed by GitHub
commit 7af7ca23aa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
43 changed files with 1018 additions and 179 deletions

View file

@ -227,3 +227,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.

View file

@ -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%

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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)

View file

@ -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;
}

View file

@ -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;
}

31
libc/sock/bind-sysv.c Normal file
View file

@ -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;
}
}

View file

@ -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 {

View file

@ -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;
}
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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) */

View file

@ -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,

View file

@ -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)) {

View file

@ -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 */

View file

@ -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)) {

66
libc/sock/sockaddr2bsd.c Normal file
View file

@ -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();
}
}

View file

@ -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;
}
}
}
}
}

View file

@ -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) */

View file

@ -0,0 +1,2 @@
.include "o/libc/sysv/macros.internal.inc"
.scall __sys_bind,0x0680680682068031,globl,hidden

View file

@ -1,2 +0,0 @@
.include "o/libc/sysv/macros.internal.inc"
.scall sys_bind,0x0680680682068031,globl,hidden

View file

@ -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

View file

@ -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))) {

27
libc/thread/ctor.S Normal file
View file

@ -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

View file

@ -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,
};

14
libc/thread/internal.h Normal file
View file

@ -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_ */

22
libc/thread/key.c Normal file
View file

@ -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];

View file

@ -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;
}
}

View file

@ -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);
}

View file

@ -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;
}
}

View file

@ -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);
}
}
}

View file

@ -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;
}
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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_ */

116
test/libc/sock/unix_test.c Normal file
View file

@ -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);
}

View file

@ -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));
}

View file

@ -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)

View file

@ -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('<!doctype html>\r\n')
Write('<title>redbean unix domain sockets example</title>\r\n')
Write('<h1>\r\n')
Write('<img style="vertical-align:middle" src="data:image/png;base64,\r\n')
Write(EncodeBase64(LoadAsset('/redbean.png')))
Write('">\r\n')
Write('redbean unix domain sockets example\r\n')
Write('</h1>\r\n')
Write([[
<p>
<strong>It worked!</strong> 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.
</p>
]])
Write('</h1>\r\n')

View file

@ -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

View file

@ -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 \