From 7be9facc5732313f55c3a9942f1f133c1dc41fa0 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Mon, 12 Jul 2021 15:08:46 +0200 Subject: [PATCH] feat: Implement the `wasi_experimental_network_unstable` namespace. This is port of what I've done inside wasmer/wasix. Just an experiment. --- examples/c/example-epoll-tcp-server.c | 242 ++++++++++++++++++ examples/c/example-tcp-server.c | 79 ++++++ .../wasm-wasi-musl/__header_sys_epoll.h | 6 + .../wasm-wasi-musl/__header_sys_socket.h | 5 + lib/libc/include/wasm-wasi-musl/sys/epoll.h | 80 ++++++ lib/libc/include/wasm-wasi-musl/sys/socket.h | 9 +- .../wasi/wasi_experimental_network.h | 146 +++++++++++ .../cloudlibc/src/common/errno.h | 6 - .../src/libc/sys/linux/epoll/epoll.c | 119 +++++++++ .../cloudlibc/src/libc/sys/socket/recv.c | 36 ++- .../cloudlibc/src/libc/sys/socket/send.c | 30 +-- .../cloudlibc/src/libc/sys/socket/shutdown.c | 33 ++- .../cloudlibc/src/libc/sys/socket/socket.c | 210 +++++++++++++++ .../libc-top-half/musl/include/sys/epoll.h | 11 + src/wasi_libc.zig | 4 +- 15 files changed, 959 insertions(+), 57 deletions(-) create mode 100644 examples/c/example-epoll-tcp-server.c create mode 100644 examples/c/example-tcp-server.c create mode 100644 lib/libc/include/wasm-wasi-musl/__header_sys_epoll.h create mode 100644 lib/libc/include/wasm-wasi-musl/sys/epoll.h create mode 100644 lib/libc/include/wasm-wasi-musl/wasi/wasi_experimental_network.h create mode 100644 lib/libc/wasi/libc-bottom-half/cloudlibc/src/libc/sys/linux/epoll/epoll.c create mode 100644 lib/libc/wasi/libc-bottom-half/cloudlibc/src/libc/sys/socket/socket.c diff --git a/examples/c/example-epoll-tcp-server.c b/examples/c/example-epoll-tcp-server.c new file mode 100644 index 000000000000..3abe05ea07d5 --- /dev/null +++ b/examples/c/example-epoll-tcp-server.c @@ -0,0 +1,242 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define EVENTS_SIZE 128 +#define BUFFER_SIZE 128 + +int main() { + printf("Creating the socket\n"); + + int server = socket(AF_INET, SOCK_STREAM, 0); + + if (-1 == server) { + printf("`socket` failed with `%d` (errno = `%d`)\n", server, errno); + return 1; + } + + struct sockaddr_in address = { + .sin_family = AF_INET, + .sin_addr.s_addr = INADDR_ANY, + .sin_port = htons(9011), + }; + + printf("Binding the socket\n"); + + int err = bind(server, (struct sockaddr *) &address, sizeof(address)); + + if (0 != err) { + printf("`bind` failed with `%d` (errno = `%d`)\n", err, errno); + return 2; + } + + printf("Listening\n"); + + err = listen(server, 128); + + if (0 != err) { + printf("`listen` failed with `%d` (errno = `%d`)\n", err, errno); + return 3; + } + + printf("Non-blocking mode\n"); + + { + /* + int old_flags = fcntl(server, F_GETFL); + + if (-1 == old_flags) { + printf("`fcntl` for `F_GETFL` failed with `%d` (errno = `%d`)\n", old_flags, errno); + return 4; + } + + int new_flags = old_flags | O_NONBLOCK; + + printf("old flags = %d, new flags = %d\n", old_flags, new_flags); + + if (old_flags != new_flags) { + err = fcntl(server, F_SETFL, new_flags); + + if (-1 == err) { + printf("`fcntl` for `F_SETFL` failed with `%d` (errno = `%d`)\n", err, errno); + return 5; + } + } + */ + + int err = socket_set_nonblocking(server, 1); + + if (0 != err) { + printf("`socket_set_nonblocking` failed with `%d` (errno = `%d`)\n", err, errno); + return 4; + } + } + + printf("Creating the poller\n"); + + int epfd = epoll_create(1); + + if (-1 == epfd) { + printf("`epoll_create` failed with `%d` (errno = `%d`)\n", epfd, errno); + return 6; + } + + printf("Registering the server to the poller\n"); + + { + struct epoll_event event = { + .events = EPOLLIN | EPOLLONESHOT, + .data.fd = server, + }; + + err = epoll_ctl(epfd, EPOLL_CTL_ADD, server, &event); + + if (0 != err) { + printf("`epoll_ctl` with `EPOLL_CTL_ADD` failed with `%d` (errno = `%d`)\n", err, errno); + return 7; + } + } + + printf("Looping\n"); + + struct epoll_event events[EVENTS_SIZE]; + + while (1) { + memset(events, 0, sizeof(events)); + + int number_of_events = 0; + + printf("Waiting for new events\n"); + + number_of_events = epoll_wait(epfd, events, EVENTS_SIZE, -1); + + if (-1 == number_of_events) { + printf("`epoll_wait` failed with `%d` (errno = `%d`)\n", number_of_events, errno); + return 8; + } + + printf("Received %d new events\n", number_of_events); + + for (int nth = 0; nth < number_of_events; ++nth) { + struct epoll_event* event = &events[nth]; + + if (event->data.fd == server) { + printf("Accepting new connections\n"); + + while (1) { + struct sockaddr_in remote_address; + socklen_t remote_address_len; + + int remote_fd = accept(server, (struct sockaddr *) &remote_address, &remote_address_len); + + if (-1 != remote_fd) { + // Success. + printf("Accepted connection (fd = `%d`)\n", remote_fd); + } else if (EAGAIN == errno) { + // If we get a `WouldBlock` error, we know our listener + // has no more incoming connections queued, so we can + // return to polling and wait for some more. + break; + } else { + // If it was any other kind of error, something went wrong + // and we terminate with an error. + printf("`accept` failed with `%d` (errno = `%d`)\n", remote_fd, errno); + return 9; + } + + printf("Registering the new connection (only writable events)\n"); + + { + struct epoll_event event = { + .events = EPOLLOUT | EPOLLONESHOT, + .data.fd = remote_fd, + }; + + err = epoll_ctl(epfd, EPOLL_CTL_ADD, remote_fd, &event); + + if (0 != err) { + printf("`epoll_ctl` with `EPOLL_CTL_ADD` failed with `%d` (errno = `%d`)\n", err, errno); + return 10; + } + } + } + + printf("Re-registering the server\n"); + + { + struct epoll_event event = { + .events = EPOLLIN | EPOLLOUT | EPOLLONESHOT, + .data.fd = server, + }; + + err = epoll_ctl(epfd, EPOLL_CTL_MOD, server, &event); + + if (0 != err) { + printf("`epoll_ctl` with `EPOLL_CTL_MOD` failed with `%d` (errno = `%d`)\n", err, errno); + return 11; + } + } + } else { + int client = event->data.fd; + + int close_connection = 0; + + if ((event->events & EPOLLOUT) != 0) { + printf("Sending “Welcome!” to the client\n"); + + ssize_t io_written = send(client, "Welcome!\n", 9, 0); + + if (-1 == io_written) { + printf("`send` failed with `%zd` (errno = `%d`)\n", io_written, errno); + return 12; + } + + printf("Re-registering the new connection (only readable events)\n"); + + { + struct epoll_event event = { + .events = EPOLLIN | EPOLLONESHOT, + .data.fd = client, + }; + + err = epoll_ctl(epfd, EPOLL_CTL_MOD, client, &event); + + if (0 != err) { + printf("`epoll_ctl` with `EPOLL_CTL_MOD` failed with `%d` (errno = `%d`)\n", err, errno); + return 11; + } + } + + close_connection = 0; + } else if ((event->events & EPOLLIN) != 0) { + printf("Receiving the message from the client\n"); + + uint8_t buffer[BUFFER_SIZE] = {0}; + ssize_t io_read = recv(client, &buffer, BUFFER_SIZE, 0); + + if (-1 == io_read) { + printf("`recv` failed with `%zd` (errno = `%d`)\n", io_read, errno); + return 12; + } + + printf("Read: `%.*s`\n", (int) io_read, buffer); + + close_connection = 1; + } else { + close_connection = 1; + } + + if (close_connection == 1) { + printf("Closing the client %d\n", client); + + socket_close(client); + } + } + } + } +} diff --git a/examples/c/example-tcp-server.c b/examples/c/example-tcp-server.c new file mode 100644 index 000000000000..b78289b45ea6 --- /dev/null +++ b/examples/c/example-tcp-server.c @@ -0,0 +1,79 @@ +#include +#include +#include +#include + +#define BUFFER_SIZE 128 + +int main() { + printf("Creating the socket\n"); + + int server = socket(AF_INET, SOCK_STREAM, 0); + + if (-1 == server) { + printf("`socket` failed with `%d` (errno = `%d`)\n", server, errno); + return 1; + } + + struct sockaddr_in address = { + .sin_family = AF_INET, + .sin_addr.s_addr = INADDR_ANY, + .sin_port = htons(9010), + }; + + printf("Binding the socket\n"); + + int err = bind(server, (struct sockaddr *) &address, sizeof(address)); + + if (0 != err) { + printf("`bind` failed with `%d` (errno = `%d`)\n", err, errno); + return 2; + } + + printf("Listening\n"); + + err = listen(server, 3); + + if (0 != err) { + printf("`listen` failed with `%d` (errno = `%d`)\n", err, errno); + return 3; + } + + for (;;) { + printf("Waiting to accept a new connection\n"); + + struct sockaddr_in remote_address; + socklen_t remote_address_len; + + int remote_fd = accept(server, (struct sockaddr *) &remote_address, &remote_address_len); + + if (-1 == remote_fd) { + printf("`accept` failed with `%d` (errno = `%d`)\n", remote_fd, errno); + return 4; + } + + uint8_t buffer[BUFFER_SIZE] = {0}; + ssize_t io_read = recv(remote_fd, &buffer, BUFFER_SIZE, 0); + + if (-1 == io_read) { + printf("`recv` failed with `%zd` (errno = `%d`)\n", io_read, errno); + return 5; + } + + printf("Read: `%.*s`\n", (int) io_read, buffer); + + ssize_t io_written = send(remote_fd, &buffer, io_read, 0); + + if (-1 == io_written) { + printf("`send` failed with `%zd` (errno = `%d`)\n", io_written, errno); + return 6; + } + + err = shutdown(remote_fd, SHUT_RDWR); + + if (0 != err) { + printf("`shutdown` failed with `%d` (errno = `%d`)\n", err, errno); + return 7; + } + } +} diff --git a/lib/libc/include/wasm-wasi-musl/__header_sys_epoll.h b/lib/libc/include/wasm-wasi-musl/__header_sys_epoll.h new file mode 100644 index 000000000000..f3b56a2e6bce --- /dev/null +++ b/lib/libc/include/wasm-wasi-musl/__header_sys_epoll.h @@ -0,0 +1,6 @@ +#ifndef __wasilibc___header_sys_epoll_h +#define __wasilibc___header_sys_epoll_h + +#include + +#endif /* sys/epoll.h */ diff --git a/lib/libc/include/wasm-wasi-musl/__header_sys_socket.h b/lib/libc/include/wasm-wasi-musl/__header_sys_socket.h index 9fa8684f830e..afeecd09457c 100644 --- a/lib/libc/include/wasm-wasi-musl/__header_sys_socket.h +++ b/lib/libc/include/wasm-wasi-musl/__header_sys_socket.h @@ -34,6 +34,11 @@ extern "C" { #endif +// TODO: Remove this. Find why `fcntl` does not work as expected. +int socket_set_nonblocking(int sockfd, int nonblocking); +// TODO: Remove this. Find why `close` does not work as expected. +int socket_close(int sockfd); + #ifdef __cplusplus } #endif diff --git a/lib/libc/include/wasm-wasi-musl/sys/epoll.h b/lib/libc/include/wasm-wasi-musl/sys/epoll.h new file mode 100644 index 000000000000..3d3f8c38d188 --- /dev/null +++ b/lib/libc/include/wasm-wasi-musl/sys/epoll.h @@ -0,0 +1,80 @@ +#ifndef _SYS_EPOLL_H +#define _SYS_EPOLL_H + +#ifdef __wasilibc_unmodified_upstream /* Use alternate WASI libc headers */ +#else +#include <__header_sys_epoll.h> +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#define __NEED_sigset_t + +#include + +#ifdef __wasilibc_unmodified_upstream /* Use alternate WASI libc headers */ + +#define EPOLL_CLOEXEC O_CLOEXEC +#define EPOLL_NONBLOCK O_NONBLOCK + +enum EPOLL_EVENTS { __EPOLL_DUMMY }; +#endif + +#define EPOLLIN 0x001 +#define EPOLLPRI 0x002 +#define EPOLLOUT 0x004 +#define EPOLLRDNORM 0x040 +#define EPOLLNVAL 0x020 +#define EPOLLRDBAND 0x080 +#define EPOLLWRNORM 0x100 +#define EPOLLWRBAND 0x200 +#define EPOLLMSG 0x400 +#define EPOLLERR 0x008 +#define EPOLLHUP 0x010 +#define EPOLLRDHUP 0x2000 +#define EPOLLEXCLUSIVE (1U<<28) +#define EPOLLWAKEUP (1U<<29) +#define EPOLLONESHOT (1U<<30) +#define EPOLLET (1U<<31) + +#define EPOLL_CTL_ADD 1 +#define EPOLL_CTL_DEL 2 +#define EPOLL_CTL_MOD 3 + +typedef union epoll_data { + void *ptr; + int fd; + uint32_t u32; + uint64_t u64; +} epoll_data_t; + +struct epoll_event { + uint32_t events; + epoll_data_t data; +} +#ifdef __wasilibc_unmodified_upstream /* Use alternate WASI libc headers */ +#ifdef __x86_64__ +__attribute__ ((__packed__)) +#endif +#endif +; + + +int epoll_create(int); +int epoll_create1(int); +int epoll_ctl(int, int, int, struct epoll_event *); +int epoll_wait(int, struct epoll_event *, int, int); +int epoll_pwait(int, struct epoll_event *, int, int, const sigset_t *); + + +#ifdef __cplusplus +} +#endif + +#endif /* sys/epoll.h */ diff --git a/lib/libc/include/wasm-wasi-musl/sys/socket.h b/lib/libc/include/wasm-wasi-musl/sys/socket.h index cea24cfd4782..b5af1d969eff 100644 --- a/lib/libc/include/wasm-wasi-musl/sys/socket.h +++ b/lib/libc/include/wasm-wasi-musl/sys/socket.h @@ -393,20 +393,17 @@ struct sockaddr_storage { #include <__struct_sockaddr_storage.h> #endif -#ifdef __wasilibc_unmodified_upstream /* WASI has no socket/socketpair */ -int socket (int, int, int); +#ifdef __wasilibc_unmodified_upstream /* WASI has no socketpair */ int socketpair (int, int, int, int [2]); #endif -int shutdown (int, int); - -#ifdef __wasilibc_unmodified_upstream /* WASI has no bind/connect/listen/accept */ +int socket (int, int, int); int bind (int, const struct sockaddr *, socklen_t); int connect (int, const struct sockaddr *, socklen_t); int listen (int, int); int accept (int, struct sockaddr *__restrict, socklen_t *__restrict); int accept4(int, struct sockaddr *__restrict, socklen_t *__restrict, int); -#endif +int shutdown (int, int); #ifdef __wasilibc_unmodified_upstream /* WASI has no getsockname/getpeername */ int getsockname (int, struct sockaddr *__restrict, socklen_t *__restrict); diff --git a/lib/libc/include/wasm-wasi-musl/wasi/wasi_experimental_network.h b/lib/libc/include/wasm-wasi-musl/wasi/wasi_experimental_network.h new file mode 100644 index 000000000000..f61b312b124e --- /dev/null +++ b/lib/libc/include/wasm-wasi-musl/wasi/wasi_experimental_network.h @@ -0,0 +1,146 @@ +#ifndef __wasi_experimental_network_h +#define __wasi_experimental_network_h + +#include +#include + +#define IMPORT(name) __attribute__((__import_module__("wasi_experimental_network_unstable"), __import_name__(name))) + +typedef uint32_t __wasi_poll_t; + +typedef uint32_t __wasi_poll_token_t; + +typedef struct __wasi_poll_event_t { + __wasi_poll_token_t token; + bool readable; + bool writable; +} __wasi_poll_event_t; + +/** + * The _domain_ specifies a communication domain; this selects the + * protocol family which will be used for communication. + * + * It uses `i32` which is the equivalent of `int` in C, which is the + * typed used by `socket(2)` for the `domain` argument. + */ +typedef int32_t __wasi_socket_domain_t; + +typedef struct __wasi_socket_address_in_t { + __wasi_socket_domain_t family; + uint8_t address[4]; + uint16_t port; +} __wasi_socket_address_in_t; + +typedef struct __wasi_socket_address_in6_t { + __wasi_socket_domain_t family; + uint16_t sin6_port; + uint32_t sin6_flowinfo; + uint8_t sin6_addr[16]; + uint32_t sin6_scope_id; +} __wasi_socket_address_in6_t; + +typedef union __wasi_socket_address_t { + struct __wasi_socket_address_in_t v4; + struct __wasi_socket_address_in6_t v6; +} __wasi_socket_address_t; + +typedef int32_t __wasi_socket_type_t; + +typedef int32_t __wasi_socket_protocol_t; + +typedef int32_t __wasi_shutdown_t; + +// Domains. +#define __WASI_AF_INET 1 +#define __WASI_AF_INET6 2 +#define __WASI_AF_UNIX 3 +// no equivalent defined in `__header_sys_socket.h`: +//#define WASI_AF_PACKET 4 +//#define WASI_AF_VSOCK 5 + +// Protocols. +#define __WASI_DEFAULT_PROTOCOL 0 +// no equivalent defined in `__header_sys_socket.h`: +//#define WASI_ICMPv4 1 +//#define WASI_ICMPv6 2 +//#define WASI_TCP 3 +//#define WASI_UDP 4 + +// Shutdowns. +#define __WASI_SHUT_RD 1 +#define __WASI_SHUT_RDWR 3 +#define __WASI_SHUT_WR 2 + +// Types. +#define __WASI_SOCK_DGRAM 2 +#define __WASI_SOCK_STREAM 1 +// no equivalent defined in `__header_sys_socket.h`: +//#define WASI_SOCK_RAW 4 +//#define WASI_SOCK_SEQPACKET 3 + + +IMPORT("poller_add") +__wasi_errno_t __wasi_experimental_network_poller_add(__wasi_poll_t poll, + __wasi_fd_t fd, + struct __wasi_poll_event_t event); + +IMPORT("poller_create") +__wasi_errno_t __wasi_experimental_network_poller_create(__wasi_poll_t *poll_out); + +IMPORT("poller_delete") +__wasi_errno_t __wasi_experimental_network_poller_delete(__wasi_poll_t poll, __wasi_fd_t fd); + +IMPORT("poller_modify") +__wasi_errno_t __wasi_experimental_network_poller_modify(__wasi_poll_t poll, + __wasi_fd_t fd, + struct __wasi_poll_event_t event); + +IMPORT("poller_wait") +__wasi_errno_t __wasi_experimental_network_poller_wait(__wasi_poll_t poll, + struct __wasi_poll_event_t *events, + uint32_t events_size, + uint32_t *events_size_out); + +IMPORT("socket_accept") +__wasi_errno_t __wasi_experimental_network_socket_accept(__wasi_fd_t fd, + union __wasi_socket_address_t *remote_address, + __wasi_fd_t *remote_fd); + +IMPORT("socket_bind") +__wasi_errno_t __wasi_experimental_network_socket_bind(__wasi_fd_t fd, const union __wasi_socket_address_t *address); + +IMPORT("socket_close") +__wasi_errno_t __wasi_experimental_network_socket_close(__wasi_fd_t fd); + +IMPORT("socket_create") +__wasi_errno_t __wasi_experimental_network_socket_create(__wasi_socket_domain_t domain, + __wasi_socket_type_t ty, + __wasi_socket_protocol_t protocol, + __wasi_fd_t *fd_out); + +IMPORT("socket_listen") +__wasi_errno_t __wasi_experimental_network_socket_listen(__wasi_fd_t fd, uint32_t backlog); + +IMPORT("socket_recv") +__wasi_errno_t __wasi_experimental_network_socket_recv(__wasi_fd_t fd, + struct __wasi_ciovec_t *iov, + uint32_t iov_size, + __wasi_siflags_t iov_flags, + uint32_t *io_size_out); + +IMPORT("socket_send") +__wasi_errno_t __wasi_experimental_network_socket_send(__wasi_fd_t fd, + const struct __wasi_ciovec_t *iov, + uint32_t iov_size, + __wasi_siflags_t iov_flags, + uint32_t *io_size_out); + +IMPORT("socket_set_nonblocking") +__wasi_errno_t __wasi_experimental_network_socket_set_nonblocking(__wasi_fd_t fd, bool nonblocking); + +IMPORT("socket_shutdown") +__wasi_errno_t __wasi_experimental_network_socket_shutdown(__wasi_fd_t fd, __wasi_shutdown_t how); + +#undef IMPORT + +#endif diff --git a/lib/libc/wasi/libc-bottom-half/cloudlibc/src/common/errno.h b/lib/libc/wasi/libc-bottom-half/cloudlibc/src/common/errno.h index 7d178fbc5696..c0a4d3387cb4 100644 --- a/lib/libc/wasi/libc-bottom-half/cloudlibc/src/common/errno.h +++ b/lib/libc/wasi/libc-bottom-half/cloudlibc/src/common/errno.h @@ -13,10 +13,4 @@ static inline __wasi_errno_t errno_fixup_directory(__wasi_fd_t fd, return error; } -// WASI syscalls should just return ENOTSOCK if that's what the problem is. -static inline __wasi_errno_t errno_fixup_socket(__wasi_fd_t fd, - __wasi_errno_t error) { - return error; -} - #endif diff --git a/lib/libc/wasi/libc-bottom-half/cloudlibc/src/libc/sys/linux/epoll/epoll.c b/lib/libc/wasi/libc-bottom-half/cloudlibc/src/libc/sys/linux/epoll/epoll.c new file mode 100644 index 000000000000..6c215e69ddaf --- /dev/null +++ b/lib/libc/wasi/libc-bottom-half/cloudlibc/src/libc/sys/linux/epoll/epoll.c @@ -0,0 +1,119 @@ +#include +#include +#include +#include + +int epoll_create(int size) { + __wasi_poll_t epfd = 0; + __wasi_errno_t err = __wasi_experimental_network_poller_create(&epfd); + + if (0 != err) { + errno = err; + return -1; + } + + return epfd; +} + + +// `flags` will always be overwritten to `EPOLL_CLOEXEC`. +int epoll_create1(int flags) { + return epoll_create(1); +} + +// All events assumed `EPOLLONESHOT` as the `events` flag. +int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) { + if (EPOLL_CTL_DEL == op) { + __wasi_errno_t err = __wasi_experimental_network_poller_delete(epfd, fd); + + if (0 != err) { + errno = err; + return -1; + } + + return 0; + } + + if (EPOLL_CTL_ADD != op && EPOLL_CTL_MOD != op) { + errno = EINVAL; + return -1; + } + + if (NULL == event) { + errno = EINVAL; + return -1; + } + + struct __wasi_poll_event_t wasi_event = { + .token = event->data.u32, + .readable = (event->events & (EPOLLIN | EPOLLRDHUP | EPOLLHUP | EPOLLERR | EPOLLPRI)) != 0, + .writable = (event->events & (EPOLLOUT | EPOLLHUP | EPOLLERR)) != 0, + }; + + if (EPOLL_CTL_ADD == op) { + __wasi_errno_t err = __wasi_experimental_network_poller_add(epfd, fd, wasi_event); + + if (0 != err) { + errno = err; + return -1; + } + } else if (EPOLL_CTL_MOD == op) { + __wasi_errno_t err = __wasi_experimental_network_poller_modify(epfd, fd, wasi_event); + + if (0 != err) { + errno = err; + return -1; + } + } + + return 0; +} + +int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout) { + if (maxevents <= 0) { + errno = EINVAL; + return -1; + } + + __wasi_poll_event_t* received_events = (__wasi_poll_event_t*) malloc(sizeof(__wasi_poll_event_t) * maxevents); + uint32_t received_events_size = maxevents; + + __wasi_errno_t err = __wasi_experimental_network_poller_wait(epfd, received_events, maxevents, &received_events_size); + + if (0 != err) { + errno = err; + return -1; + } + + if ((int)(received_events_size) > maxevents) { + return -1; + } + + for (uint32_t nth = 0; nth < received_events_size; ++nth) { + __wasi_poll_event_t* received_event = &received_events[nth]; + + struct epoll_event* event = &events[nth]; + + if (received_event->readable) { + event->events = EPOLLIN | EPOLLRDHUP | EPOLLHUP | EPOLLERR | EPOLLPRI; + } else if (received_event->writable) { + event->events = EPOLLOUT | EPOLLHUP | EPOLLERR; + } else { + event->events = 0; + } + + event->data.u32 = received_event->token; + } + + return received_events_size; +} + +int epoll_pwait( + int epfd, + struct epoll_event *events, + int maxevents, + int timeout, + const sigset_t *sigmask + ) { + return -1; +} diff --git a/lib/libc/wasi/libc-bottom-half/cloudlibc/src/libc/sys/socket/recv.c b/lib/libc/wasi/libc-bottom-half/cloudlibc/src/libc/sys/socket/recv.c index 49c09d03768e..63c294127e9d 100644 --- a/lib/libc/wasi/libc-bottom-half/cloudlibc/src/libc/sys/socket/recv.c +++ b/lib/libc/wasi/libc-bottom-half/cloudlibc/src/libc/sys/socket/recv.c @@ -2,41 +2,37 @@ // // SPDX-License-Identifier: BSD-2-Clause -#include - #include #include #include +#include #include #include static_assert(MSG_PEEK == __WASI_RIFLAGS_RECV_PEEK, "Value mismatch"); static_assert(MSG_WAITALL == __WASI_RIFLAGS_RECV_WAITALL, "Value mismatch"); -ssize_t recv(int socket, void *restrict buffer, size_t length, int flags) { - // Validate flags. +ssize_t recv(int fd, void *restrict buffer, size_t length, int flags) { if ((flags & ~(MSG_PEEK | MSG_WAITALL)) != 0) { errno = EOPNOTSUPP; return -1; } - // Prepare input parameters. - __wasi_iovec_t iov = {.buf = buffer, .buf_len = length}; - __wasi_iovec_t *ri_data = &iov; - size_t ri_data_len = 1; - __wasi_riflags_t ri_flags = flags; - - // Perform system call. - size_t ro_datalen; - __wasi_roflags_t ro_flags; - __wasi_errno_t error = __wasi_sock_recv(socket, - ri_data, ri_data_len, ri_flags, - &ro_datalen, - &ro_flags); - if (error != 0) { - errno = errno_fixup_socket(socket, error); + __wasi_ciovec_t iov = { + .buf = buffer, + .buf_len = length + }; + uint32_t iov_size = 1; + __wasi_siflags_t iov_flags = flags; + uint32_t iov_size_out; + + __wasi_errno_t err = __wasi_experimental_network_socket_recv(fd, &iov, iov_size, iov_flags, &iov_size_out); + + if (0 != err) { + errno = err; return -1; } - return ro_datalen; + + return iov_size_out; } diff --git a/lib/libc/wasi/libc-bottom-half/cloudlibc/src/libc/sys/socket/send.c b/lib/libc/wasi/libc-bottom-half/cloudlibc/src/libc/sys/socket/send.c index 0759abf9b2c8..4b71fe604fbc 100644 --- a/lib/libc/wasi/libc-bottom-half/cloudlibc/src/libc/sys/socket/send.c +++ b/lib/libc/wasi/libc-bottom-half/cloudlibc/src/libc/sys/socket/send.c @@ -2,33 +2,33 @@ // // SPDX-License-Identifier: BSD-2-Clause -#include - #include #include #include +#include #include -ssize_t send(int socket, const void *buffer, size_t length, int flags) { - // This implementation does not support any flags. +ssize_t send(int fd, const void *buffer, size_t length, int flags) { if (flags != 0) { errno = EOPNOTSUPP; return -1; } - // Prepare input parameters. - __wasi_ciovec_t iov = {.buf = buffer, .buf_len = length}; - __wasi_ciovec_t *si_data = &iov; - size_t si_data_len = 1; - __wasi_siflags_t si_flags = 0; + __wasi_ciovec_t iov = { + .buf = buffer, + .buf_len = length + }; + uint32_t iov_size = 1; + __wasi_siflags_t iov_flags = flags; + uint32_t iov_size_out; + + __wasi_errno_t err = __wasi_experimental_network_socket_send(fd, &iov, iov_size, iov_flags, &iov_size_out); - // Perform system call. - size_t so_datalen; - __wasi_errno_t error = __wasi_sock_send(socket, si_data, si_data_len, si_flags, &so_datalen); - if (error != 0) { - errno = errno_fixup_socket(socket, error); + if (0 != err) { + errno = err; return -1; } - return so_datalen; + + return iov_size_out; } diff --git a/lib/libc/wasi/libc-bottom-half/cloudlibc/src/libc/sys/socket/shutdown.c b/lib/libc/wasi/libc-bottom-half/cloudlibc/src/libc/sys/socket/shutdown.c index 883b551ae832..0b78f1df5d5c 100644 --- a/lib/libc/wasi/libc-bottom-half/cloudlibc/src/libc/sys/socket/shutdown.c +++ b/lib/libc/wasi/libc-bottom-half/cloudlibc/src/libc/sys/socket/shutdown.c @@ -2,28 +2,43 @@ // // SPDX-License-Identifier: BSD-2-Clause -#include - #include #include #include +#include #include static_assert(SHUT_RD == __WASI_SDFLAGS_RD, "Value mismatch"); static_assert(SHUT_WR == __WASI_SDFLAGS_WR, "Value mismatch"); -int shutdown(int socket, int how) { - // Validate shutdown flags. - if (how != SHUT_RD && how != SHUT_WR && how != SHUT_RDWR) { +int shutdown(int fd, int how) { + __wasi_shutdown_t wasi_how; + + switch (how) { + case SHUT_RD: + wasi_how = __WASI_SHUT_RD; + break; + + case SHUT_WR: + wasi_how = __WASI_SHUT_WR; + break; + + case SHUT_RDWR: + wasi_how = __WASI_SHUT_RDWR; + break; + + default: errno = EINVAL; return -1; } - __wasi_errno_t error = __wasi_sock_shutdown(socket, how); - if (error != 0) { - errno = errno_fixup_socket(socket, error); + __wasi_errno_t err = __wasi_experimental_network_socket_shutdown(fd, wasi_how); + + if (0 != err) { + errno = err; return -1; } - return error; + + return err; } diff --git a/lib/libc/wasi/libc-bottom-half/cloudlibc/src/libc/sys/socket/socket.c b/lib/libc/wasi/libc-bottom-half/cloudlibc/src/libc/sys/socket/socket.c new file mode 100644 index 000000000000..40ae6626053f --- /dev/null +++ b/lib/libc/wasi/libc-bottom-half/cloudlibc/src/libc/sys/socket/socket.c @@ -0,0 +1,210 @@ +#include +#include +#include +#include +#include + +static int to_wasi_domain(int domain, __wasi_socket_domain_t *wasi_domain) { + switch (domain) { + case AF_INET: + *wasi_domain = __WASI_AF_INET; + break; + + case AF_INET6: + *wasi_domain = __WASI_AF_INET6; + break; + + case AF_UNIX: + *wasi_domain = __WASI_AF_UNIX; + + default: + return -1; + } + + return 0; +} + +static int to_wasi_type(int type, __wasi_socket_type_t *wasi_type) { + switch (type) { + case SOCK_DGRAM: + *wasi_type = __WASI_SOCK_DGRAM; + break; + + case SOCK_STREAM: + *wasi_type = __WASI_SOCK_STREAM; + break; + + default: + return -1; + } + + return 0; +} + +static int to_wasi_protocol(int protocol, __wasi_socket_protocol_t *wasi_protocol) { + switch (protocol) { + case 0: + *wasi_protocol = __WASI_DEFAULT_PROTOCOL; + break; + + default: + return -1; + } + + return 0; +} + +int socket(int domain, int type, int protocol) { + __wasi_socket_domain_t wasi_domain; + __wasi_socket_type_t wasi_type; + __wasi_socket_protocol_t wasi_protocol; + __wasi_fd_t fd; + __wasi_errno_t err; + + if (0 != to_wasi_domain(domain, &wasi_domain)) { + errno = EAFNOSUPPORT; + return -1; + } + + if (0 != to_wasi_type(type, &wasi_type)) { + errno = EACCES; + return -1; + } + + if (0 != to_wasi_protocol(protocol, &wasi_protocol)) { + errno = EPROTONOSUPPORT; + return -1; + } + + err = __wasi_experimental_network_socket_create(wasi_domain, wasi_type, wasi_protocol, &fd); + + if (0 != err) { + errno = err; + return -1; + } + + return fd; +} + +int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { + return -1; +} + +int bind(int fd, const struct sockaddr *addr, socklen_t addrlen) { + union __wasi_socket_address_t wasi_address; + __wasi_errno_t err; + + // IPv4. + if (addrlen == sizeof(struct sockaddr_in)) { + const struct sockaddr_in *ipv4 = (const struct sockaddr_in *) addr; + __wasi_socket_domain_t wasi_domain; + + if (-1 == to_wasi_domain(ipv4->sin_family, &wasi_domain)) { + errno = EAFNOSUPPORT; + return -1; + } + + union address_t { + in_addr_t s_addr; + uint8_t address[4]; + }; + + union address_t address; + address.s_addr = ipv4->sin_addr.s_addr; + + wasi_address.v4.family = wasi_domain; + wasi_address.v4.address[0] = address.address[0]; + wasi_address.v4.address[1] = address.address[1]; + wasi_address.v4.address[2] = address.address[2]; + wasi_address.v4.address[3] = address.address[3]; + wasi_address.v4.port = ipv4->sin_port; + } else { + // TODO: not fully implemented for the moment + return -1; + } + + err = __wasi_experimental_network_socket_bind(fd, &wasi_address); + + if (0 != err) { + errno = err; + return -1; + } + + return 0; +} + +int listen(int fd, int backlog) { + __wasi_errno_t err = __wasi_experimental_network_socket_listen(fd, backlog); + + if (0 != err) { + errno = err; + return -1; + } + + return 0; +} + +int accept(int fd, struct sockaddr *addr, socklen_t *addrlen) { + union __wasi_socket_address_t remote_address; + __wasi_fd_t remote_fd; + + __wasi_errno_t err = __wasi_experimental_network_socket_accept(fd, &remote_address, &remote_fd); + + if (0 != err) { + errno = err; + return -1; + } + + if (NULL != addr && NULL != addrlen) { + // IPv4 + if (remote_address.v4.family == __WASI_AF_INET) { + struct sockaddr_in *ipv4 = (struct sockaddr_in *) addr; + + union address_t { + in_addr_t s_addr; + uint8_t address[4]; + }; + + union address_t address; + address.address[0] = remote_address.v4.address[0]; + address.address[1] = remote_address.v4.address[1]; + address.address[2] = remote_address.v4.address[2]; + address.address[3] = remote_address.v4.address[3]; + + ipv4->sin_family = AF_INET; + ipv4->sin_port = remote_address.v4.port; + ipv4->sin_addr.s_addr = address.s_addr; + + *addrlen = sizeof(struct sockaddr_in); + } else { + // TODO: not fully implemented for the moment + return -1; + } + } + + return remote_fd; +} + +int accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags) { + return accept(sockfd, addr, addrlen); +} + +int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen) { + return 0; +} + +int socket_set_nonblocking(int sockfd, int nonblocking) { + if (0 != __wasi_experimental_network_socket_set_nonblocking(sockfd, 1 == nonblocking)) { + return -1; + } + + return 0; +} + +int socket_close(int sockfd) { + if (0 != __wasi_experimental_network_socket_close(sockfd)) { + return -1; + } + + return 0; +} diff --git a/lib/libc/wasi/libc-top-half/musl/include/sys/epoll.h b/lib/libc/wasi/libc-top-half/musl/include/sys/epoll.h index ac81a8418a0c..3d3f8c38d188 100644 --- a/lib/libc/wasi/libc-top-half/musl/include/sys/epoll.h +++ b/lib/libc/wasi/libc-top-half/musl/include/sys/epoll.h @@ -1,6 +1,11 @@ #ifndef _SYS_EPOLL_H #define _SYS_EPOLL_H +#ifdef __wasilibc_unmodified_upstream /* Use alternate WASI libc headers */ +#else +#include <__header_sys_epoll.h> +#endif + #ifdef __cplusplus extern "C" { #endif @@ -13,10 +18,14 @@ extern "C" { #include +#ifdef __wasilibc_unmodified_upstream /* Use alternate WASI libc headers */ + #define EPOLL_CLOEXEC O_CLOEXEC #define EPOLL_NONBLOCK O_NONBLOCK enum EPOLL_EVENTS { __EPOLL_DUMMY }; +#endif + #define EPOLLIN 0x001 #define EPOLLPRI 0x002 #define EPOLLOUT 0x004 @@ -49,9 +58,11 @@ struct epoll_event { uint32_t events; epoll_data_t data; } +#ifdef __wasilibc_unmodified_upstream /* Use alternate WASI libc headers */ #ifdef __x86_64__ __attribute__ ((__packed__)) #endif +#endif ; diff --git a/src/wasi_libc.zig b/src/wasi_libc.zig index 0ef8d4f4d474..fc918970937d 100644 --- a/src/wasi_libc.zig +++ b/src/wasi_libc.zig @@ -408,14 +408,16 @@ const libc_bottom_half_src_files = [_][]const u8{ "wasi/libc-bottom-half/cloudlibc/src/libc/stdio/renameat.c", "wasi/libc-bottom-half/cloudlibc/src/libc/stdlib/_Exit.c", "wasi/libc-bottom-half/cloudlibc/src/libc/sys/ioctl/ioctl.c", + "wasi/libc-bottom-half/cloudlibc/src/libc/sys/linux/epoll/epoll.c", "wasi/libc-bottom-half/cloudlibc/src/libc/sys/select/pselect.c", "wasi/libc-bottom-half/cloudlibc/src/libc/sys/select/select.c", "wasi/libc-bottom-half/cloudlibc/src/libc/sys/socket/getsockopt.c", "wasi/libc-bottom-half/cloudlibc/src/libc/sys/socket/recv.c", "wasi/libc-bottom-half/cloudlibc/src/libc/sys/socket/send.c", "wasi/libc-bottom-half/cloudlibc/src/libc/sys/socket/shutdown.c", - "wasi/libc-bottom-half/cloudlibc/src/libc/sys/stat/fstatat.c", + "wasi/libc-bottom-half/cloudlibc/src/libc/sys/socket/socket.c", "wasi/libc-bottom-half/cloudlibc/src/libc/sys/stat/fstat.c", + "wasi/libc-bottom-half/cloudlibc/src/libc/sys/stat/fstatat.c", "wasi/libc-bottom-half/cloudlibc/src/libc/sys/stat/futimens.c", "wasi/libc-bottom-half/cloudlibc/src/libc/sys/stat/mkdirat.c", "wasi/libc-bottom-half/cloudlibc/src/libc/sys/stat/utimensat.c",