mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-12-06 19:41:15 +01:00
Merge branch 'ticket28179_squashed' into ticket28179_squashed_merged
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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). */
|
||||
@@ -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). */
|
||||
@@ -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
@@ -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
@@ -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
|
||||
Reference in New Issue
Block a user