Use waitpid code to learn when a controlled process dies

This lets us avoid sending SIGTERM to something that has already
died, since we realize it has already died, and is a fix for the
unix version of #8746.
This commit is contained in:
Nick Mathewson 2014-04-10 11:06:10 -04:00
parent 4ed03965a5
commit f8344c2d28
2 changed files with 51 additions and 4 deletions

View File

@ -26,6 +26,7 @@
#include "address.h"
#include "sandbox.h"
#include "backtrace.h"
#include "util_process.h"
#ifdef _WIN32
#include <io.h>
@ -3642,7 +3643,10 @@ tor_terminate_process(process_handle_t *process_handle)
return 0;
}
#else /* Unix */
if (process_handle->waitpid_cb) {
/* We haven't got a waitpid yet, so we can just kill off the process. */
return kill(process_handle->pid, SIGTERM);
}
#endif
return -1;
@ -3691,6 +3695,23 @@ process_handle_new(void)
return out;
}
#ifndef _WIN32
/** Invoked when a process that we've launched via tor_spawn_background() has
* been found to have terminated.
*/
static void
process_handle_waitpid_cb(int status, void *arg)
{
process_handle_t *process_handle = arg;
process_handle->waitpid_exit_status = status;
clear_waitpid_callback(process_handle->waitpid_cb);
if (process_handle->status == PROCESS_STATUS_RUNNING)
process_handle->status = PROCESS_STATUS_NOTRUNNING;
process_handle->waitpid_cb = 0;
}
#endif
/**
* @name child-process states
*
@ -4007,6 +4028,10 @@ tor_spawn_background(const char *const filename, const char **argv,
strerror(errno));
}
process_handle->waitpid_cb = set_waitpid_callback(pid,
process_handle_waitpid_cb,
process_handle);
process_handle->stderr_pipe = stderr_pipe[0];
retval = close(stderr_pipe[1]);
@ -4071,6 +4096,8 @@ tor_process_handle_destroy,(process_handle_t *process_handle,
if (process_handle->stderr_handle)
fclose(process_handle->stderr_handle);
clear_waitpid_callback(process_handle->waitpid_cb);
#endif
memset(process_handle, 0x0f, sizeof(process_handle_t));
@ -4088,7 +4115,7 @@ tor_process_handle_destroy,(process_handle_t *process_handle,
* probably not work in Tor, because waitpid() is called in main.c to reap any
* terminated child processes.*/
int
tor_get_exit_code(const process_handle_t *process_handle,
tor_get_exit_code(process_handle_t *process_handle,
int block, int *exit_code)
{
#ifdef _WIN32
@ -4128,7 +4155,20 @@ tor_get_exit_code(const process_handle_t *process_handle,
int stat_loc;
int retval;
if (process_handle->waitpid_cb) {
/* We haven't processed a SIGCHLD yet. */
retval = waitpid(process_handle->pid, &stat_loc, block?0:WNOHANG);
if (retval == process_handle->pid) {
clear_waitpid_callback(process_handle->waitpid_cb);
process_handle->waitpid_cb = NULL;
process_handle->waitpid_exit_status = stat_loc;
}
} else {
/* We already got a SIGCHLD for this process, and handled it. */
retval = process_handle->pid;
stat_loc = process_handle->waitpid_exit_status;
}
if (!block && 0 == retval) {
/* Process has not exited */
return PROCESS_EXIT_RUNNING;

View File

@ -446,6 +446,7 @@ void set_environment_variable_in_smartlist(struct smartlist_t *env_vars,
#define PROCESS_STATUS_ERROR -1
#ifdef UTIL_PRIVATE
struct waitpid_callback_t;
/** Structure to represent the state of a process with which Tor is
* communicating. The contents of this structure are private to util.c */
struct process_handle_t {
@ -461,6 +462,12 @@ struct process_handle_t {
FILE *stdout_handle;
FILE *stderr_handle;
pid_t pid;
/** If the process has not given us a SIGCHLD yet, this has the
* waitpid_callback_t that gets invoked once it has. Otherwise this
* contains NULL. */
struct waitpid_callback_t *waitpid_cb;
/** The exit status reported by waitpid. */
int waitpid_exit_status;
#endif // _WIN32
};
#endif
@ -469,7 +476,7 @@ struct process_handle_t {
#define PROCESS_EXIT_RUNNING 1
#define PROCESS_EXIT_EXITED 0
#define PROCESS_EXIT_ERROR -1
int tor_get_exit_code(const process_handle_t *process_handle,
int tor_get_exit_code(process_handle_t *process_handle,
int block, int *exit_code);
int tor_split_lines(struct smartlist_t *sl, char *buf, int len);
#ifdef _WIN32