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

Commit

Permalink
unix: report and store execve() errors
Browse files Browse the repository at this point in the history
Make the forked child process send the errno to its parent process when
it fails to spawn the new process.
  • Loading branch information
bnoordhuis committed Aug 8, 2012
1 parent bf28aa4 commit f227ca6
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

1 comment on commit f227ca6

@piscisaureus
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bn

Please sign in to comment.