|
@@ -0,0 +1,857 @@
|
|
|
+/* 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_win32.c
|
|
|
+ * \brief Module for working with Windows processes.
|
|
|
+ **/
|
|
|
+
|
|
|
+#define PROCESS_WIN32_PRIVATE
|
|
|
+#include "lib/intmath/cmp.h"
|
|
|
+#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/log/win32err.h"
|
|
|
+#include "lib/process/process.h"
|
|
|
+#include "lib/process/process_win32.h"
|
|
|
+#include "lib/process/subprocess.h"
|
|
|
+#include "lib/process/env.h"
|
|
|
+
|
|
|
+#ifdef HAVE_SYS_TIME_H
|
|
|
+#include <sys/time.h>
|
|
|
+#endif
|
|
|
+
|
|
|
+#ifdef _WIN32
|
|
|
+
|
|
|
+/** The size of our intermediate buffers. */
|
|
|
+#define BUFFER_SIZE (1024)
|
|
|
+
|
|
|
+/** Timer that ticks once a second and calls the process_win32_timer_callback()
|
|
|
+ * function. */
|
|
|
+static periodic_timer_t *periodic_timer;
|
|
|
+
|
|
|
+/** Structure to represent the state around the pipe HANDLE.
|
|
|
+ *
|
|
|
+ * This structure is used to store state about a given HANDLE, including
|
|
|
+ * whether we have reached end of file, its intermediate buffers, and how much
|
|
|
+ * data that is available in the intermediate buffer. */
|
|
|
+struct process_win32_handle_t {
|
|
|
+ /** Standard out pipe handle. */
|
|
|
+ HANDLE pipe;
|
|
|
+
|
|
|
+ /** True iff we have reached EOF from the pipe. */
|
|
|
+ bool reached_eof;
|
|
|
+
|
|
|
+ /** How much data is available in buffer. */
|
|
|
+ size_t data_available;
|
|
|
+
|
|
|
+ /** Intermediate buffer for ReadFileEx() and WriteFileEx(). */
|
|
|
+ char buffer[BUFFER_SIZE];
|
|
|
+
|
|
|
+ /** Overlapped structure for ReadFileEx() and WriteFileEx(). */
|
|
|
+ OVERLAPPED overlapped;
|
|
|
+
|
|
|
+ /** Are we waiting for another I/O operation to complete? */
|
|
|
+ bool busy;
|
|
|
+};
|
|
|
+
|
|
|
+/** Structure to represent the Windows specific implementation details of this
|
|
|
+ * Process backend.
|
|
|
+ *
|
|
|
+ * This structure is attached to <b>process_t</b> (see process.h) and is
|
|
|
+ * reachable from <b>process_t</b> via the <b>process_get_win32_process()</b>
|
|
|
+ * method. */
|
|
|
+struct process_win32_t {
|
|
|
+ /** Standard in state. */
|
|
|
+ process_win32_handle_t stdin_handle;
|
|
|
+
|
|
|
+ /** Standard out state. */
|
|
|
+ process_win32_handle_t stdout_handle;
|
|
|
+
|
|
|
+ /** Standard error state. */
|
|
|
+ process_win32_handle_t stderr_handle;
|
|
|
+
|
|
|
+ /** Process Information. */
|
|
|
+ PROCESS_INFORMATION process_information;
|
|
|
+};
|
|
|
+
|
|
|
+/** Create a new <b>process_win32_t</b>.
|
|
|
+ *
|
|
|
+ * This function constructs a new <b>process_win32_t</b> and initializes the
|
|
|
+ * default values. */
|
|
|
+process_win32_t *
|
|
|
+process_win32_new(void)
|
|
|
+{
|
|
|
+ process_win32_t *win32_process;
|
|
|
+ win32_process = tor_malloc_zero(sizeof(process_win32_t));
|
|
|
+
|
|
|
+ win32_process->stdin_handle.pipe = INVALID_HANDLE_VALUE;
|
|
|
+ win32_process->stdout_handle.pipe = INVALID_HANDLE_VALUE;
|
|
|
+ win32_process->stderr_handle.pipe = INVALID_HANDLE_VALUE;
|
|
|
+
|
|
|
+ return win32_process;
|
|
|
+}
|
|
|
+
|
|
|
+/** Free a given <b>process_win32_t</b>.
|
|
|
+ *
|
|
|
+ * This function deinitializes and frees up the resources allocated for the
|
|
|
+ * given <b>process_win32_t</b>. */
|
|
|
+void
|
|
|
+process_win32_free_(process_win32_t *win32_process)
|
|
|
+{
|
|
|
+ if (! win32_process)
|
|
|
+ return;
|
|
|
+
|
|
|
+ /* Cleanup our handles. */
|
|
|
+ process_win32_cleanup_handle(&win32_process->stdin_handle);
|
|
|
+ process_win32_cleanup_handle(&win32_process->stdout_handle);
|
|
|
+ process_win32_cleanup_handle(&win32_process->stderr_handle);
|
|
|
+
|
|
|
+ tor_free(win32_process);
|
|
|
+}
|
|
|
+
|
|
|
+/** Initialize the Windows backend of the Process subsystem. */
|
|
|
+void
|
|
|
+process_win32_init(void)
|
|
|
+{
|
|
|
+ /* We don't start the periodic timer here because it makes no sense to have
|
|
|
+ * the timer running until we have some processes that benefits from the
|
|
|
+ * timer timer ticks. */
|
|
|
+}
|
|
|
+
|
|
|
+/** Deinitialize the Windows backend of the Process subsystem. */
|
|
|
+void
|
|
|
+process_win32_deinit(void)
|
|
|
+{
|
|
|
+ /* Stop our timer, but only if it's running. */
|
|
|
+ if (process_win32_timer_running())
|
|
|
+ process_win32_timer_stop();
|
|
|
+}
|
|
|
+
|
|
|
+/** Execute the given process. This function is responsible for setting up
|
|
|
+ * named pipes for I/O between the child process and the Tor process. Returns
|
|
|
+ * <b>PROCESS_STATUS_RUNNING</b> upon success. */
|
|
|
+process_status_t
|
|
|
+process_win32_exec(process_t *process)
|
|
|
+{
|
|
|
+ tor_assert(process);
|
|
|
+
|
|
|
+ process_win32_t *win32_process = process_get_win32_process(process);
|
|
|
+
|
|
|
+ HANDLE stdout_pipe_read = NULL;
|
|
|
+ HANDLE stdout_pipe_write = NULL;
|
|
|
+ HANDLE stderr_pipe_read = NULL;
|
|
|
+ HANDLE stderr_pipe_write = NULL;
|
|
|
+ HANDLE stdin_pipe_read = NULL;
|
|
|
+ HANDLE stdin_pipe_write = NULL;
|
|
|
+ BOOL ret = FALSE;
|
|
|
+ const char *filename = process_get_command(process);
|
|
|
+
|
|
|
+ /* Not much we can do if we haven't been told what to start. */
|
|
|
+ if (BUG(filename == NULL))
|
|
|
+ return PROCESS_STATUS_ERROR;
|
|
|
+
|
|
|
+ /* Setup our security attributes. */
|
|
|
+ SECURITY_ATTRIBUTES security_attributes;
|
|
|
+ security_attributes.nLength = sizeof(security_attributes);
|
|
|
+ security_attributes.bInheritHandle = TRUE;
|
|
|
+ /* FIXME: should we set explicit security attributes?
|
|
|
+ * (See Ticket #2046, comment 5) */
|
|
|
+ security_attributes.lpSecurityDescriptor = NULL;
|
|
|
+
|
|
|
+ /* Create our standard out pipe. */
|
|
|
+ if (! process_win32_create_pipe(&stdout_pipe_read,
|
|
|
+ &stdout_pipe_write,
|
|
|
+ &security_attributes,
|
|
|
+ PROCESS_WIN32_PIPE_TYPE_READER)) {
|
|
|
+ return PROCESS_STATUS_ERROR;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Create our standard error pipe. */
|
|
|
+ if (! process_win32_create_pipe(&stderr_pipe_read,
|
|
|
+ &stderr_pipe_write,
|
|
|
+ &security_attributes,
|
|
|
+ PROCESS_WIN32_PIPE_TYPE_READER)) {
|
|
|
+ return PROCESS_STATUS_ERROR;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Create out standard in pipe. */
|
|
|
+ if (! process_win32_create_pipe(&stdin_pipe_read,
|
|
|
+ &stdin_pipe_write,
|
|
|
+ &security_attributes,
|
|
|
+ PROCESS_WIN32_PIPE_TYPE_WRITER)) {
|
|
|
+ return PROCESS_STATUS_ERROR;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Configure startup info for our child process. */
|
|
|
+ STARTUPINFOA startup_info;
|
|
|
+
|
|
|
+ memset(&startup_info, 0, sizeof(startup_info));
|
|
|
+ startup_info.cb = sizeof(startup_info);
|
|
|
+ startup_info.hStdError = stderr_pipe_write;
|
|
|
+ startup_info.hStdOutput = stdout_pipe_write;
|
|
|
+ startup_info.hStdInput = stdin_pipe_read;
|
|
|
+ startup_info.dwFlags |= STARTF_USESTDHANDLES;
|
|
|
+
|
|
|
+ /* Create the env value for our new process. */
|
|
|
+ process_environment_t *env = process_get_environment(process);
|
|
|
+
|
|
|
+ /* Create the argv value for our new process. */
|
|
|
+ char **argv = process_get_argv(process);
|
|
|
+
|
|
|
+ /* Windows expects argv to be a whitespace delimited string, so join argv up
|
|
|
+ */
|
|
|
+ char *joined_argv = tor_join_win_cmdline((const char **)argv);
|
|
|
+
|
|
|
+ /* Create the child process */
|
|
|
+ ret = CreateProcessA(filename,
|
|
|
+ joined_argv,
|
|
|
+ NULL,
|
|
|
+ NULL,
|
|
|
+ TRUE,
|
|
|
+ CREATE_NO_WINDOW,
|
|
|
+ env->windows_environment_block[0] == '\0' ?
|
|
|
+ NULL : env->windows_environment_block,
|
|
|
+ NULL,
|
|
|
+ &startup_info,
|
|
|
+ &win32_process->process_information);
|
|
|
+
|
|
|
+ tor_free(argv);
|
|
|
+ tor_free(joined_argv);
|
|
|
+ process_environment_free(env);
|
|
|
+
|
|
|
+ if (! ret) {
|
|
|
+ log_warn(LD_PROCESS, "CreateProcessA() failed: %s",
|
|
|
+ format_win32_error(GetLastError()));
|
|
|
+
|
|
|
+ /* Cleanup our handles. */
|
|
|
+ CloseHandle(stdout_pipe_read);
|
|
|
+ CloseHandle(stdout_pipe_write);
|
|
|
+ CloseHandle(stderr_pipe_read);
|
|
|
+ CloseHandle(stderr_pipe_write);
|
|
|
+ CloseHandle(stdin_pipe_read);
|
|
|
+ CloseHandle(stdin_pipe_write);
|
|
|
+
|
|
|
+ return PROCESS_STATUS_ERROR;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* TODO: Should we close hProcess and hThread in
|
|
|
+ * process_handle->process_information? */
|
|
|
+ win32_process->stdout_handle.pipe = stdout_pipe_read;
|
|
|
+ win32_process->stderr_handle.pipe = stderr_pipe_read;
|
|
|
+ win32_process->stdin_handle.pipe = stdin_pipe_write;
|
|
|
+
|
|
|
+ /* Used by the callback functions from ReadFileEx() and WriteFileEx() such
|
|
|
+ * that we can figure out which process_t that was responsible for the event.
|
|
|
+ *
|
|
|
+ * Warning, here be dragons:
|
|
|
+ *
|
|
|
+ * MSDN says that the hEvent member of the overlapped structure is unused
|
|
|
+ * for ReadFileEx() and WriteFileEx, which allows us to store a pointer to
|
|
|
+ * our process state there.
|
|
|
+ */
|
|
|
+ win32_process->stdout_handle.overlapped.hEvent = (HANDLE)process;
|
|
|
+ win32_process->stderr_handle.overlapped.hEvent = (HANDLE)process;
|
|
|
+ win32_process->stdin_handle.overlapped.hEvent = (HANDLE)process;
|
|
|
+
|
|
|
+ /* Start our timer if it is not already running. */
|
|
|
+ if (! process_win32_timer_running())
|
|
|
+ process_win32_timer_start();
|
|
|
+
|
|
|
+ /* We use Windows Extended I/O functions, so our completion callbacks are
|
|
|
+ * called automatically for us when there is data to read. Because of this
|
|
|
+ * we start the read of standard out and error right away. */
|
|
|
+ process_notify_event_stdout(process);
|
|
|
+ process_notify_event_stderr(process);
|
|
|
+
|
|
|
+ return PROCESS_STATUS_RUNNING;
|
|
|
+}
|
|
|
+
|
|
|
+/** Schedule an async write of the data found in <b>buffer</b> for the given
|
|
|
+ * process. This function runs an async write operation of the content of
|
|
|
+ * buffer, if we are not already waiting for a pending I/O request. Returns the
|
|
|
+ * number of bytes that Windows will hopefully write for us in the background.
|
|
|
+ * */
|
|
|
+int
|
|
|
+process_win32_write(struct process_t *process, buf_t *buffer)
|
|
|
+{
|
|
|
+ tor_assert(process);
|
|
|
+ tor_assert(buffer);
|
|
|
+
|
|
|
+ process_win32_t *win32_process = process_get_win32_process(process);
|
|
|
+ BOOL ret = FALSE;
|
|
|
+ const size_t buffer_size = buf_datalen(buffer);
|
|
|
+
|
|
|
+ /* Windows is still writing our buffer. */
|
|
|
+ if (win32_process->stdin_handle.busy)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ /* Nothing for us to do right now. */
|
|
|
+ if (buffer_size == 0)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ /* We have reached end of file already? */
|
|
|
+ if (BUG(win32_process->stdin_handle.reached_eof))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ /* Figure out how much data we should read. */
|
|
|
+ const size_t write_size = MIN(buffer_size,
|
|
|
+ sizeof(win32_process->stdin_handle.buffer));
|
|
|
+
|
|
|
+ /* Read data from the process_t buffer into our intermediate buffer. */
|
|
|
+ buf_get_bytes(buffer, win32_process->stdin_handle.buffer, write_size);
|
|
|
+
|
|
|
+ /* Schedule our write. */
|
|
|
+ ret = WriteFileEx(win32_process->stdin_handle.pipe,
|
|
|
+ win32_process->stdin_handle.buffer,
|
|
|
+ write_size,
|
|
|
+ &win32_process->stdin_handle.overlapped,
|
|
|
+ process_win32_stdin_write_done);
|
|
|
+
|
|
|
+ if (! ret) {
|
|
|
+ log_warn(LD_PROCESS, "WriteFileEx() failed: %s",
|
|
|
+ format_win32_error(GetLastError()));
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* This cast should be safe since our buffer can maximum be BUFFER_SIZE
|
|
|
+ * large. */
|
|
|
+ return (int)write_size;
|
|
|
+}
|
|
|
+
|
|
|
+/** This function is called from the Process subsystem whenever the Windows
|
|
|
+ * backend says it has data ready. This function also ensures that we are
|
|
|
+ * starting a new background read from the standard output of the child process
|
|
|
+ * and asks Windows to call process_win32_stdout_read_done() when that
|
|
|
+ * operation is finished. Returns the number of bytes moved into <b>buffer</b>.
|
|
|
+ * */
|
|
|
+int
|
|
|
+process_win32_read_stdout(struct process_t *process, buf_t *buffer)
|
|
|
+{
|
|
|
+ tor_assert(process);
|
|
|
+ tor_assert(buffer);
|
|
|
+
|
|
|
+ process_win32_t *win32_process = process_get_win32_process(process);
|
|
|
+
|
|
|
+ return process_win32_read_from_handle(&win32_process->stdout_handle,
|
|
|
+ buffer,
|
|
|
+ process_win32_stdout_read_done);
|
|
|
+}
|
|
|
+
|
|
|
+/** This function is called from the Process subsystem whenever the Windows
|
|
|
+ * backend says it has data ready. This function also ensures that we are
|
|
|
+ * starting a new background read from the standard error of the child process
|
|
|
+ * and asks Windows to call process_win32_stderr_read_done() when that
|
|
|
+ * operation is finished. Returns the number of bytes moved into <b>buffer</b>.
|
|
|
+ * */
|
|
|
+int
|
|
|
+process_win32_read_stderr(struct process_t *process, buf_t *buffer)
|
|
|
+{
|
|
|
+ tor_assert(process);
|
|
|
+ tor_assert(buffer);
|
|
|
+
|
|
|
+ process_win32_t *win32_process = process_get_win32_process(process);
|
|
|
+
|
|
|
+ return process_win32_read_from_handle(&win32_process->stderr_handle,
|
|
|
+ buffer,
|
|
|
+ process_win32_stderr_read_done);
|
|
|
+}
|
|
|
+
|
|
|
+/** This function is responsible for moving the Tor process into what Microsoft
|
|
|
+ * calls an "alertable" state. Once the process is in an alertable state the
|
|
|
+ * Windows kernel will notify us when our background I/O requests have finished
|
|
|
+ * and the callbacks will be executed. */
|
|
|
+void
|
|
|
+process_win32_trigger_completion_callbacks(void)
|
|
|
+{
|
|
|
+ DWORD ret;
|
|
|
+
|
|
|
+ /* The call to SleepEx(dwMilliseconds, dwAlertable) makes the process sleep
|
|
|
+ * for dwMilliseconds and if dwAlertable is set to TRUE it will also cause
|
|
|
+ * the process to enter alertable state, where the Windows kernel will notify
|
|
|
+ * us about completed I/O requests from ReadFileEx() and WriteFileEX(), which
|
|
|
+ * will cause our completion callbacks to be executed.
|
|
|
+ *
|
|
|
+ * This function returns 0 if the time interval expired or WAIT_IO_COMPLETION
|
|
|
+ * if one or more I/O callbacks were executed. */
|
|
|
+ ret = SleepEx(0, TRUE);
|
|
|
+
|
|
|
+ /* Warn us if the function returned something we did not anticipate. */
|
|
|
+ if (ret != 0 && ret != WAIT_IO_COMPLETION) {
|
|
|
+ log_warn(LD_PROCESS, "SleepEx() returned %lu", ret);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/** Start the periodic timer which is reponsible for checking whether processes
|
|
|
+ * are still alive and to make sure that the Tor process is periodically being
|
|
|
+ * moved into an alertable state. */
|
|
|
+STATIC void
|
|
|
+process_win32_timer_start(void)
|
|
|
+{
|
|
|
+ /* Make sure we never start our timer if it's already running. */
|
|
|
+ if (BUG(process_win32_timer_running()))
|
|
|
+ return;
|
|
|
+
|
|
|
+ /* Wake up once a second. */
|
|
|
+ static const struct timeval interval = {1, 0};
|
|
|
+
|
|
|
+ log_info(LD_PROCESS, "Starting Windows Process I/O timer");
|
|
|
+ periodic_timer = periodic_timer_new(tor_libevent_get_base(),
|
|
|
+ &interval,
|
|
|
+ process_win32_timer_callback,
|
|
|
+ NULL);
|
|
|
+}
|
|
|
+
|
|
|
+/** Stops the periodic timer. */
|
|
|
+STATIC void
|
|
|
+process_win32_timer_stop(void)
|
|
|
+{
|
|
|
+ if (BUG(periodic_timer == NULL))
|
|
|
+ return;
|
|
|
+
|
|
|
+ log_info(LD_PROCESS, "Stopping Windows Process I/O timer");
|
|
|
+ periodic_timer_free(periodic_timer);
|
|
|
+}
|
|
|
+
|
|
|
+/** Returns true iff the periodic timer is running. */
|
|
|
+STATIC bool
|
|
|
+process_win32_timer_running(void)
|
|
|
+{
|
|
|
+ return periodic_timer != NULL;
|
|
|
+}
|
|
|
+
|
|
|
+/** This function is called whenever the periodic_timer ticks. The function is
|
|
|
+ * responsible for moving the Tor process into an alertable state once a second
|
|
|
+ * and checking for whether our child processes have terminated since the last
|
|
|
+ * tick. */
|
|
|
+STATIC void
|
|
|
+process_win32_timer_callback(periodic_timer_t *timer, void *data)
|
|
|
+{
|
|
|
+ tor_assert(timer == periodic_timer);
|
|
|
+ tor_assert(data == NULL);
|
|
|
+
|
|
|
+ log_debug(LD_PROCESS, "Windows Process I/O timer ticked");
|
|
|
+ const smartlist_t *processes = process_get_all_processes();
|
|
|
+
|
|
|
+ SMARTLIST_FOREACH(processes, process_t *, p,
|
|
|
+ process_win32_timer_test_process(p));
|
|
|
+
|
|
|
+ process_win32_trigger_completion_callbacks();
|
|
|
+}
|
|
|
+
|
|
|
+/** Test whether a given process is still alive. Notify the Process subsystem
|
|
|
+ * if our process have died. */
|
|
|
+STATIC void
|
|
|
+process_win32_timer_test_process(process_t *process)
|
|
|
+{
|
|
|
+ tor_assert(process);
|
|
|
+
|
|
|
+ /* No need to look at processes that don't claim they are running. */
|
|
|
+ if (process_get_status(process) != PROCESS_STATUS_RUNNING)
|
|
|
+ return;
|
|
|
+
|
|
|
+ process_win32_t *win32_process = process_get_win32_process(process);
|
|
|
+ BOOL ret = FALSE;
|
|
|
+ DWORD exit_code = 0;
|
|
|
+
|
|
|
+ /* We start by testing whether our process is still running. */
|
|
|
+ ret = GetExitCodeProcess(win32_process->process_information.hProcess,
|
|
|
+ &exit_code);
|
|
|
+
|
|
|
+ if (! ret) {
|
|
|
+ log_warn(LD_PROCESS, "GetExitCodeProcess() failed: %s",
|
|
|
+ format_win32_error(GetLastError()));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Notify our process_t that our process have terminated. */
|
|
|
+ if (exit_code != STILL_ACTIVE)
|
|
|
+ process_notify_event_exit(process, exit_code);
|
|
|
+}
|
|
|
+
|
|
|
+/** Create a new overlapped named pipe. This function creates a new connected,
|
|
|
+ * named, pipe in <b>*read_pipe</b> and <b>*write_pipe</b> if the function is
|
|
|
+ * succesful. Returns true on sucess, false on failure. */
|
|
|
+STATIC bool
|
|
|
+process_win32_create_pipe(HANDLE *read_pipe,
|
|
|
+ HANDLE *write_pipe,
|
|
|
+ SECURITY_ATTRIBUTES *attributes,
|
|
|
+ process_win32_pipe_type_t pipe_type)
|
|
|
+{
|
|
|
+ tor_assert(read_pipe);
|
|
|
+ tor_assert(write_pipe);
|
|
|
+ tor_assert(attributes);
|
|
|
+
|
|
|
+ BOOL ret = FALSE;
|
|
|
+
|
|
|
+ /* Buffer size. */
|
|
|
+ const size_t size = 4096;
|
|
|
+
|
|
|
+ /* Our additional read/write modes that depends on which pipe type we are
|
|
|
+ * creating. */
|
|
|
+ DWORD read_mode = 0;
|
|
|
+ DWORD write_mode = 0;
|
|
|
+
|
|
|
+ /* Generate the unique pipe name. */
|
|
|
+ char pipe_name[MAX_PATH];
|
|
|
+ static DWORD process_id = 0;
|
|
|
+ static DWORD counter = 0;
|
|
|
+
|
|
|
+ if (process_id == 0)
|
|
|
+ process_id = GetCurrentProcessId();
|
|
|
+
|
|
|
+ tor_snprintf(pipe_name, sizeof(pipe_name),
|
|
|
+ "\\\\.\\Pipe\\Tor-Process-Pipe-%lu-%lu",
|
|
|
+ process_id, counter++);
|
|
|
+
|
|
|
+ /* Only one of our handles can be overlapped. */
|
|
|
+ switch (pipe_type) {
|
|
|
+ case PROCESS_WIN32_PIPE_TYPE_READER:
|
|
|
+ read_mode = FILE_FLAG_OVERLAPPED;
|
|
|
+ break;
|
|
|
+ case PROCESS_WIN32_PIPE_TYPE_WRITER:
|
|
|
+ write_mode = FILE_FLAG_OVERLAPPED;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ /* LCOV_EXCL_START */
|
|
|
+ tor_assert_nonfatal_unreached_once();
|
|
|
+ /* LCOV_EXCL_STOP */
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Setup our read and write handles. */
|
|
|
+ HANDLE read_handle;
|
|
|
+ HANDLE write_handle;
|
|
|
+
|
|
|
+ /* Create our named pipe. */
|
|
|
+ read_handle = CreateNamedPipeA(pipe_name,
|
|
|
+ (PIPE_ACCESS_INBOUND|read_mode),
|
|
|
+ (PIPE_TYPE_BYTE|PIPE_WAIT),
|
|
|
+ 1,
|
|
|
+ size,
|
|
|
+ size,
|
|
|
+ 1000,
|
|
|
+ attributes);
|
|
|
+
|
|
|
+ if (read_handle == INVALID_HANDLE_VALUE) {
|
|
|
+ log_warn(LD_PROCESS, "CreateNamedPipeA() failed: %s",
|
|
|
+ format_win32_error(GetLastError()));
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Create our file in the pipe namespace. */
|
|
|
+ write_handle = CreateFileA(pipe_name,
|
|
|
+ GENERIC_WRITE,
|
|
|
+ 0,
|
|
|
+ attributes,
|
|
|
+ OPEN_EXISTING,
|
|
|
+ (FILE_ATTRIBUTE_NORMAL|write_mode),
|
|
|
+ NULL);
|
|
|
+
|
|
|
+ if (write_handle == INVALID_HANDLE_VALUE) {
|
|
|
+ log_warn(LD_PROCESS, "CreateFileA() failed: %s",
|
|
|
+ format_win32_error(GetLastError()));
|
|
|
+
|
|
|
+ CloseHandle(read_handle);
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Set the inherit flag for our pipe. */
|
|
|
+ switch (pipe_type) {
|
|
|
+ case PROCESS_WIN32_PIPE_TYPE_READER:
|
|
|
+ ret = SetHandleInformation(read_handle, HANDLE_FLAG_INHERIT, 0);
|
|
|
+ break;
|
|
|
+ case PROCESS_WIN32_PIPE_TYPE_WRITER:
|
|
|
+ ret = SetHandleInformation(write_handle, HANDLE_FLAG_INHERIT, 0);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ /* LCOV_EXCL_START */
|
|
|
+ tor_assert_nonfatal_unreached_once();
|
|
|
+ /* LCOV_EXCL_STOP */
|
|
|
+ }
|
|
|
+
|
|
|
+ if (! ret) {
|
|
|
+ log_warn(LD_PROCESS, "SetHandleInformation() failed: %s",
|
|
|
+ format_win32_error(GetLastError()));
|
|
|
+
|
|
|
+ CloseHandle(read_handle);
|
|
|
+ CloseHandle(write_handle);
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Everything is good. */
|
|
|
+ *read_pipe = read_handle;
|
|
|
+ *write_pipe = write_handle;
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+/** Cleanup a given <b>handle</b>. */
|
|
|
+STATIC void
|
|
|
+process_win32_cleanup_handle(process_win32_handle_t *handle)
|
|
|
+{
|
|
|
+ tor_assert(handle);
|
|
|
+
|
|
|
+#if 0
|
|
|
+ /* FIXME(ahf): My compiler does not set _WIN32_WINNT to a high enough value
|
|
|
+ * for this code to be available. Should we force it? CancelIoEx() is
|
|
|
+ * available from Windows 7 and above. If we decide to require this, we need
|
|
|
+ * to update the checks in all the three I/O completion callbacks to handle
|
|
|
+ * the ERROR_OPERATION_ABORTED as well as ERROR_BROKEN_PIPE. */
|
|
|
+
|
|
|
+#if _WIN32_WINNT >= 0x0600
|
|
|
+ /* This code is only supported from Windows 7 and onwards. */
|
|
|
+ BOOL ret;
|
|
|
+ DWORD error_code;
|
|
|
+
|
|
|
+ /* Cancel any pending I/O requests. */
|
|
|
+ ret = CancelIoEx(handle->pipe, &handle->overlapped);
|
|
|
+
|
|
|
+ if (! ret) {
|
|
|
+ error_code = GetLastError();
|
|
|
+
|
|
|
+ /* There was no pending I/O requests for our handle. */
|
|
|
+ if (error_code != ERROR_NOT_FOUND) {
|
|
|
+ log_warn(LD_PROCESS, "CancelIoEx() failed: %s",
|
|
|
+ format_win32_error(error_code));
|
|
|
+ }
|
|
|
+ }
|
|
|
+#endif
|
|
|
+#endif
|
|
|
+
|
|
|
+ /* Close our handle. */
|
|
|
+ if (handle->pipe != INVALID_HANDLE_VALUE) {
|
|
|
+ CloseHandle(handle->pipe);
|
|
|
+ handle->pipe = INVALID_HANDLE_VALUE;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/** This function is called when ReadFileEx() completes its background read
|
|
|
+ * from the child process's standard output. We notify the Process subsystem if
|
|
|
+ * there is data available for it to read from us. */
|
|
|
+STATIC VOID WINAPI
|
|
|
+process_win32_stdout_read_done(DWORD error_code,
|
|
|
+ DWORD byte_count,
|
|
|
+ LPOVERLAPPED overlapped)
|
|
|
+{
|
|
|
+ tor_assert(overlapped);
|
|
|
+ tor_assert(overlapped->hEvent);
|
|
|
+
|
|
|
+ /* This happens when we have asked ReadFileEx() to read some data, but we
|
|
|
+ * then decided to call CloseHandle() on the HANDLE. This can happen if
|
|
|
+ * someone runs process_free() in the exit_callback of process_t, which means
|
|
|
+ * we cannot call process_get_win32_process() here. */
|
|
|
+ if (error_code == ERROR_BROKEN_PIPE) {
|
|
|
+ log_debug(LD_PROCESS, "Process reported broken pipe on standard out");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Extract our process_t from the hEvent member of OVERLAPPED. */
|
|
|
+ process_t *process = (process_t *)overlapped->hEvent;
|
|
|
+ process_win32_t *win32_process = process_get_win32_process(process);
|
|
|
+
|
|
|
+ if (process_win32_handle_read_completion(&win32_process->stdout_handle,
|
|
|
+ error_code,
|
|
|
+ byte_count)) {
|
|
|
+ /* Schedule our next read. */
|
|
|
+ process_notify_event_stdout(process);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/** This function is called when ReadFileEx() completes its background read
|
|
|
+ * from the child process's standard error. We notify the Process subsystem if
|
|
|
+ * there is data available for it to read from us. */
|
|
|
+STATIC VOID WINAPI
|
|
|
+process_win32_stderr_read_done(DWORD error_code,
|
|
|
+ DWORD byte_count,
|
|
|
+ LPOVERLAPPED overlapped)
|
|
|
+{
|
|
|
+ tor_assert(overlapped);
|
|
|
+ tor_assert(overlapped->hEvent);
|
|
|
+
|
|
|
+ /* This happens when we have asked ReadFileEx() to read some data, but we
|
|
|
+ * then decided to call CloseHandle() on the HANDLE. This can happen if
|
|
|
+ * someone runs process_free() in the exit_callback of process_t, which means
|
|
|
+ * we cannot call process_get_win32_process() here. */
|
|
|
+ if (error_code == ERROR_BROKEN_PIPE) {
|
|
|
+ log_debug(LD_PROCESS, "Process reported broken pipe on standard error");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Extract our process_t from the hEvent member of OVERLAPPED. */
|
|
|
+ process_t *process = (process_t *)overlapped->hEvent;
|
|
|
+ process_win32_t *win32_process = process_get_win32_process(process);
|
|
|
+
|
|
|
+ if (process_win32_handle_read_completion(&win32_process->stderr_handle,
|
|
|
+ error_code,
|
|
|
+ byte_count)) {
|
|
|
+ /* Schedule our next read. */
|
|
|
+ process_notify_event_stderr(process);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/** This function is called when WriteFileEx() completes its background write
|
|
|
+ * to the child process's standard input. We notify the Process subsystem that
|
|
|
+ * it can write data to us again. */
|
|
|
+STATIC VOID WINAPI
|
|
|
+process_win32_stdin_write_done(DWORD error_code,
|
|
|
+ DWORD byte_count,
|
|
|
+ LPOVERLAPPED overlapped)
|
|
|
+{
|
|
|
+ tor_assert(overlapped);
|
|
|
+ tor_assert(overlapped->hEvent);
|
|
|
+
|
|
|
+ (void)byte_count;
|
|
|
+
|
|
|
+ /* This happens when we have asked WriteFileEx() to write some data, but we
|
|
|
+ * then decided to call CloseHandle() on the HANDLE. This can happen if
|
|
|
+ * someone runs process_free() in the exit_callback of process_t, which means
|
|
|
+ * we cannot call process_get_win32_process() here. */
|
|
|
+ if (error_code == ERROR_BROKEN_PIPE) {
|
|
|
+ log_debug(LD_PROCESS, "Process reported broken pipe on standard input");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ process_t *process = (process_t *)overlapped->hEvent;
|
|
|
+ process_win32_t *win32_process = process_get_win32_process(process);
|
|
|
+
|
|
|
+ /* Mark our handle as not having any outstanding I/O requests. */
|
|
|
+ win32_process->stdin_handle.busy = false;
|
|
|
+
|
|
|
+ /* Check if we have been asked to write to the handle that have been marked
|
|
|
+ * as having reached EOF. */
|
|
|
+ if (BUG(win32_process->stdin_handle.reached_eof))
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (error_code == 0) {
|
|
|
+ /** Our data have been succesfully written. Clear our state and schedule
|
|
|
+ * the next write. */
|
|
|
+ win32_process->stdin_handle.data_available = 0;
|
|
|
+ memset(win32_process->stdin_handle.buffer, 0,
|
|
|
+ sizeof(win32_process->stdin_handle.buffer));
|
|
|
+
|
|
|
+ /* Schedule the next write. */
|
|
|
+ process_notify_event_stdin(process);
|
|
|
+ } else if (error_code == ERROR_HANDLE_EOF) {
|
|
|
+ /* Our WriteFileEx() call was succesful, but we reached the end of our
|
|
|
+ * file. We mark our handle as having reached EOF and returns. */
|
|
|
+ tor_assert(byte_count == 0);
|
|
|
+
|
|
|
+ win32_process->stdin_handle.reached_eof = true;
|
|
|
+ } else {
|
|
|
+ /* An error happened: We warn the user and mark our handle as having
|
|
|
+ * reached EOF */
|
|
|
+ log_warn(LD_PROCESS,
|
|
|
+ "Error in I/O completion routine from WriteFileEx(): %s",
|
|
|
+ format_win32_error(error_code));
|
|
|
+ win32_process->stdin_handle.reached_eof = true;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/** This function reads data from the given <b>handle</b>'s internal buffer and
|
|
|
+ * moves it into the given <b>buffer</b>. Additionally, we start the next
|
|
|
+ * ReadFileEx() background operation with the given <b>callback</b> as
|
|
|
+ * completion callback. Returns the number of bytes written to the buffer. */
|
|
|
+STATIC int
|
|
|
+process_win32_read_from_handle(process_win32_handle_t *handle,
|
|
|
+ buf_t *buffer,
|
|
|
+ LPOVERLAPPED_COMPLETION_ROUTINE callback)
|
|
|
+{
|
|
|
+ tor_assert(handle);
|
|
|
+ tor_assert(buffer);
|
|
|
+ tor_assert(callback);
|
|
|
+
|
|
|
+ BOOL ret = FALSE;
|
|
|
+ int bytes_available = 0;
|
|
|
+
|
|
|
+ /* We already have a request to read data that isn't complete yet. */
|
|
|
+ if (BUG(handle->busy))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ /* Check if we have been asked to read from a handle that have already told
|
|
|
+ * us that we have reached the end of the file. */
|
|
|
+ if (BUG(handle->reached_eof))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ /* This cast should be safe since our buffer can be at maximum up to
|
|
|
+ * BUFFER_SIZE in size. */
|
|
|
+ bytes_available = (int)handle->data_available;
|
|
|
+
|
|
|
+ if (handle->data_available > 0) {
|
|
|
+ /* Read data from our intermediate buffer into the process_t buffer. */
|
|
|
+ buf_add(buffer, handle->buffer, handle->data_available);
|
|
|
+
|
|
|
+ /* Reset our read state. */
|
|
|
+ handle->data_available = 0;
|
|
|
+ memset(handle->buffer, 0, sizeof(handle->buffer));
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Ask the Windows kernel to read data from our pipe into our buffer and call
|
|
|
+ * the callback function when it is done. */
|
|
|
+ ret = ReadFileEx(handle->pipe,
|
|
|
+ handle->buffer,
|
|
|
+ sizeof(handle->buffer),
|
|
|
+ &handle->overlapped,
|
|
|
+ callback);
|
|
|
+
|
|
|
+ if (! ret) {
|
|
|
+ log_warn(LD_PROCESS, "ReadFileEx() failed: %s",
|
|
|
+ format_win32_error(GetLastError()));
|
|
|
+ return bytes_available;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* We mark our handle as having a pending I/O request. */
|
|
|
+ handle->busy = true;
|
|
|
+
|
|
|
+ return bytes_available;
|
|
|
+}
|
|
|
+
|
|
|
+/** This function checks the callback values from ReadFileEx() in
|
|
|
+ * <b>error_code</b> and <b>byte_count</b> if we have read data. Returns true
|
|
|
+ * iff our caller should request more data from ReadFileEx(). */
|
|
|
+STATIC bool
|
|
|
+process_win32_handle_read_completion(process_win32_handle_t *handle,
|
|
|
+ DWORD error_code,
|
|
|
+ DWORD byte_count)
|
|
|
+{
|
|
|
+ tor_assert(handle);
|
|
|
+
|
|
|
+ /* Mark our handle as not having any outstanding I/O requests. */
|
|
|
+ handle->busy = false;
|
|
|
+
|
|
|
+ if (error_code == 0) {
|
|
|
+ /* Our ReadFileEx() call was succesful and there is data for us. */
|
|
|
+
|
|
|
+ /* This cast should be safe since byte_count should never be larger than
|
|
|
+ * BUFFER_SIZE. */
|
|
|
+ tor_assert(byte_count <= BUFFER_SIZE);
|
|
|
+ handle->data_available = (size_t)byte_count;
|
|
|
+
|
|
|
+ /* Tell our caller to schedule the next read. */
|
|
|
+ return true;
|
|
|
+ } else if (error_code == ERROR_HANDLE_EOF) {
|
|
|
+ /* Our ReadFileEx() call was succesful, but we reached the end of our file.
|
|
|
+ * We mark our handle as having reached EOF and returns. */
|
|
|
+ tor_assert(byte_count == 0);
|
|
|
+
|
|
|
+ handle->reached_eof = true;
|
|
|
+ } else {
|
|
|
+ /* An error happened: We warn the user and mark our handle as having
|
|
|
+ * reached EOF */
|
|
|
+ log_warn(LD_PROCESS,
|
|
|
+ "Error in I/O completion routine from ReadFileEx(): %s",
|
|
|
+ format_win32_error(error_code));
|
|
|
+
|
|
|
+ handle->reached_eof = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Our caller should NOT schedule the next read. */
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+#endif /* ! defined(_WIN32). */
|