Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions include/uv.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
19 changes: 19 additions & 0 deletions src/unix/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@
#include <sys/time.h>
#include <time.h> /* 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 <sys/filio.h>
# include <sys/wait.h>
Expand Down Expand Up @@ -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++)
Expand Down
257 changes: 254 additions & 3 deletions src/unix/process.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,12 @@
#include <fcntl.h>
#include <poll.h>

#if defined(__APPLE__)
#if defined(__APPLE__) || defined(__wasi__)
# include <spawn.h>
# include <paths.h>
#endif

#if defined(__APPLE__)
# include <sys/kauth.h>
# include <sys/types.h>
# include <sys/sysctl.h>
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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:
Expand Down
Loading