unix: reap child on execvp() failure

Reap the forked process when execvp() fails.  Fixes leaking zombie child
processes.

PR-URL: https://github.com/libuv/libuv/pull/274
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Saúl Ibarra Corretgé <saghul@gmail.com>
This commit is contained in:
Ryan Phillips 2015-03-17 10:01:53 -05:00 committed by Ben Noordhuis
parent 252f3624c2
commit 3616e61439
3 changed files with 51 additions and 5 deletions

View File

@ -367,6 +367,7 @@ int uv_spawn(uv_loop_t* loop,
int err;
int exec_errorno;
int i;
int status;
assert(options->file != NULL);
assert(!(options->flags & ~(UV_PROCESS_DETACHED |
@ -453,11 +454,17 @@ int uv_spawn(uv_loop_t* loop,
if (r == 0)
; /* okay, EOF */
else if (r == sizeof(exec_errorno))
; /* okay, read errorno */
else if (r == -1 && errno == EPIPE)
; /* okay, got EPIPE */
else
else if (r == sizeof(exec_errorno)) {
do
err = waitpid(pid, &status, 0); /* okay, read errorno */
while (err == -1 && errno == EINTR);
assert(err == pid);
} else if (r == -1 && errno == EPIPE) {
do
err = waitpid(pid, &status, 0); /* okay, got EPIPE */
while (err == -1 && errno == EINTR);
assert(err == pid);
} else
abort();
uv__close(signal_pipe[0]);

View File

@ -196,6 +196,9 @@ TEST_DECLARE (fail_always)
TEST_DECLARE (pass_always)
TEST_DECLARE (socket_buffer_size)
TEST_DECLARE (spawn_fails)
#ifndef _WIN32
TEST_DECLARE (spawn_fails_check_for_waitpid_cleanup)
#endif
TEST_DECLARE (spawn_exit_code)
TEST_DECLARE (spawn_stdout)
TEST_DECLARE (spawn_stdin)
@ -553,6 +556,9 @@ TASK_LIST_START
TEST_ENTRY (socket_buffer_size)
TEST_ENTRY (spawn_fails)
#ifndef _WIN32
TEST_ENTRY (spawn_fails_check_for_waitpid_cleanup)
#endif
TEST_ENTRY (spawn_exit_code)
TEST_ENTRY (spawn_stdout)
TEST_ENTRY (spawn_stdin)

View File

@ -21,6 +21,7 @@
#include "uv.h"
#include "task.h"
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
@ -34,6 +35,7 @@
# include <wchar.h>
#else
# include <unistd.h>
# include <sys/wait.h>
#endif
@ -180,6 +182,37 @@ TEST_IMPL(spawn_fails) {
}
#ifndef _WIN32
TEST_IMPL(spawn_fails_check_for_waitpid_cleanup) {
int r;
int status;
int err;
init_process_options("", fail_cb);
options.file = options.args[0] = "program-that-had-better-not-exist";
r = uv_spawn(uv_default_loop(), &process, &options);
ASSERT(r == UV_ENOENT || r == UV_EACCES);
ASSERT(0 == uv_is_active((uv_handle_t*) &process));
ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT));
/* verify the child is successfully cleaned up within libuv */
do
err = waitpid(process.pid, &status, 0);
while (err == -1 && errno == EINTR);
ASSERT(err == -1);
ASSERT(errno == ECHILD);
uv_close((uv_handle_t*) &process, NULL);
ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT));
MAKE_VALGRIND_HAPPY();
return 0;
}
#endif
TEST_IMPL(spawn_exit_code) {
int r;