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.
This commit is contained in:
parent
bf28aa4e78
commit
13467a40d4
@ -258,7 +258,8 @@ struct uv__io_s {
|
|||||||
int retcode;
|
int retcode;
|
||||||
|
|
||||||
#define UV_PROCESS_PRIVATE_FIELDS \
|
#define UV_PROCESS_PRIVATE_FIELDS \
|
||||||
ev_child child_watcher;
|
ev_child child_watcher; \
|
||||||
|
int errorno; \
|
||||||
|
|
||||||
#define UV_FS_PRIVATE_FIELDS \
|
#define UV_FS_PRIVATE_FIELDS \
|
||||||
struct stat statbuf; \
|
struct stat statbuf; \
|
||||||
|
|||||||
@ -53,17 +53,19 @@ static void uv__chld(EV_P_ ev_child* watcher, int revents) {
|
|||||||
|
|
||||||
ev_child_stop(EV_A_ &process->child_watcher);
|
ev_child_stop(EV_A_ &process->child_watcher);
|
||||||
|
|
||||||
if (WIFEXITED(status)) {
|
if (process->exit_cb == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (WIFEXITED(status))
|
||||||
exit_status = WEXITSTATUS(status);
|
exit_status = WEXITSTATUS(status);
|
||||||
}
|
|
||||||
|
|
||||||
if (WIFSIGNALED(status)) {
|
if (WIFSIGNALED(status))
|
||||||
term_signal = WTERMSIG(status);
|
term_signal = WTERMSIG(status);
|
||||||
}
|
|
||||||
|
|
||||||
if (process->exit_cb) {
|
if (process->errorno)
|
||||||
process->exit_cb(process, exit_status, term_signal);
|
uv__set_sys_error(process->loop, process->errorno);
|
||||||
}
|
|
||||||
|
process->exit_cb(process, exit_status, term_signal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -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,
|
static void uv__process_child_init(uv_process_options_t options,
|
||||||
int stdio_count,
|
int stdio_count,
|
||||||
int* pipes) {
|
int* pipes,
|
||||||
|
int error_fd) {
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (options.flags & UV_PROCESS_DETACHED) {
|
if (options.flags & UV_PROCESS_DETACHED) {
|
||||||
@ -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);
|
use_fd = open("/dev/null", i == 0 ? O_RDONLY : O_RDWR);
|
||||||
|
|
||||||
if (use_fd < 0) {
|
if (use_fd < 0) {
|
||||||
|
uv__write_int(error_fd, errno);
|
||||||
perror("failed to open stdio");
|
perror("failed to open stdio");
|
||||||
_exit(127);
|
_exit(127);
|
||||||
}
|
}
|
||||||
@ -243,16 +274,19 @@ static void uv__process_child_init(uv_process_options_t options,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (options.cwd && chdir(options.cwd)) {
|
if (options.cwd && chdir(options.cwd)) {
|
||||||
|
uv__write_int(error_fd, errno);
|
||||||
perror("chdir()");
|
perror("chdir()");
|
||||||
_exit(127);
|
_exit(127);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((options.flags & UV_PROCESS_SETGID) && setgid(options.gid)) {
|
if ((options.flags & UV_PROCESS_SETGID) && setgid(options.gid)) {
|
||||||
|
uv__write_int(error_fd, errno);
|
||||||
perror("setgid()");
|
perror("setgid()");
|
||||||
_exit(127);
|
_exit(127);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((options.flags & UV_PROCESS_SETUID) && setuid(options.uid)) {
|
if ((options.flags & UV_PROCESS_SETUID) && setuid(options.uid)) {
|
||||||
|
uv__write_int(error_fd, errno);
|
||||||
perror("setuid()");
|
perror("setuid()");
|
||||||
_exit(127);
|
_exit(127);
|
||||||
}
|
}
|
||||||
@ -260,6 +294,7 @@ static void uv__process_child_init(uv_process_options_t options,
|
|||||||
environ = options.env;
|
environ = options.env;
|
||||||
|
|
||||||
execvp(options.file, options.args);
|
execvp(options.file, options.args);
|
||||||
|
uv__write_int(error_fd, errno);
|
||||||
perror("execvp()");
|
perror("execvp()");
|
||||||
_exit(127);
|
_exit(127);
|
||||||
}
|
}
|
||||||
@ -277,9 +312,9 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process,
|
|||||||
int* pipes = malloc(2 * stdio_count * sizeof(int));
|
int* pipes = malloc(2 * stdio_count * sizeof(int));
|
||||||
int signal_pipe[2] = { -1, -1 };
|
int signal_pipe[2] = { -1, -1 };
|
||||||
struct pollfd pfd;
|
struct pollfd pfd;
|
||||||
int status;
|
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
int i;
|
int i;
|
||||||
|
int r;
|
||||||
|
|
||||||
if (pipes == NULL) {
|
if (pipes == NULL) {
|
||||||
errno = ENOMEM;
|
errno = ENOMEM;
|
||||||
@ -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);
|
uv__handle_init(loop, (uv_handle_t*)process, UV_PROCESS);
|
||||||
loop->counters.process_init++;
|
loop->counters.process_init++;
|
||||||
uv__handle_start(process);
|
|
||||||
|
|
||||||
process->exit_cb = options.exit_cb;
|
|
||||||
|
|
||||||
/* Init pipe pairs */
|
/* Init pipe pairs */
|
||||||
for (i = 0; i < stdio_count; i++) {
|
for (i = 0; i < stdio_count; i++) {
|
||||||
@ -345,35 +377,38 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
/* Child */
|
uv__process_child_init(options, stdio_count, pipes, signal_pipe[1]);
|
||||||
uv__process_child_init(options, stdio_count, pipes);
|
abort();
|
||||||
|
|
||||||
/* Execution never reaches here. */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Parent. */
|
|
||||||
|
|
||||||
/* Restore environment. */
|
/* Restore environment. */
|
||||||
environ = save_our_env;
|
environ = save_our_env;
|
||||||
|
|
||||||
/* POLLHUP signals child has exited or execve()'d. */
|
/* POLLHUP signals child has exited or execve()'d. */
|
||||||
close(signal_pipe[1]);
|
close(signal_pipe[1]);
|
||||||
do {
|
pfd.revents = 0;
|
||||||
pfd.fd = signal_pipe[0];
|
pfd.events = POLLIN|POLLHUP;
|
||||||
pfd.events = POLLIN|POLLHUP;
|
pfd.fd = signal_pipe[0];
|
||||||
pfd.revents = 0;
|
|
||||||
errno = 0, status = poll(&pfd, 1, -1);
|
do
|
||||||
}
|
r = poll(&pfd, 1, -1);
|
||||||
while (status == -1 && (errno == EINTR || errno == ENOMEM));
|
while (r == -1 && errno == EINTR);
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
assert((status == 1) && "poll() on pipe read end failed");
|
|
||||||
close(signal_pipe[0]);
|
close(signal_pipe[0]);
|
||||||
|
|
||||||
process->pid = pid;
|
|
||||||
|
|
||||||
ev_child_init(&process->child_watcher, uv__chld, pid, 0);
|
ev_child_init(&process->child_watcher, uv__chld, pid, 0);
|
||||||
ev_child_start(process->loop->ev, &process->child_watcher);
|
ev_child_start(process->loop->ev, &process->child_watcher);
|
||||||
process->child_watcher.data = process;
|
process->child_watcher.data = process;
|
||||||
|
process->exit_cb = options.exit_cb;
|
||||||
|
process->pid = pid;
|
||||||
|
|
||||||
for (i = 0; i < options.stdio_count; i++) {
|
for (i = 0; i < options.stdio_count; i++) {
|
||||||
if (uv__process_open_stream(&options.stdio[i], pipes + i * 2, i == 0)) {
|
if (uv__process_open_stream(&options.stdio[i], pipes + i * 2, i == 0)) {
|
||||||
@ -387,6 +422,7 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uv__handle_start(process);
|
||||||
free(pipes);
|
free(pipes);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@ -123,6 +123,7 @@ TEST_DECLARE (getsockname_tcp)
|
|||||||
TEST_DECLARE (getsockname_udp)
|
TEST_DECLARE (getsockname_udp)
|
||||||
TEST_DECLARE (fail_always)
|
TEST_DECLARE (fail_always)
|
||||||
TEST_DECLARE (pass_always)
|
TEST_DECLARE (pass_always)
|
||||||
|
TEST_DECLARE (spawn_fails)
|
||||||
TEST_DECLARE (spawn_exit_code)
|
TEST_DECLARE (spawn_exit_code)
|
||||||
TEST_DECLARE (spawn_stdout)
|
TEST_DECLARE (spawn_stdout)
|
||||||
TEST_DECLARE (spawn_stdin)
|
TEST_DECLARE (spawn_stdin)
|
||||||
@ -350,6 +351,7 @@ TASK_LIST_START
|
|||||||
TEST_ENTRY (poll_unidirectional)
|
TEST_ENTRY (poll_unidirectional)
|
||||||
TEST_ENTRY (poll_close)
|
TEST_ENTRY (poll_close)
|
||||||
|
|
||||||
|
TEST_ENTRY (spawn_fails)
|
||||||
TEST_ENTRY (spawn_exit_code)
|
TEST_ENTRY (spawn_exit_code)
|
||||||
TEST_ENTRY (spawn_stdout)
|
TEST_ENTRY (spawn_stdout)
|
||||||
TEST_ENTRY (spawn_stdin)
|
TEST_ENTRY (spawn_stdin)
|
||||||
|
|||||||
@ -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) {
|
TEST_IMPL(spawn_exit_code) {
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user