Merge branch 'ticket28179_squashed' into ticket28179_squashed_merged

This commit is contained in:
Nick Mathewson
2018-12-17 16:41:01 -05:00
38 changed files with 4320 additions and 2348 deletions
+3 -2
View File
@@ -4,8 +4,9 @@ lib/cc/*.h
lib/container/*.h
lib/ctime/*.h
lib/err/*.h
lib/intmath/*.h
lib/evloop/*.h
lib/fs/*.h
lib/intmath/*.h
lib/log/*.h
lib/malloc/*.h
lib/net/*.h
@@ -15,4 +16,4 @@ lib/subsys/*.h
lib/testsupport/*.h
lib/thread/*.h
ext/ht.h
ext/ht.h
+6 -2
View File
@@ -9,9 +9,11 @@ src_lib_libtor_process_a_SOURCES = \
src/lib/process/daemon.c \
src/lib/process/env.c \
src/lib/process/pidfile.c \
src/lib/process/process.c \
src/lib/process/process_unix.c \
src/lib/process/process_win32.c \
src/lib/process/restrict.c \
src/lib/process/setuid.c \
src/lib/process/subprocess.c \
src/lib/process/waitpid.c \
src/lib/process/winprocess_sys.c
@@ -24,8 +26,10 @@ noinst_HEADERS += \
src/lib/process/daemon.h \
src/lib/process/env.h \
src/lib/process/pidfile.h \
src/lib/process/process.h \
src/lib/process/process_unix.h \
src/lib/process/process_win32.h \
src/lib/process/restrict.h \
src/lib/process/setuid.h \
src/lib/process/subprocess.h \
src/lib/process/waitpid.h \
src/lib/process/winprocess_sys.h
+797
View File
@@ -0,0 +1,797 @@
/* Copyright (c) 2003, Roger Dingledine
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2018, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file process.c
* \brief Module for working with other processes.
**/
#define PROCESS_PRIVATE
#include "lib/container/buffers.h"
#include "lib/net/buffers_net.h"
#include "lib/container/smartlist.h"
#include "lib/log/log.h"
#include "lib/log/util_bug.h"
#include "lib/process/process.h"
#include "lib/process/process_unix.h"
#include "lib/process/process_win32.h"
#include "lib/process/env.h"
#ifdef HAVE_STDDEF_H
#include <stddef.h>
#endif
/** A list of all <b>process_t</b> instances currently allocated. */
static smartlist_t *processes;
/**
* Boolean. If true, then Tor may call execve or CreateProcess via
* tor_spawn_background.
**/
static int may_spawn_background_process = 1;
/** Structure to represent a child process. */
struct process_t {
/** Process status. */
process_status_t status;
/** Which protocol is the process using? */
process_protocol_t protocol;
/** Which function to call when we have data ready from stdout? */
process_read_callback_t stdout_read_callback;
/** Which function to call when we have data ready from stderr? */
process_read_callback_t stderr_read_callback;
/** Which function call when our process terminated? */
process_exit_callback_t exit_callback;
/** Our exit code when the process have terminated. */
process_exit_code_t exit_code;
/** Name of the command we want to execute (for example: /bin/ls). */
char *command;
/** The arguments used for the new process. The format here is one argument
* per element of the smartlist_t. On Windows these arguments are combined
* together using the <b>tor_join_win_cmdline</b> function. On Unix the
* process name (argv[0]) and the trailing NULL is added automatically before
* the process is executed. */
smartlist_t *arguments;
/** The environment used for the new process. */
smartlist_t *environment;
/** Buffer to store data from stdout when it is read. */
buf_t *stdout_buffer;
/** Buffer to store data from stderr when it is read. */
buf_t *stderr_buffer;
/** Buffer to store data to stdin before it is written. */
buf_t *stdin_buffer;
/** Do we need to store some custom data with the process? */
void *data;
#ifndef _WIN32
/** Our Unix process handle. */
process_unix_t *unix_process;
#else
/** Our Win32 process handle. */
process_win32_t *win32_process;
#endif
};
/** Convert a given process status in <b>status</b> to its string
* representation. */
const char *
process_status_to_string(process_status_t status)
{
switch (status) {
case PROCESS_STATUS_NOT_RUNNING:
return "not running";
case PROCESS_STATUS_RUNNING:
return "running";
case PROCESS_STATUS_ERROR:
return "error";
}
/* LCOV_EXCL_START */
tor_assert_unreached();
return NULL;
/* LCOV_EXCL_STOP */
}
/** Convert a given process protocol in <b>protocol</b> to its string
* representation. */
const char *
process_protocol_to_string(process_protocol_t protocol)
{
switch (protocol) {
case PROCESS_PROTOCOL_LINE:
return "Line";
case PROCESS_PROTOCOL_RAW:
return "Raw";
}
/* LCOV_EXCL_START */
tor_assert_unreached();
return NULL;
/* LCOV_EXCL_STOP */
}
/**
* Turn off may_spawn_background_process, so that all future calls to
* tor_spawn_background are guaranteed to fail.
**/
void
tor_disable_spawning_background_processes(void)
{
may_spawn_background_process = 0;
}
/** Initialize the Process subsystem. This function initializes the Process
* subsystem's global state. For cleaning up, <b>process_free_all()</b> should
* be called. */
void
process_init(void)
{
processes = smartlist_new();
#ifdef _WIN32
process_win32_init();
#endif
}
/** Free up all resources that is handled by the Process subsystem. Note that
* this call does not terminate already running processes. */
void
process_free_all(void)
{
#ifdef _WIN32
process_win32_deinit();
#endif
SMARTLIST_FOREACH(processes, process_t *, x, process_free(x));
smartlist_free(processes);
}
/** Get a list of all processes. This function returns a smartlist of
* <b>process_t</b> containing all the currently allocated processes. */
const smartlist_t *
process_get_all_processes(void)
{
return processes;
}
/** Allocate and initialize a new process. This function returns a newly
* allocated and initialized process data, which can be used to configure and
* later run a subprocess of Tor. Use the various <b>process_set_*()</b>
* methods to configure it and run the process using <b>process_exec()</b>. Use
* <b>command</b> to specify the path to the command to run. You can either
* specify an absolute path to the command or relative where Tor will use the
* underlying operating system's functionality for finding the command to run.
* */
process_t *
process_new(const char *command)
{
tor_assert(command);
process_t *process;
process = tor_malloc_zero(sizeof(process_t));
/* Set our command. */
process->command = tor_strdup(command);
/* By default we are not running. */
process->status = PROCESS_STATUS_NOT_RUNNING;
/* Prepare process environment. */
process->arguments = smartlist_new();
process->environment = smartlist_new();
/* Prepare the buffers. */
process->stdout_buffer = buf_new();
process->stderr_buffer = buf_new();
process->stdin_buffer = buf_new();
#ifndef _WIN32
/* Prepare our Unix process handle. */
process->unix_process = process_unix_new();
#else
/* Prepare our Win32 process handle. */
process->win32_process = process_win32_new();
#endif
smartlist_add(processes, process);
return process;
}
/** Deallocate the given process in <b>process</b>. */
void
process_free_(process_t *process)
{
if (! process)
return;
/* Cleanup parameters. */
tor_free(process->command);
/* Cleanup arguments and environment. */
SMARTLIST_FOREACH(process->arguments, char *, x, tor_free(x));
smartlist_free(process->arguments);
SMARTLIST_FOREACH(process->environment, char *, x, tor_free(x));
smartlist_free(process->environment);
/* Cleanup the buffers. */
buf_free(process->stdout_buffer);
buf_free(process->stderr_buffer);
buf_free(process->stdin_buffer);
#ifndef _WIN32
/* Cleanup our Unix process handle. */
process_unix_free(process->unix_process);
#else
/* Cleanup our Win32 process handle. */
process_win32_free(process->win32_process);
#endif
smartlist_remove(processes, process);
tor_free(process);
}
/** Execute the given process. This function executes the given process as a
* subprocess of Tor. Returns <b>PROCESS_STATUS_RUNNING</b> upon success. */
process_status_t
process_exec(process_t *process)
{
tor_assert(process);
if (BUG(may_spawn_background_process == 0))
return PROCESS_STATUS_ERROR;
process_status_t status = PROCESS_STATUS_NOT_RUNNING;
log_info(LD_PROCESS, "Starting new process: %s", process->command);
#ifndef _WIN32
status = process_unix_exec(process);
#else
status = process_win32_exec(process);
#endif
/* Update our state. */
process_set_status(process, status);
if (status != PROCESS_STATUS_RUNNING) {
log_warn(LD_PROCESS, "Failed to start process: %s",
process_get_command(process));
}
return status;
}
/** Terminate the given process. Returns true on success,
* otherwise false. */
bool
process_terminate(process_t *process)
{
tor_assert(process);
/* Terminating a non-running process isn't going to work. */
if (process_get_status(process) != PROCESS_STATUS_RUNNING)
return false;
log_debug(LD_PROCESS, "Terminating process");
#ifndef _WIN32
return process_unix_terminate(process);
#else
return process_win32_terminate(process);
#endif
}
/** Returns the unique process identifier for the given <b>process</b>. */
process_pid_t
process_get_pid(process_t *process)
{
tor_assert(process);
#ifndef _WIN32
return process_unix_get_pid(process);
#else
return process_win32_get_pid(process);
#endif
}
/** Set the callback function for output from the child process's standard out
* handle. This function sets the callback function which is called every time
* the child process have written output to its standard out file handle.
*
* Use <b>process_set_protocol(process, PROCESS_PROTOCOL_LINE)</b> if you want
* the callback to only contain complete "\n" or "\r\n" terminated lines. */
void
process_set_stdout_read_callback(process_t *process,
process_read_callback_t callback)
{
tor_assert(process);
process->stdout_read_callback = callback;
}
/** Set the callback function for output from the child process's standard
* error handle. This function sets the callback function which is called
* every time the child process have written output to its standard error file
* handle.
*
* Use <b>process_set_protocol(process, PROCESS_PROTOCOL_LINE)</b> if you want
* the callback to only contain complete "\n" or "\r\n" terminated lines. */
void
process_set_stderr_read_callback(process_t *process,
process_read_callback_t callback)
{
tor_assert(process);
process->stderr_read_callback = callback;
}
/** Set the callback function for process exit notification. The
* <b>callback</b> function will be called every time your child process have
* terminated. */
void
process_set_exit_callback(process_t *process,
process_exit_callback_t callback)
{
tor_assert(process);
process->exit_callback = callback;
}
/** Get the current command of the given process. */
const char *
process_get_command(const process_t *process)
{
tor_assert(process);
return process->command;
}
void
process_set_protocol(process_t *process, process_protocol_t protocol)
{
tor_assert(process);
process->protocol = protocol;
}
/** Get the currently used protocol of the given process. */
process_protocol_t
process_get_protocol(const process_t *process)
{
tor_assert(process);
return process->protocol;
}
/** Set opague pointer to data. This function allows you to store a pointer to
* your own data in the given process. Use <b>process_get_data()</b> in the
* various callback functions to retrieve the data again.
*
* Note that the given process does NOT take ownership of the data and you are
* responsible for freeing up any resources allocated by the given data.
* */
void
process_set_data(process_t *process, void *data)
{
tor_assert(process);
process->data = data;
}
/** Get the opaque pointer to callback data from the given process. This
* function allows you get the data you stored with <b>process_set_data()</b>
* in the different callback functions. */
void *
process_get_data(const process_t *process)
{
tor_assert(process);
return process->data;
}
/** Set the status of a given process. */
void
process_set_status(process_t *process, process_status_t status)
{
tor_assert(process);
process->status = status;
}
/** Get the status of the given process. */
process_status_t
process_get_status(const process_t *process)
{
tor_assert(process);
return process->status;
}
/** Append an argument to the list of arguments in the given process. */
void
process_append_argument(process_t *process, const char *argument)
{
tor_assert(process);
tor_assert(argument);
smartlist_add(process->arguments, tor_strdup(argument));
}
/** Returns a list of arguments (excluding the command itself) from the
* given process. */
const smartlist_t *
process_get_arguments(const process_t *process)
{
tor_assert(process);
return process->arguments;
}
/** Returns a newly allocated Unix style argument vector. Use <b>tor_free()</b>
* to deallocate it after use. */
char **
process_get_argv(const process_t *process)
{
tor_assert(process);
/** Generate a Unix style process argument vector from our process's
* arguments smartlist_t. */
char **argv = NULL;
char *filename = process->command;
const smartlist_t *arguments = process->arguments;
const size_t size = smartlist_len(arguments);
/* Make space for the process filename as argv[0] and a trailing NULL. */
argv = tor_malloc_zero(sizeof(char *) * (size + 2));
/* Set our filename as first argument. */
argv[0] = filename;
/* Put in the rest of the values from arguments. */
SMARTLIST_FOREACH_BEGIN(arguments, char *, arg_val) {
tor_assert(arg_val != NULL);
argv[arg_val_sl_idx + 1] = arg_val;
} SMARTLIST_FOREACH_END(arg_val);
return argv;
}
/** This function clears the internal environment and copies over every string
* from <b>env</b> as the new environment. */
void
process_reset_environment(process_t *process, const smartlist_t *env)
{
tor_assert(process);
tor_assert(env);
/* Cleanup old environment. */
SMARTLIST_FOREACH(process->environment, char *, x, tor_free(x));
smartlist_free(process->environment);
process->environment = smartlist_new();
SMARTLIST_FOREACH(env, char *, x,
smartlist_add(process->environment, tor_strdup(x)));
}
/** Set the given <b>key</b>/<b>value</b> pair as environment variable in the
* given process. */
void
process_set_environment(process_t *process,
const char *key,
const char *value)
{
tor_assert(process);
tor_assert(key);
tor_assert(value);
smartlist_add_asprintf(process->environment, "%s=%s", key, value);
}
/** Returns a newly allocated <b>process_environment_t</b> containing the
* environment variables for the given process. */
process_environment_t *
process_get_environment(const process_t *process)
{
tor_assert(process);
return process_environment_make(process->environment);
}
#ifndef _WIN32
/** Get the internal handle for the Unix backend. */
process_unix_t *
process_get_unix_process(const process_t *process)
{
tor_assert(process);
tor_assert(process->unix_process);
return process->unix_process;
}
#else
/** Get the internal handle for Windows backend. */
process_win32_t *
process_get_win32_process(const process_t *process)
{
tor_assert(process);
tor_assert(process->win32_process);
return process->win32_process;
}
#endif
/** Write <b>size</b> bytes of <b>data</b> to the given process's standard
* input. */
void
process_write(process_t *process,
const uint8_t *data, size_t size)
{
tor_assert(process);
tor_assert(data);
buf_add(process->stdin_buffer, (char *)data, size);
process_write_stdin(process, process->stdin_buffer);
}
/** As tor_vsnprintf(), but write the data to the given process's standard
* input. */
void
process_vprintf(process_t *process,
const char *format, va_list args)
{
tor_assert(process);
tor_assert(format);
int size;
char *data;
size = tor_vasprintf(&data, format, args);
process_write(process, (uint8_t *)data, size);
tor_free(data);
}
/** As tor_snprintf(), but write the data to the given process's standard
* input. */
void
process_printf(process_t *process,
const char *format, ...)
{
tor_assert(process);
tor_assert(format);
va_list ap;
va_start(ap, format);
process_vprintf(process, format, ap);
va_end(ap);
}
/** This function is called by the Process backend when a given process have
* data that is ready to be read from the child process's standard output
* handle. */
void
process_notify_event_stdout(process_t *process)
{
tor_assert(process);
int ret;
ret = process_read_stdout(process, process->stdout_buffer);
if (ret > 0)
process_read_data(process,
process->stdout_buffer,
process->stdout_read_callback);
}
/** This function is called by the Process backend when a given process have
* data that is ready to be read from the child process's standard error
* handle. */
void
process_notify_event_stderr(process_t *process)
{
tor_assert(process);
int ret;
ret = process_read_stderr(process, process->stderr_buffer);
if (ret > 0)
process_read_data(process,
process->stderr_buffer,
process->stderr_read_callback);
}
/** This function is called by the Process backend when a given process is
* allowed to begin writing data to the standard input of the child process. */
void
process_notify_event_stdin(process_t *process)
{
tor_assert(process);
process_write_stdin(process, process->stdin_buffer);
}
/** This function is called by the Process backend when a given process have
* terminated. The exit status code is passed in <b>exit_code</b>. We mark the
* process as no longer running and calls the <b>exit_callback</b> with
* information about the process termination. The given <b>process</b> is
* free'd iff the exit_callback returns true. */
void
process_notify_event_exit(process_t *process, process_exit_code_t exit_code)
{
tor_assert(process);
log_debug(LD_PROCESS,
"Process terminated with exit code: %"PRIu64, exit_code);
/* Update our state. */
process_set_status(process, PROCESS_STATUS_NOT_RUNNING);
process->exit_code = exit_code;
/* Call our exit callback, if it exists. */
bool free_process_handle = false;
/* The exit callback will tell us if we should process_free() our handle. */
if (process->exit_callback)
free_process_handle = process->exit_callback(process, exit_code);
if (free_process_handle)
process_free(process);
}
/** This function is called whenever the Process backend have notified us that
* there is data to be read from its standard out handle. Returns the number of
* bytes that have been put into the given buffer. */
MOCK_IMPL(STATIC int, process_read_stdout, (process_t *process, buf_t *buffer))
{
tor_assert(process);
tor_assert(buffer);
#ifndef _WIN32
return process_unix_read_stdout(process, buffer);
#else
return process_win32_read_stdout(process, buffer);
#endif
}
/** This function is called whenever the Process backend have notified us that
* there is data to be read from its standard error handle. Returns the number
* of bytes that have been put into the given buffer. */
MOCK_IMPL(STATIC int, process_read_stderr, (process_t *process, buf_t *buffer))
{
tor_assert(process);
tor_assert(buffer);
#ifndef _WIN32
return process_unix_read_stderr(process, buffer);
#else
return process_win32_read_stderr(process, buffer);
#endif
}
/** This function calls the backend function for the given process whenever
* there is data to be written to the backends' file handles. */
MOCK_IMPL(STATIC void, process_write_stdin,
(process_t *process, buf_t *buffer))
{
tor_assert(process);
tor_assert(buffer);
#ifndef _WIN32
process_unix_write(process, buffer);
#else
process_win32_write(process, buffer);
#endif
}
/** This function calls the protocol handlers based on the value of
* <b>process_get_protocol(process)</b>. Currently we call
* <b>process_read_buffer()</b> for <b>PROCESS_PROTOCOL_RAW</b> and
* <b>process_read_lines()</b> for <b>PROCESS_PROTOCOL_LINE</b>. */
STATIC void
process_read_data(process_t *process,
buf_t *buffer,
process_read_callback_t callback)
{
tor_assert(process);
tor_assert(buffer);
switch (process_get_protocol(process)) {
case PROCESS_PROTOCOL_RAW:
process_read_buffer(process, buffer, callback);
break;
case PROCESS_PROTOCOL_LINE:
process_read_lines(process, buffer, callback);
break;
default:
/* LCOV_EXCL_START */
tor_assert_unreached();
return;
/* LCOV_EXCL_STOP */
}
}
/** This function takes the content of the given <b>buffer</b> and passes it to
* the given <b>callback</b> function, but ensures that an additional zero byte
* is added to the end of the data such that the given callback implementation
* can threat the content as a ASCIIZ string. */
STATIC void
process_read_buffer(process_t *process,
buf_t *buffer,
process_read_callback_t callback)
{
tor_assert(process);
tor_assert(buffer);
const size_t size = buf_datalen(buffer);
/* We allocate an extra byte for the zero byte in the end. */
char *data = tor_malloc_zero(size + 1);
buf_get_bytes(buffer, data, size);
log_debug(LD_PROCESS, "Read data from process");
if (callback)
callback(process, data, size);
tor_free(data);
}
/** This function tries to extract complete lines from the given <b>buffer</b>
* and calls the given <b>callback</b> function whenever it has a complete
* line. Before calling <b>callback</b> we remove the trailing "\n" or "\r\n"
* from the line. If we are unable to extract a complete line we leave the data
* in the buffer for next call. */
STATIC void
process_read_lines(process_t *process,
buf_t *buffer,
process_read_callback_t callback)
{
tor_assert(process);
tor_assert(buffer);
const size_t size = buf_datalen(buffer) + 1;
size_t line_size = 0;
char *data = tor_malloc_zero(size);
int ret;
while (true) {
line_size = size;
ret = buf_get_line(buffer, data, &line_size);
/* A complete line should always be smaller than the size of our
* buffer. */
tor_assert(ret != -1);
/* Remove \n from the end of the line. */
if (line_size >= 1 && data[line_size - 1] == '\n') {
data[line_size - 1] = '\0';
--line_size;
}
/* Remove \r from the end of the line. */
if (line_size >= 1 && data[line_size - 1] == '\r') {
data[line_size - 1] = '\0';
--line_size;
}
if (ret == 1) {
log_debug(LD_PROCESS, "Read line from process: \"%s\"", data);
if (callback)
callback(process, data, line_size);
/* We have read a whole line, let's see if there is more lines to read.
* */
continue;
}
/* No complete line for us to read. We are done for now. */
tor_assert_nonfatal(ret == 0);
break;
}
tor_free(data);
}
+142
View File
@@ -0,0 +1,142 @@
/* Copyright (c) 2003-2004, Roger Dingledine
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2018, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file process.h
* \brief Header for process.c
**/
#ifndef TOR_PROCESS_H
#define TOR_PROCESS_H
#include "orconfig.h"
#include "lib/malloc/malloc.h"
#include "lib/string/printf.h"
/** Maximum number of bytes to write to a process' stdin. */
#define PROCESS_MAX_WRITE (1024)
/** Maximum number of bytes to read from a process' stdout/stderr. */
#define PROCESS_MAX_READ (1024)
typedef enum {
/** The process is not running. */
PROCESS_STATUS_NOT_RUNNING,
/** The process is running. */
PROCESS_STATUS_RUNNING,
/** The process is in an erroneous state. */
PROCESS_STATUS_ERROR
} process_status_t;
const char *process_status_to_string(process_status_t status);
typedef enum {
/** Pass complete \n-terminated lines to the
* callback (with the \n or \r\n removed). */
PROCESS_PROTOCOL_LINE,
/** Pass the raw response from read() to the callback. */
PROCESS_PROTOCOL_RAW
} process_protocol_t;
const char *process_protocol_to_string(process_protocol_t protocol);
void tor_disable_spawning_background_processes(void);
struct process_t;
typedef struct process_t process_t;
typedef uint64_t process_exit_code_t;
typedef uint64_t process_pid_t;
typedef void (*process_read_callback_t)(process_t *,
const char *,
size_t);
typedef bool
(*process_exit_callback_t)(process_t *, process_exit_code_t);
void process_init(void);
void process_free_all(void);
const smartlist_t *process_get_all_processes(void);
process_t *process_new(const char *command);
void process_free_(process_t *process);
#define process_free(s) FREE_AND_NULL(process_t, process_free_, (s))
process_status_t process_exec(process_t *process);
bool process_terminate(process_t *process);
process_pid_t process_get_pid(process_t *process);
void process_set_stdout_read_callback(process_t *,
process_read_callback_t);
void process_set_stderr_read_callback(process_t *,
process_read_callback_t);
void process_set_exit_callback(process_t *,
process_exit_callback_t);
const char *process_get_command(const process_t *process);
void process_append_argument(process_t *process, const char *argument);
const smartlist_t *process_get_arguments(const process_t *process);
char **process_get_argv(const process_t *process);
void process_reset_environment(process_t *process, const smartlist_t *env);
void process_set_environment(process_t *process,
const char *key,
const char *value);
struct process_environment_t;
struct process_environment_t *process_get_environment(const process_t *);
void process_set_protocol(process_t *process, process_protocol_t protocol);
process_protocol_t process_get_protocol(const process_t *process);
void process_set_data(process_t *process, void *data);
void *process_get_data(const process_t *process);
void process_set_status(process_t *process, process_status_t status);
process_status_t process_get_status(const process_t *process);
#ifndef _WIN32
struct process_unix_t;
struct process_unix_t *process_get_unix_process(const process_t *process);
#else
struct process_win32_t;
struct process_win32_t *process_get_win32_process(const process_t *process);
#endif
void process_write(process_t *process,
const uint8_t *data, size_t size);
void process_vprintf(process_t *process,
const char *format, va_list args) CHECK_PRINTF(2, 0);
void process_printf(process_t *process,
const char *format, ...) CHECK_PRINTF(2, 3);
void process_notify_event_stdout(process_t *process);
void process_notify_event_stderr(process_t *process);
void process_notify_event_stdin(process_t *process);
void process_notify_event_exit(process_t *process,
process_exit_code_t);
#ifdef PROCESS_PRIVATE
MOCK_DECL(STATIC int, process_read_stdout, (process_t *, buf_t *));
MOCK_DECL(STATIC int, process_read_stderr, (process_t *, buf_t *));
MOCK_DECL(STATIC void, process_write_stdin, (process_t *, buf_t *));
STATIC void process_read_data(process_t *process,
buf_t *buffer,
process_read_callback_t callback);
STATIC void process_read_buffer(process_t *process,
buf_t *buffer,
process_read_callback_t callback);
STATIC void process_read_lines(process_t *process,
buf_t *buffer,
process_read_callback_t callback);
#endif /* defined(PROCESS_PRIVATE). */
#endif /* defined(TOR_PROCESS_H). */
+704
View File
@@ -0,0 +1,704 @@
/* Copyright (c) 2003, Roger Dingledine
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2018, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file process_unix.c
* \brief Module for working with Unix processes.
**/
#define PROCESS_UNIX_PRIVATE
#include "lib/intmath/cmp.h"
#include "lib/container/buffers.h"
#include "lib/net/buffers_net.h"
#include "lib/container/smartlist.h"
#include "lib/evloop/compat_libevent.h"
#include "lib/log/log.h"
#include "lib/log/util_bug.h"
#include "lib/process/process.h"
#include "lib/process/process_unix.h"
#include "lib/process/waitpid.h"
#include "lib/process/env.h"
#include <stdio.h>
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#if defined(HAVE_SYS_PRCTL_H) && defined(__linux__)
#include <sys/prctl.h>
#endif
#if HAVE_SIGNAL_H
#include <signal.h>
#endif
#ifndef _WIN32
/** Maximum number of file descriptors, if we cannot get it via sysconf() */
#define DEFAULT_MAX_FD 256
/** Internal state for Unix handles. */
struct process_unix_handle_t {
/** Unix File Descriptor. */
int fd;
/** Have we reached end of file? */
bool reached_eof;
/** Event structure for libevent. */
struct event *event;
/** Are we writing? */
bool is_writing;
};
/** Internal state for our Unix process. */
struct process_unix_t {
/** Standard in handle. */
process_unix_handle_t stdin_handle;
/** Standard out handle. */
process_unix_handle_t stdout_handle;
/** Standard error handle. */
process_unix_handle_t stderr_handle;
/** The process identifier of our process. */
pid_t pid;
/** Waitpid Callback structure. */
waitpid_callback_t *waitpid;
};
/** Returns a newly allocated <b>process_unix_t</b>. */
process_unix_t *
process_unix_new(void)
{
process_unix_t *unix_process;
unix_process = tor_malloc_zero(sizeof(process_unix_t));
unix_process->stdin_handle.fd = -1;
unix_process->stderr_handle.fd = -1;
unix_process->stdout_handle.fd = -1;
return unix_process;
}
/** Deallocates the given <b>unix_process</b>. */
void
process_unix_free_(process_unix_t *unix_process)
{
if (! unix_process)
return;
/* Clean up our waitpid callback. */
clear_waitpid_callback(unix_process->waitpid);
/* FIXME(ahf): Refactor waitpid code? */
unix_process->waitpid = NULL;
/* Cleanup our events. */
if (! unix_process->stdout_handle.reached_eof)
process_unix_stop_reading(&unix_process->stdout_handle);
if (! unix_process->stderr_handle.reached_eof)
process_unix_stop_reading(&unix_process->stderr_handle);
if (unix_process->stdin_handle.is_writing)
process_unix_stop_writing(&unix_process->stdin_handle);
/* Close all our file descriptors. */
process_unix_close_file_descriptors(unix_process);
tor_event_free(unix_process->stdout_handle.event);
tor_event_free(unix_process->stderr_handle.event);
tor_event_free(unix_process->stdin_handle.event);
tor_free(unix_process);
}
/** Executes the given process as a child process of Tor. This function is
* responsible for setting up the child process and run it. This includes
* setting up pipes for interprocess communication, initialize the waitpid
* callbacks, and finally run fork() followed by execve(). Returns
* <b>PROCESS_STATUS_RUNNING</b> upon success. */
process_status_t
process_unix_exec(process_t *process)
{
static int max_fd = -1;
process_unix_t *unix_process;
pid_t pid;
int stdin_pipe[2];
int stdout_pipe[2];
int stderr_pipe[2];
int retval, fd;
unix_process = process_get_unix_process(process);
/* Create standard in pipe. */
retval = pipe(stdin_pipe);
if (-1 == retval) {
log_warn(LD_PROCESS,
"Unable to create pipe for stdin "
"communication with process: %s",
strerror(errno));
return PROCESS_STATUS_ERROR;
}
/* Create standard out pipe. */
retval = pipe(stdout_pipe);
if (-1 == retval) {
log_warn(LD_PROCESS,
"Unable to create pipe for stdout "
"communication with process: %s",
strerror(errno));
/** Cleanup standard in pipe. */
close(stdin_pipe[0]);
close(stdin_pipe[1]);
return PROCESS_STATUS_ERROR;
}
/* Create standard error pipe. */
retval = pipe(stderr_pipe);
if (-1 == retval) {
log_warn(LD_PROCESS,
"Unable to create pipe for stderr "
"communication with process: %s",
strerror(errno));
/** Cleanup standard in pipe. */
close(stdin_pipe[0]);
close(stdin_pipe[1]);
/** Cleanup standard out pipe. */
close(stdin_pipe[0]);
close(stdin_pipe[1]);
return PROCESS_STATUS_ERROR;
}
#ifdef _SC_OPEN_MAX
if (-1 == max_fd) {
max_fd = (int)sysconf(_SC_OPEN_MAX);
if (max_fd == -1) {
max_fd = DEFAULT_MAX_FD;
log_warn(LD_PROCESS,
"Cannot find maximum file descriptor, assuming: %d", max_fd);
}
}
#else /* !(defined(_SC_OPEN_MAX)) */
max_fd = DEFAULT_MAX_FD;
#endif /* defined(_SC_OPEN_MAX) */
pid = fork();
if (0 == pid) {
/* This code is running in the child process context. */
#if defined(HAVE_SYS_PRCTL_H) && defined(__linux__)
/* Attempt to have the kernel issue a SIGTERM if the parent
* goes away. Certain attributes of the binary being execve()ed
* will clear this during the execve() call, but it's better
* than nothing.
*/
prctl(PR_SET_PDEATHSIG, SIGTERM);
#endif /* defined(HAVE_SYS_PRCTL_H) && defined(__linux__) */
/* Link process stdout to the write end of the pipe. */
retval = dup2(stdout_pipe[1], STDOUT_FILENO);
if (-1 == retval)
goto error;
/* Link process stderr to the write end of the pipe. */
retval = dup2(stderr_pipe[1], STDERR_FILENO);
if (-1 == retval)
goto error;
/* Link process stdin to the read end of the pipe */
retval = dup2(stdin_pipe[0], STDIN_FILENO);
if (-1 == retval)
goto error;
/* Close our pipes now after they have been dup2()'ed. */
close(stderr_pipe[0]);
close(stderr_pipe[1]);
close(stdout_pipe[0]);
close(stdout_pipe[1]);
close(stdin_pipe[0]);
close(stdin_pipe[1]);
/* Close all other fds, including the read end of the pipe. XXX: We should
* now be doing enough FD_CLOEXEC setting to make this needless.
*/
for (fd = STDERR_FILENO + 1; fd < max_fd; fd++)
close(fd);
/* Create the argv value for our new process. */
char **argv = process_get_argv(process);
/* Create the env value for our new process. */
process_environment_t *env = process_get_environment(process);
/* Call the requested program. */
retval = execve(argv[0], argv, env->unixoid_environment_block);
/* If we made it here it is because execve failed :-( */
if (-1 == retval)
fprintf(stderr, "Call to execve() failed: %s", strerror(errno));
tor_free(argv);
process_environment_free(env);
tor_assert_unreached();
error:
/* LCOV_EXCL_START */
fprintf(stderr, "Error from child process: %s", strerror(errno));
_exit(1);
/* LCOV_EXCL_STOP */
}
/* We are in the parent process. */
if (-1 == pid) {
log_warn(LD_PROCESS,
"Failed to create child process: %s", strerror(errno));
/** Cleanup standard in pipe. */
close(stdin_pipe[0]);
close(stdin_pipe[1]);
/** Cleanup standard out pipe. */
close(stdin_pipe[0]);
close(stdin_pipe[1]);
/** Cleanup standard error pipe. */
close(stderr_pipe[0]);
close(stderr_pipe[1]);
return PROCESS_STATUS_ERROR;
}
/* Register our PID. */
unix_process->pid = pid;
/* Setup waitpid callbacks. */
unix_process->waitpid = set_waitpid_callback(pid,
process_unix_waitpid_callback,
process);
/* Handle standard out. */
unix_process->stdout_handle.fd = stdout_pipe[0];
retval = close(stdout_pipe[1]);
if (-1 == retval) {
log_warn(LD_PROCESS, "Failed to close write end of standard out pipe: %s",
strerror(errno));
}
/* Handle standard error. */
unix_process->stderr_handle.fd = stderr_pipe[0];
retval = close(stderr_pipe[1]);
if (-1 == retval) {
log_warn(LD_PROCESS,
"Failed to close write end of standard error pipe: %s",
strerror(errno));
}
/* Handle standard in. */
unix_process->stdin_handle.fd = stdin_pipe[1];
retval = close(stdin_pipe[0]);
if (-1 == retval) {
log_warn(LD_PROCESS, "Failed to close read end of standard in pipe: %s",
strerror(errno));
}
/* Setup our handles. */
process_unix_setup_handle(process,
&unix_process->stdout_handle,
EV_READ|EV_PERSIST,
stdout_read_callback);
process_unix_setup_handle(process,
&unix_process->stderr_handle,
EV_READ|EV_PERSIST,
stderr_read_callback);
process_unix_setup_handle(process,
&unix_process->stdin_handle,
EV_WRITE|EV_PERSIST,
stdin_write_callback);
/* Start reading from standard out and standard error. */
process_unix_start_reading(&unix_process->stdout_handle);
process_unix_start_reading(&unix_process->stderr_handle);
return PROCESS_STATUS_RUNNING;
}
/** Terminate the given process. Returns true on success, otherwise false. */
bool
process_unix_terminate(process_t *process)
{
tor_assert(process);
process_unix_t *unix_process = process_get_unix_process(process);
/* All running processes should have a waitpid. */
if (BUG(unix_process->waitpid == NULL))
return false;
bool success = true;
/* Send a SIGTERM to our child process. */
int ret;
ret = kill(unix_process->pid, SIGTERM);
if (ret == -1) {
log_warn(LD_PROCESS, "Unable to terminate process: %s",
strerror(errno));
success = false;
}
/* Close all our FD's. */
if (! process_unix_close_file_descriptors(unix_process))
success = false;
return success;
}
/** Returns the unique process identifier for the given <b>process</b>. */
process_pid_t
process_unix_get_pid(process_t *process)
{
tor_assert(process);
process_unix_t *unix_process = process_get_unix_process(process);
return (process_pid_t)unix_process->pid;
}
/** Write the given <b>buffer</b> as input to the given <b>process</b>'s
* standard input. Returns the number of bytes written. */
int
process_unix_write(process_t *process, buf_t *buffer)
{
tor_assert(process);
tor_assert(buffer);
process_unix_t *unix_process = process_get_unix_process(process);
size_t buffer_flush_len = buf_datalen(buffer);
const size_t max_to_write = MIN(PROCESS_MAX_WRITE, buffer_flush_len);
/* If we have data to write (when buffer_flush_len > 0) and we are not
* currently getting file descriptor events from the kernel, we tell the
* kernel to start notifying us about when we can write to our file
* descriptor and return. */
if (buffer_flush_len > 0 && ! unix_process->stdin_handle.is_writing) {
process_unix_start_writing(&unix_process->stdin_handle);
return 0;
}
/* We don't have any data to write, but the kernel is currently notifying us
* about whether we are able to write or not. Tell the kernel to stop
* notifying us until we have data to write. */
if (buffer_flush_len == 0 && unix_process->stdin_handle.is_writing) {
process_unix_stop_writing(&unix_process->stdin_handle);
return 0;
}
/* We have data to write and the kernel have told us to write it. */
return buf_flush_to_pipe(buffer,
process_get_unix_process(process)->stdin_handle.fd,
max_to_write, &buffer_flush_len);
}
/** Read data from the given process's standard output and put it into
* <b>buffer</b>. Returns the number of bytes read. */
int
process_unix_read_stdout(process_t *process, buf_t *buffer)
{
tor_assert(process);
tor_assert(buffer);
process_unix_t *unix_process = process_get_unix_process(process);
return process_unix_read_handle(process,
&unix_process->stdout_handle,
buffer);
}
/** Read data from the given process's standard error and put it into
* <b>buffer</b>. Returns the number of bytes read. */
int
process_unix_read_stderr(process_t *process, buf_t *buffer)
{
tor_assert(process);
tor_assert(buffer);
process_unix_t *unix_process = process_get_unix_process(process);
return process_unix_read_handle(process,
&unix_process->stderr_handle,
buffer);
}
/** This function is called whenever libevent thinks we have data that could be
* read from the child process's standard output. We notify the Process
* subsystem, which is then responsible for calling back to us for doing the
* actual reading of the data. */
STATIC void
stdout_read_callback(evutil_socket_t fd, short event, void *data)
{
(void)fd;
(void)event;
process_t *process = data;
tor_assert(process);
process_notify_event_stdout(process);
}
/** This function is called whenever libevent thinks we have data that could be
* read from the child process's standard error. We notify the Process
* subsystem, which is then responsible for calling back to us for doing the
* actual reading of the data. */
STATIC void
stderr_read_callback(evutil_socket_t fd, short event, void *data)
{
(void)fd;
(void)event;
process_t *process = data;
tor_assert(process);
process_notify_event_stderr(process);
}
/** This function is called whenever libevent thinks we have data that could be
* written the child process's standard input. We notify the Process subsystem,
* which is then responsible for calling back to us for doing the actual write
* of the data. */
STATIC void
stdin_write_callback(evutil_socket_t fd, short event, void *data)
{
(void)fd;
(void)event;
process_t *process = data;
tor_assert(process);
process_notify_event_stdin(process);
}
/** This function tells libevent that we are interested in receiving read
* events from the given <b>handle</b>. */
STATIC void
process_unix_start_reading(process_unix_handle_t *handle)
{
tor_assert(handle);
if (event_add(handle->event, NULL))
log_warn(LD_PROCESS,
"Unable to add libevent event for handle.");
}
/** This function tells libevent that we are no longer interested in receiving
* read events from the given <b>handle</b>. */
STATIC void
process_unix_stop_reading(process_unix_handle_t *handle)
{
tor_assert(handle);
if (handle->event == NULL)
return;
if (event_del(handle->event))
log_warn(LD_PROCESS,
"Unable to delete libevent event for handle.");
}
/** This function tells libevent that we are interested in receiving write
* events from the given <b>handle</b>. */
STATIC void
process_unix_start_writing(process_unix_handle_t *handle)
{
tor_assert(handle);
if (event_add(handle->event, NULL))
log_warn(LD_PROCESS,
"Unable to add libevent event for handle.");
handle->is_writing = true;
}
/** This function tells libevent that we are no longer interested in receiving
* write events from the given <b>handle</b>. */
STATIC void
process_unix_stop_writing(process_unix_handle_t *handle)
{
tor_assert(handle);
if (handle->event == NULL)
return;
if (event_del(handle->event))
log_warn(LD_PROCESS,
"Unable to delete libevent event for handle.");
handle->is_writing = false;
}
/** This function is called when the waitpid system have detected that our
* process have terminated. We disable the waitpid system and notify the
* Process subsystem that we have terminated. */
STATIC void
process_unix_waitpid_callback(int status, void *data)
{
tor_assert(data);
process_t *process = data;
process_unix_t *unix_process = process_get_unix_process(process);
/* Remove our waitpid callback. */
clear_waitpid_callback(unix_process->waitpid);
unix_process->waitpid = NULL;
/* Notify our process. */
process_notify_event_exit(process, status);
/* Make sure you don't modify the process after we have called
* process_notify_event_exit() on it, to allow users to process_free() it in
* the exit callback. */
}
/** This function sets the file descriptor in the <b>handle</b> as non-blocking
* and configures the libevent event structure based on the given <b>flags</b>
* to ensure that <b>callback</b> is called whenever we have events on the
* given <b>handle</b>. */
STATIC void
process_unix_setup_handle(process_t *process,
process_unix_handle_t *handle,
short flags,
event_callback_fn callback)
{
tor_assert(process);
tor_assert(handle);
tor_assert(callback);
/* Put our file descriptor into non-blocking mode. */
if (fcntl(handle->fd, F_SETFL, O_NONBLOCK) < 0) {
log_warn(LD_PROCESS, "Unable mark Unix handle as non-blocking: %s",
strerror(errno));
}
/* Setup libevent event. */
handle->event = tor_event_new(tor_libevent_get_base(),
handle->fd,
flags,
callback,
process);
}
/** This function reads data from the given <b>handle</b> and puts it into
* <b>buffer</b>. Returns the number of bytes read this way. */
STATIC int
process_unix_read_handle(process_t *process,
process_unix_handle_t *handle,
buf_t *buffer)
{
tor_assert(process);
tor_assert(handle);
tor_assert(buffer);
int ret = 0;
int eof = 0;
int error = 0;
ret = buf_read_from_pipe(buffer,
handle->fd,
PROCESS_MAX_READ,
&eof,
&error);
if (error)
log_warn(LD_PROCESS,
"Unable to read data: %s", strerror(error));
if (eof) {
handle->reached_eof = true;
process_unix_stop_reading(handle);
}
return ret;
}
/** Close the standard in, out, and error handles of the given
* <b>unix_process</b>. */
STATIC bool
process_unix_close_file_descriptors(process_unix_t *unix_process)
{
tor_assert(unix_process);
int ret;
bool success = true;
if (unix_process->stdin_handle.fd != -1) {
ret = close(unix_process->stdin_handle.fd);
if (ret == -1) {
log_warn(LD_PROCESS, "Unable to close standard in");
success = false;
}
unix_process->stdin_handle.fd = -1;
}
if (unix_process->stdout_handle.fd != -1) {
ret = close(unix_process->stdout_handle.fd);
if (ret == -1) {
log_warn(LD_PROCESS, "Unable to close standard out");
success = false;
}
unix_process->stdout_handle.fd = -1;
}
if (unix_process->stderr_handle.fd != -1) {
ret = close(unix_process->stderr_handle.fd);
if (ret == -1) {
log_warn(LD_PROCESS, "Unable to close standard error");
success = false;
}
unix_process->stderr_handle.fd = -1;
}
return success;
}
#endif /* defined(_WIN32). */
+68
View File
@@ -0,0 +1,68 @@
/* Copyright (c) 2003-2004, Roger Dingledine
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2018, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file process_unix.h
* \brief Header for process_unix.c
**/
#ifndef TOR_PROCESS_UNIX_H
#define TOR_PROCESS_UNIX_H
#ifndef _WIN32
#include "orconfig.h"
#include "lib/malloc/malloc.h"
#include <event2/event.h>
struct process_t;
struct process_unix_t;
typedef struct process_unix_t process_unix_t;
process_unix_t *process_unix_new(void);
void process_unix_free_(process_unix_t *unix_process);
#define process_unix_free(s) \
FREE_AND_NULL(process_unix_t, process_unix_free_, (s))
process_status_t process_unix_exec(struct process_t *process);
bool process_unix_terminate(struct process_t *process);
process_pid_t process_unix_get_pid(struct process_t *process);
int process_unix_write(struct process_t *process, buf_t *buffer);
int process_unix_read_stdout(struct process_t *process, buf_t *buffer);
int process_unix_read_stderr(struct process_t *process, buf_t *buffer);
#ifdef PROCESS_UNIX_PRIVATE
struct process_unix_handle_t;
typedef struct process_unix_handle_t process_unix_handle_t;
STATIC void stdout_read_callback(evutil_socket_t fd, short event, void *data);
STATIC void stderr_read_callback(evutil_socket_t fd, short event, void *data);
STATIC void stdin_write_callback(evutil_socket_t fd, short event, void *data);
STATIC void process_unix_start_reading(process_unix_handle_t *);
STATIC void process_unix_stop_reading(process_unix_handle_t *);
STATIC void process_unix_start_writing(process_unix_handle_t *);
STATIC void process_unix_stop_writing(process_unix_handle_t *);
STATIC void process_unix_waitpid_callback(int status, void *data);
STATIC void process_unix_setup_handle(process_t *process,
process_unix_handle_t *handle,
short flags,
event_callback_fn callback);
STATIC int process_unix_read_handle(process_t *,
process_unix_handle_t *,
buf_t *);
STATIC bool process_unix_close_file_descriptors(process_unix_t *);
#endif /* defined(PROCESS_UNIX_PRIVATE). */
#endif /* defined(_WIN32). */
#endif /* defined(TOR_PROCESS_UNIX_H). */
File diff suppressed because it is too large Load Diff
+97
View File
@@ -0,0 +1,97 @@
/* Copyright (c) 2003-2004, Roger Dingledine
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2018, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file process_win32.h
* \brief Header for process_win32.c
**/
#ifndef TOR_PROCESS_WIN32_H
#define TOR_PROCESS_WIN32_H
#ifdef _WIN32
#include "orconfig.h"
#include "lib/malloc/malloc.h"
#include "lib/evloop/compat_libevent.h"
#include <windows.h>
struct process_t;
struct process_win32_t;
typedef struct process_win32_t process_win32_t;
process_win32_t *process_win32_new(void);
void process_win32_free_(process_win32_t *win32_process);
#define process_win32_free(s) \
FREE_AND_NULL(process_win32_t, process_win32_free_, (s))
void process_win32_init(void);
void process_win32_deinit(void);
process_status_t process_win32_exec(struct process_t *process);
bool process_win32_terminate(struct process_t *process);
process_pid_t process_win32_get_pid(struct process_t *process);
int process_win32_write(struct process_t *process, buf_t *buffer);
int process_win32_read_stdout(struct process_t *process, buf_t *buffer);
int process_win32_read_stderr(struct process_t *process, buf_t *buffer);
void process_win32_trigger_completion_callbacks(void);
/* Timer handling. */
void process_win32_timer_start(void);
void process_win32_timer_stop(void);
bool process_win32_timer_running(void);
#ifdef PROCESS_WIN32_PRIVATE
STATIC void process_win32_timer_callback(periodic_timer_t *, void *);
STATIC bool process_win32_timer_test_process(process_t *);
/* I/O pipe handling. */
struct process_win32_handle_t;
typedef struct process_win32_handle_t process_win32_handle_t;
typedef enum process_win32_pipe_type_t {
/** This pipe is used for reading. */
PROCESS_WIN32_PIPE_TYPE_READER,
/** This pipe is used for writing. */
PROCESS_WIN32_PIPE_TYPE_WRITER
} process_win32_pipe_type_t;
STATIC bool process_win32_create_pipe(HANDLE *,
HANDLE *,
SECURITY_ATTRIBUTES *,
process_win32_pipe_type_t);
STATIC void process_win32_cleanup_handle(process_win32_handle_t *handle);
STATIC VOID WINAPI process_win32_stdout_read_done(DWORD,
DWORD,
LPOVERLAPPED);
STATIC VOID WINAPI process_win32_stderr_read_done(DWORD,
DWORD,
LPOVERLAPPED);
STATIC VOID WINAPI process_win32_stdin_write_done(DWORD,
DWORD,
LPOVERLAPPED);
STATIC int process_win32_read_from_handle(process_win32_handle_t *,
buf_t *,
LPOVERLAPPED_COMPLETION_ROUTINE);
STATIC bool process_win32_handle_read_completion(process_win32_handle_t *,
DWORD,
DWORD);
STATIC char *format_win_cmdline_argument(const char *arg);
STATIC char *tor_join_win_cmdline(const char *argv[]);
#endif /* defined(PROCESS_WIN32_PRIVATE). */
#endif /* ! defined(_WIN32). */
#endif /* defined(TOR_PROCESS_WIN32_H). */
File diff suppressed because it is too large Load Diff
-134
View File
@@ -1,134 +0,0 @@
/* Copyright (c) 2003-2004, Roger Dingledine
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2018, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file subprocess.h
* \brief Header for subprocess.c
**/
#ifndef TOR_SUBPROCESS_H
#define TOR_SUBPROCESS_H
#include "lib/cc/torint.h"
#include "lib/testsupport/testsupport.h"
#include <stddef.h>
#ifdef _WIN32
#include <windows.h>
#endif
struct smartlist_t;
void tor_disable_spawning_background_processes(void);
typedef struct process_handle_t process_handle_t;
struct process_environment_t;
int tor_spawn_background(const char *const filename, const char **argv,
struct process_environment_t *env,
process_handle_t **process_handle_out);
#define SPAWN_ERROR_MESSAGE "ERR: Failed to spawn background process - code "
/** Status of an I/O stream. */
enum stream_status {
IO_STREAM_OKAY,
IO_STREAM_EAGAIN,
IO_STREAM_TERM,
IO_STREAM_CLOSED
};
const char *stream_status_to_string(enum stream_status stream_status);
enum stream_status get_string_from_pipe(int fd, char *buf, size_t count);
/* Values of process_handle_t.status. */
#define PROCESS_STATUS_NOTRUNNING 0
#define PROCESS_STATUS_RUNNING 1
#define PROCESS_STATUS_ERROR -1
#ifdef SUBPROCESS_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 {
/** One of the PROCESS_STATUS_* values */
int status;
#ifdef _WIN32
HANDLE stdin_pipe;
HANDLE stdout_pipe;
HANDLE stderr_pipe;
PROCESS_INFORMATION pid;
#else /* !(defined(_WIN32)) */
int stdin_pipe;
int stdout_pipe;
int stderr_pipe;
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 /* defined(_WIN32) */
};
#endif /* defined(SUBPROCESS_PRIVATE) */
/* Return values of tor_get_exit_code() */
#define PROCESS_EXIT_RUNNING 1
#define PROCESS_EXIT_EXITED 0
#define PROCESS_EXIT_ERROR -1
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
ssize_t tor_read_all_handle(HANDLE h, char *buf, size_t count,
const process_handle_t *process);
#else
ssize_t tor_read_all_handle(int fd, char *buf, size_t count,
const process_handle_t *process,
int *eof);
#endif /* defined(_WIN32) */
ssize_t tor_read_all_from_process_stdout(
const process_handle_t *process_handle, char *buf, size_t count);
ssize_t tor_read_all_from_process_stderr(
const process_handle_t *process_handle, char *buf, size_t count);
char *tor_join_win_cmdline(const char *argv[]);
int tor_process_get_pid(process_handle_t *process_handle);
#ifdef _WIN32
HANDLE tor_process_get_stdout_pipe(process_handle_t *process_handle);
#else
int tor_process_get_stdout_pipe(process_handle_t *process_handle);
#endif
#ifdef _WIN32
MOCK_DECL(struct smartlist_t *, tor_get_lines_from_handle,(HANDLE *handle,
enum stream_status *stream_status));
#else
MOCK_DECL(struct smartlist_t *, tor_get_lines_from_handle,(int fd,
enum stream_status *stream_status));
#endif /* defined(_WIN32) */
int tor_terminate_process(process_handle_t *process_handle);
MOCK_DECL(void, tor_process_handle_destroy,(process_handle_t *process_handle,
int also_terminate_process));
#ifdef SUBPROCESS_PRIVATE
/* Prototypes for private functions only used by util.c (and unit tests) */
#ifndef _WIN32
STATIC int format_helper_exit_status(unsigned char child_state,
int saved_errno, char *hex_errno);
/* Space for hex values of child state, a slash, saved_errno (with
leading minus) and newline (no null) */
#define HEX_ERRNO_SIZE (sizeof(char) * 2 + 1 + \
1 + sizeof(int) * 2 + 1)
#endif /* !defined(_WIN32) */
#endif /* defined(SUBPROCESS_PRIVATE) */
#endif