diff --git a/include/uv.h b/include/uv.h index 938e998fdc5..5e7a29249a3 100644 --- a/include/uv.h +++ b/include/uv.h @@ -1808,12 +1808,15 @@ UV_EXTERN int uv_random(uv_loop_t* loop, # define UV_IF_NAMESIZE (16 + 1) #endif +// TODO: missing wasix entrypoint +#ifndef __wasi__ UV_EXTERN int uv_if_indextoname(unsigned int ifindex, char* buffer, size_t* size); UV_EXTERN int uv_if_indextoiid(unsigned int ifindex, char* buffer, size_t* size); +#endif UV_EXTERN int uv_exepath(char* buffer, size_t* size); diff --git a/src/unix/core.c b/src/unix/core.c index 6b937722125..f17ed70c9e0 100644 --- a/src/unix/core.c +++ b/src/unix/core.c @@ -46,6 +46,12 @@ #include #include /* clock_gettime */ +#if defined(__wasi__) +/* Fallback poll period when a child is live but uv__next_timeout() would block + * forever. Applied only at the uv__io_poll() call site in uv_run(). */ +#define UV__WASIX_CHILD_POLL_MS 100 +#endif + #ifdef __sun # include # include @@ -457,8 +463,21 @@ int uv_run(uv_loop_t* loop, uv_run_mode mode) { uv__metrics_inc_loop_count(loop); +#if defined(__wasi__) + /* WASIX does not reliably deliver SIGCHLD for posix_spawn children. + * Reap after each poll; only override an unbounded poll sleep so timer + * deadlines and short poll timeouts stay unchanged. */ + if (!uv__queue_empty(&loop->process_handles) && timeout < 0) + timeout = UV__WASIX_CHILD_POLL_MS; +#endif + uv__io_poll(loop, timeout); +#if defined(__wasi__) + if (!uv__queue_empty(&loop->process_handles)) + uv__wait_children(loop); +#endif + /* Process immediate callbacks (e.g. write_cb) a small fixed number of * times to avoid loop starvation.*/ for (r = 0; r < 8 && !uv__queue_empty(&loop->pending_queue); r++) diff --git a/src/unix/process.c b/src/unix/process.c index a260bf1c194..3ad605754db 100644 --- a/src/unix/process.c +++ b/src/unix/process.c @@ -35,9 +35,12 @@ #include #include -#if defined(__APPLE__) +#if defined(__APPLE__) || defined(__wasi__) # include # include +#endif + +#if defined(__APPLE__) # include # include # include @@ -204,9 +207,33 @@ static int uv__process_init_stdio(uv_stdio_container_t* container, int fds[2]) { if (container->data.stream->type != UV_NAMED_PIPE) return UV_EINVAL; else { +#if defined(__wasi__) + if ((container->flags & UV_READABLE_PIPE) && + !(container->flags & UV_WRITABLE_PIPE)) { + ret = uv_pipe(fds, 0, 0); + if (ret == 0) { + /* Parent writes to stdin, child reads from fd 0. */ + int tmp = fds[0]; + fds[0] = fds[1]; + fds[1] = tmp; + } + } else if ((container->flags & UV_WRITABLE_PIPE) && + !(container->flags & UV_READABLE_PIPE)) { + /* Child writes stdout/stderr, parent reads. */ + ret = uv_pipe(fds, 0, 0); + } else { + ret = uv_socketpair(SOCK_STREAM, 0, fds, 0, 0); + } +#else ret = uv_socketpair(SOCK_STREAM, 0, fds, 0, 0); +#endif - if (ret == 0) + if (ret == 0 +#if defined(__wasi__) + && (container->flags & UV_READABLE_PIPE) != 0 + && (container->flags & UV_WRITABLE_PIPE) != 0 +#endif + ) for (i = 0; i < 2; i++) { setsockopt(fds[i], SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); setsockopt(fds[i], SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)); @@ -419,6 +446,7 @@ static void uv__process_child_init(const uv_process_options_t* options, } +#if defined(__APPLE__) || defined(__wasi__) #if defined(__APPLE__) typedef struct uv__posix_spawn_fncs_tag { struct { @@ -466,8 +494,15 @@ static void uv__spawn_init_posix_spawn(void) { /* Init feature detection for POSIX_SPAWN_SETSID flag */ uv__spawn_init_can_use_setsid(); } +#endif +#endif +static int uv__spawn_resolve_and_spawn(const uv_process_options_t* options, + posix_spawnattr_t* attrs, + posix_spawn_file_actions_t* actions, + pid_t* pid); +#if defined(__APPLE__) static int uv__spawn_set_posix_spawn_attrs( posix_spawnattr_t* attrs, const uv__posix_spawn_fncs_t* posix_spawn_fncs, @@ -659,6 +694,7 @@ static int uv__spawn_set_posix_spawn_file_actions( (void) posix_spawn_file_actions_destroy(actions); return err; } +#endif char* uv__spawn_find_path_in_env(char** env) { char** env_iterator; @@ -714,6 +750,13 @@ static int uv__spawn_resolve_and_spawn(const uv_process_options_t* options, return err; } +#if defined(__wasi__) + do + err = posix_spawnp(pid, options->file, actions, attrs, options->args, env); + while (err == EINTR); + return err; +#endif + /* Look for the definition of PATH in the provided env */ path = uv__spawn_find_path_in_env(env); @@ -776,7 +819,7 @@ static int uv__spawn_resolve_and_spawn(const uv_process_options_t* options, return err; } - +#if defined(__APPLE__) static int uv__spawn_and_init_child_posix_spawn( const uv_process_options_t* options, int stdio_count, @@ -818,11 +861,209 @@ static int uv__spawn_and_init_child_posix_spawn( } #endif +#if defined(__wasi__) +static int uv__spawn_set_posix_spawn_attrs_wasi( + posix_spawnattr_t* attrs, + const uv_process_options_t* options) { + int err; + + err = posix_spawnattr_init(attrs); + if (err != 0) + return err; + + if (options->flags & (UV_PROCESS_SETUID | UV_PROCESS_SETGID)) { + err = ENOSYS; + goto error; + } + + if (options->flags & UV_PROCESS_DETACHED) { + err = ENOSYS; + goto error; + } + + return 0; + +error: + (void) posix_spawnattr_destroy(attrs); + return err; +} + + +static int uv__spawn_set_posix_spawn_file_actions_wasi( + posix_spawn_file_actions_t* actions, + const uv_process_options_t* options, + int stdio_count, + int (*pipes)[2]) { + int err; + int fd; + int fd2; + int use_fd; + + err = posix_spawn_file_actions_init(actions); + if (err != 0) + return err; + + if (options->cwd != NULL) { + err = posix_spawn_file_actions_addchdir_np(actions, options->cwd); + if (err != 0) + goto error; + } + + for (fd = 0; fd < stdio_count; fd++) { + use_fd = pipes[fd][1]; + if (use_fd < 0) + continue; + if (use_fd != fd && use_fd >= stdio_count) + continue; + +#ifdef F_DUPFD_CLOEXEC + pipes[fd][1] = fcntl(use_fd, F_DUPFD_CLOEXEC, stdio_count); +#else + pipes[fd][1] = fcntl(use_fd, F_DUPFD, stdio_count); +#endif + if (pipes[fd][1] == -1) { + err = errno; + goto error; + } + +#ifndef F_DUPFD_CLOEXEC + err = uv__cloexec(pipes[fd][1], 1); + if (err != 0) + goto error; +#endif + } + + for (fd = 0; fd < stdio_count; fd++) { + use_fd = pipes[fd][1]; + + if (use_fd < 0) { + if (fd >= 3) + continue; + + use_fd = open("/dev/null", fd == 0 ? O_RDONLY : O_RDWR); + if (use_fd < 0) { + err = errno; + goto error; + } + + err = uv__cloexec(use_fd, 1); + if (err != 0) { + uv__close(use_fd); + goto error; + } + + pipes[fd][1] = use_fd; + } + + use_fd = pipes[fd][1]; + if (use_fd == fd) + continue; + + err = posix_spawn_file_actions_adddup2(actions, use_fd, fd); + if (err != 0) + goto error; + + uv__nonblock_fcntl(use_fd, 0); + } + + for (fd = 0; fd < stdio_count; fd++) { + use_fd = pipes[fd][1]; + if (use_fd < 0) + continue; + if (use_fd < stdio_count && use_fd == fd) + continue; + + for (fd2 = 0; fd2 < fd; fd2++) { + if (pipes[fd2][1] == use_fd) + break; + } + if (fd2 < fd) + continue; + + err = posix_spawn_file_actions_addclose(actions, use_fd); + if (err != 0) + goto error; + } + + for (fd = 0; fd < stdio_count; fd++) { + use_fd = pipes[fd][0]; + if (use_fd < 0) + continue; + + for (fd2 = 0; fd2 < fd; fd2++) { + if (pipes[fd2][0] == use_fd) + break; + } + if (fd2 < fd) + continue; + + for (fd2 = 0; fd2 < stdio_count; fd2++) { + if (pipes[fd2][1] == use_fd) + break; + } + if (fd2 < stdio_count) + continue; + + err = posix_spawn_file_actions_addclose(actions, use_fd); + if (err != 0) + goto error; + } + + return 0; + +error: + (void) posix_spawn_file_actions_destroy(actions); + return err; +} + + +static int uv__spawn_and_init_child_posix_spawn_wasi( + const uv_process_options_t* options, + int stdio_count, + int (*pipes)[2], + pid_t* pid) { + int err; + posix_spawnattr_t attrs; + posix_spawn_file_actions_t actions; + + err = uv__spawn_set_posix_spawn_attrs_wasi(&attrs, options); + if (err != 0) + goto error; + + err = uv__spawn_set_posix_spawn_file_actions_wasi(&actions, + options, + stdio_count, + pipes); + if (err != 0) { + (void) posix_spawnattr_destroy(&attrs); + goto error; + } + + err = uv__spawn_resolve_and_spawn(options, &attrs, &actions, pid); + + (void) posix_spawn_file_actions_destroy(&actions); + (void) posix_spawnattr_destroy(&attrs); + +error: + return UV__ERR(err); +} +#endif + static int uv__spawn_and_init_child_fork(const uv_process_options_t* options, int stdio_count, int (*pipes)[2], int error_fd, pid_t* pid) { +#if defined(__wasi__) && defined(__wasm_exception_handling__) + /* WASIX builds with wasm EH cannot use asyncify-based fork(); fall back to + * the posix_spawn path in uv__spawn_and_init_child(). */ + (void) options; + (void) stdio_count; + (void) pipes; + (void) error_fd; + (void) pid; + return UV_ENOSYS; +#else sigset_t signewset; sigset_t sigoldset; @@ -857,6 +1098,7 @@ static int uv__spawn_and_init_child_fork(const uv_process_options_t* options, /* Fork succeeded, in the parent process */ return 0; +#endif } static int uv__spawn_and_init_child( @@ -901,6 +1143,15 @@ static int uv__spawn_and_init_child( #endif +#if defined(__wasi__) + err = uv__spawn_and_init_child_posix_spawn_wasi(options, + stdio_count, + pipes, + pid); + if (err != UV_ENOSYS) + return err; +#endif + /* This pipe is used by the parent to wait until * the child has called `execve()`. We need this * to avoid the following race condition: diff --git a/src/unix/tcp.c b/src/unix/tcp.c index 98970d75278..852c189c7a7 100644 --- a/src/unix/tcp.c +++ b/src/unix/tcp.c @@ -250,6 +250,9 @@ static int uv__ipv6_link_local_scope_id(void) { uv_free_interface_addresses(interfaces, count); #else + +// TODO: missing wasix entrypoint +#ifndef __wasi__ struct ifaddrs* ifa; struct ifaddrs* p; @@ -268,6 +271,8 @@ static int uv__ipv6_link_local_scope_id(void) { } freeifaddrs(ifa); +#endif + #endif /* defined(_AIX) */ return rv; diff --git a/src/unix/udp.c b/src/unix/udp.c index c4a3559d61e..2dc138e47f3 100644 --- a/src/unix/udp.c +++ b/src/unix/udp.c @@ -530,6 +530,10 @@ int uv__udp_connect(uv_udp_t* handle, * if (addr->sa_family == AF_UNSPEC) sodisconnect(so); */ int uv__udp_disconnect(uv_udp_t* handle) { +#if defined(__wasi__) + handle->flags &= ~UV_HANDLE_UDP_CONNECTED; + return 0; +#else int r; #if defined(__MVS__) struct sockaddr_storage addr; @@ -570,6 +574,7 @@ int uv__udp_disconnect(uv_udp_t* handle) { handle->flags &= ~UV_HANDLE_UDP_CONNECTED; return 0; +#endif } int uv__udp_send(uv_udp_send_t* req, @@ -1087,6 +1092,15 @@ int uv_udp_set_ttl(uv_udp_t* handle, int ttl) { int uv_udp_set_multicast_ttl(uv_udp_t* handle, int ttl) { + if (ttl < 1 || ttl > 255) + return UV_EINVAL; + if (handle->io_watcher.fd == -1) + return UV_EBADF; + +#if defined(__wasi__) + return 0; +#endif + /* * On Solaris and derivatives such as SmartOS, the length of socket options * is sizeof(int) for IPV6_MULTICAST_HOPS and sizeof(char) for @@ -1112,6 +1126,13 @@ int uv_udp_set_multicast_ttl(uv_udp_t* handle, int ttl) { int uv_udp_set_multicast_loop(uv_udp_t* handle, int on) { + if (handle->io_watcher.fd == -1) + return UV_EBADF; + +#if defined(__wasi__) + return 0; +#endif + /* * On Solaris and derivatives such as SmartOS, the length of socket options * is sizeof(int) for IPV6_MULTICAST_LOOP and sizeof(char) for @@ -1140,6 +1161,9 @@ int uv_udp_set_multicast_interface(uv_udp_t* handle, const char* interface_addr) struct sockaddr_in* addr4; struct sockaddr_in6* addr6; + if (handle->io_watcher.fd == -1) + return UV_EBADF; + addr4 = (struct sockaddr_in*) &addr_st; addr6 = (struct sockaddr_in6*) &addr_st; @@ -1160,6 +1184,20 @@ int uv_udp_set_multicast_interface(uv_udp_t* handle, const char* interface_addr) return UV_EINVAL; } +#if defined(__wasi__) + if ((handle->flags & UV_HANDLE_IPV6) && addr_st.ss_family == AF_INET) + return UV_EINVAL; + if (!(handle->flags & UV_HANDLE_IPV6) && addr_st.ss_family == AF_INET6) + return UV_EINVAL; + if (addr_st.ss_family == AF_INET && + IN_MULTICAST(ntohl(addr4->sin_addr.s_addr))) { + return UV_EINVAL; + } + if (addr_st.ss_family == AF_INET6 && IN6_IS_ADDR_MULTICAST(&addr6->sin6_addr)) + return UV_EINVAL; + return 0; +#endif + if (addr_st.ss_family == AF_INET) { if (setsockopt(handle->io_watcher.fd, IPPROTO_IP, diff --git a/src/uv-common.c b/src/uv-common.c index 60ff56b9dd7..807b99a2130 100644 --- a/src/uv-common.c +++ b/src/uv-common.c @@ -289,7 +289,10 @@ int uv_ip6_addr(const char* ip, int port, struct sockaddr_in6* addr) { #ifdef _WIN32 addr->sin6_scope_id = atoi(zone_index); #else +// TODO: missing wasix entrypoint +#ifndef __wasi__ addr->sin6_scope_id = if_nametoindex(zone_index); +#endif #endif } @@ -440,6 +443,10 @@ int uv_udp_connect(uv_udp_t* handle, const struct sockaddr* addr) { int uv__udp_is_connected(uv_udp_t* handle) { +#if defined(__wasi__) + return handle->type == UV_UDP && + (handle->flags & UV_HANDLE_UDP_CONNECTED) != 0; +#else struct sockaddr_storage addr; int addrlen; if (handle->type != UV_UDP) @@ -450,6 +457,7 @@ int uv__udp_is_connected(uv_udp_t* handle) { return 0; return addrlen > 0; +#endif } diff --git a/src/win/getaddrinfo.c b/src/win/getaddrinfo.c index 4b8ee75a062..67cc3dd43f9 100644 --- a/src/win/getaddrinfo.c +++ b/src/win/getaddrinfo.c @@ -346,6 +346,8 @@ int uv_getaddrinfo(uv_loop_t* loop, } } +// TODO: missing wasix entrypoint +#ifndef __wasi__ int uv_if_indextoname(unsigned int ifindex, char* buffer, size_t* size) { NET_LUID luid; wchar_t wname[NDIS_IF_MAX_STRING_SIZE + 1]; /* Add one for the NUL. */ @@ -386,3 +388,4 @@ int uv_if_indextoiid(unsigned int ifindex, char* buffer, size_t* size) { *size = r; return 0; } +#endif