Skip to content
This repository has been archived by the owner on May 4, 2018. It is now read-only.

Commit

Permalink
Browse files Browse the repository at this point in the history
unix: retrieve execve() errors in process.c
Make the forked child process send the errno to its parent process when it
fails to spawn a new process.
  • Loading branch information
bnoordhuis committed Aug 8, 2012
1 parent bf28aa4 commit 13467a4
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 29 deletions.
3 changes: 2 additions & 1 deletion include/uv-private/uv-unix.h
Expand Up @@ -258,7 +258,8 @@ struct uv__io_s {
int retcode;

#define UV_PROCESS_PRIVATE_FIELDS \
ev_child child_watcher;
ev_child child_watcher; \
int errorno; \

#define UV_FS_PRIVATE_FIELDS \
struct stat statbuf; \
Expand Down
92 changes: 64 additions & 28 deletions src/unix/process.c
Expand Up @@ -53,17 +53,19 @@ static void uv__chld(EV_P_ ev_child* watcher, int revents) {

ev_child_stop(EV_A_ &process->child_watcher);

if (WIFEXITED(status)) {
if (process->exit_cb == NULL)
return;

if (WIFEXITED(status))
exit_status = WEXITSTATUS(status);
}

if (WIFSIGNALED(status)) {
if (WIFSIGNALED(status))
term_signal = WTERMSIG(status);
}

if (process->exit_cb) {
process->exit_cb(process, exit_status, term_signal);
}
if (process->errorno)
uv__set_sys_error(process->loop, process->errorno);

process->exit_cb(process, exit_status, term_signal);
}


Expand Down Expand Up @@ -202,9 +204,37 @@ static void uv__process_close_stream(uv_stdio_container_t* container) {
}


static int uv__read_int(int fd) {
ssize_t n;
int val;

do
n = read(fd, &val, sizeof(val));
while (n == -1 && errno == EINTR);

assert(n == sizeof(val));
return val;
}


static void uv__write_int(int fd, int val) {
ssize_t n;

do
n = write(fd, &val, sizeof(val));
while (n == -1 && errno == EINTR);

if (n == -1 && errno == EPIPE)
return; /* parent process has quit */

assert(n == sizeof(val));
}


static void uv__process_child_init(uv_process_options_t options,
int stdio_count,
int* pipes) {
int* pipes,
int error_fd) {
int i;

if (options.flags & UV_PROCESS_DETACHED) {
Expand All @@ -227,6 +257,7 @@ static void uv__process_child_init(uv_process_options_t options,
use_fd = open("/dev/null", i == 0 ? O_RDONLY : O_RDWR);

if (use_fd < 0) {
uv__write_int(error_fd, errno);
perror("failed to open stdio");
_exit(127);
}
Expand All @@ -243,23 +274,27 @@ static void uv__process_child_init(uv_process_options_t options,
}

if (options.cwd && chdir(options.cwd)) {
uv__write_int(error_fd, errno);
perror("chdir()");
_exit(127);
}

if ((options.flags & UV_PROCESS_SETGID) && setgid(options.gid)) {
uv__write_int(error_fd, errno);
perror("setgid()");
_exit(127);
}

if ((options.flags & UV_PROCESS_SETUID) && setuid(options.uid)) {
uv__write_int(error_fd, errno);
perror("setuid()");
_exit(127);
}

environ = options.env;

execvp(options.file, options.args);
uv__write_int(error_fd, errno);
perror("execvp()");
_exit(127);
}
Expand All @@ -277,9 +312,9 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process,
int* pipes = malloc(2 * stdio_count * sizeof(int));
int signal_pipe[2] = { -1, -1 };
struct pollfd pfd;
int status;
pid_t pid;
int i;
int r;

if (pipes == NULL) {
errno = ENOMEM;
Expand All @@ -295,9 +330,6 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process,

uv__handle_init(loop, (uv_handle_t*)process, UV_PROCESS);
loop->counters.process_init++;
uv__handle_start(process);

process->exit_cb = options.exit_cb;

/* Init pipe pairs */
for (i = 0; i < stdio_count; i++) {
Expand Down Expand Up @@ -345,35 +377,38 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process,
}

if (pid == 0) {
/* Child */
uv__process_child_init(options, stdio_count, pipes);

/* Execution never reaches here. */
uv__process_child_init(options, stdio_count, pipes, signal_pipe[1]);
abort();
}

/* Parent. */

/* Restore environment. */
environ = save_our_env;

/* POLLHUP signals child has exited or execve()'d. */
close(signal_pipe[1]);
do {
pfd.fd = signal_pipe[0];
pfd.events = POLLIN|POLLHUP;
pfd.revents = 0;
errno = 0, status = poll(&pfd, 1, -1);
}
while (status == -1 && (errno == EINTR || errno == ENOMEM));
pfd.revents = 0;
pfd.events = POLLIN|POLLHUP;
pfd.fd = signal_pipe[0];

assert((status == 1) && "poll() on pipe read end failed");
close(signal_pipe[0]);
do
r = poll(&pfd, 1, -1);
while (r == -1 && errno == EINTR);

process->pid = pid;
assert((r == 1) && "poll()_on read end of pipe failed");
assert((pfd.revents & (POLLIN|POLLHUP)) && "unexpected poll() revents");

if (pfd.revents & POLLIN)
process->errorno = uv__read_int(signal_pipe[0]);
else /* POLLHUP */
process->errorno = 0;

close(signal_pipe[0]);

ev_child_init(&process->child_watcher, uv__chld, pid, 0);
ev_child_start(process->loop->ev, &process->child_watcher);
process->child_watcher.data = process;
process->exit_cb = options.exit_cb;
process->pid = pid;

for (i = 0; i < options.stdio_count; i++) {
if (uv__process_open_stream(&options.stdio[i], pipes + i * 2, i == 0)) {
Expand All @@ -387,6 +422,7 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process,
}
}

uv__handle_start(process);
free(pipes);

return 0;
Expand Down
2 changes: 2 additions & 0 deletions test/test-list.h
Expand Up @@ -123,6 +123,7 @@ TEST_DECLARE (getsockname_tcp)
TEST_DECLARE (getsockname_udp)
TEST_DECLARE (fail_always)
TEST_DECLARE (pass_always)
TEST_DECLARE (spawn_fails)
TEST_DECLARE (spawn_exit_code)
TEST_DECLARE (spawn_stdout)
TEST_DECLARE (spawn_stdin)
Expand Down Expand Up @@ -350,6 +351,7 @@ TASK_LIST_START
TEST_ENTRY (poll_unidirectional)
TEST_ENTRY (poll_close)

TEST_ENTRY (spawn_fails)
TEST_ENTRY (spawn_exit_code)
TEST_ENTRY (spawn_stdout)
TEST_ENTRY (spawn_stdin)
Expand Down
11 changes: 11 additions & 0 deletions test/test-spawn.c
Expand Up @@ -145,6 +145,17 @@ static void timer_cb(uv_timer_t* handle, int status) {
}


TEST_IMPL(spawn_fails) {
init_process_options("", exit_cb_failure_expected);
options.file = options.args[0] = "program-that-had-better-not-exist";
ASSERT(0 == uv_spawn(uv_default_loop(), &process, options));
ASSERT(0 != uv_is_active((uv_handle_t*)&process));
ASSERT(0 == uv_run(uv_default_loop()));
ASSERT(uv_last_error(uv_default_loop()).code == UV_ENOENT);
return 0;
}


TEST_IMPL(spawn_exit_code) {
int r;

Expand Down

0 comments on commit 13467a4

Please sign in to comment.