1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087 |
- /* Copyright (c) 2003, Roger Dingledine
- * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2019, 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/buf/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/env.h"
- #ifdef HAVE_SYS_TIME_H
- #include <sys/time.h>
- #endif
- #ifdef HAVE_STRING_H
- #include <string.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;
- /* 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(NULL,
- 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;
- /* Close our ends of the pipes that is now owned by the child process. */
- CloseHandle(stdout_pipe_write);
- CloseHandle(stderr_pipe_write);
- CloseHandle(stdin_pipe_read);
- /* 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;
- }
- /** Terminate the given process. Returns true on success, otherwise false. */
- bool
- process_win32_terminate(process_t *process)
- {
- tor_assert(process);
- process_win32_t *win32_process = process_get_win32_process(process);
- /* Terminate our process. */
- BOOL ret;
- ret = TerminateProcess(win32_process->process_information.hProcess, 0);
- if (! ret) {
- log_warn(LD_PROCESS, "TerminateProcess() failed: %s",
- format_win32_error(GetLastError()));
- return false;
- }
- /* 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);
- return true;
- }
- /** Returns the unique process identifier for the given <b>process</b>. */
- process_pid_t
- process_win32_get_pid(process_t *process)
- {
- tor_assert(process);
- process_win32_t *win32_process = process_get_win32_process(process);
- return (process_pid_t)win32_process->process_information.dwProcessId;
- }
- /** 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;
- DWORD error_code = 0;
- 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);
- /* Because of the slightly weird API for WriteFileEx() we must set this to 0
- * before we call WriteFileEx() because WriteFileEx() does not reset the last
- * error itself when it's succesful. See comment below after the call to
- * GetLastError(). */
- SetLastError(0);
- /* 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) {
- error_code = GetLastError();
- /* No need to log at warning level for these two. */
- if (error_code == ERROR_HANDLE_EOF || error_code == ERROR_BROKEN_PIPE) {
- log_debug(LD_PROCESS, "WriteFileEx() returned EOF from pipe: %s",
- format_win32_error(error_code));
- } else {
- log_warn(LD_PROCESS, "WriteFileEx() failed: %s",
- format_win32_error(error_code));
- }
- win32_process->stdin_handle.reached_eof = true;
- return 0;
- }
- /* Here be dragons: According to MSDN's documentation for WriteFileEx() we
- * should check GetLastError() after a call to WriteFileEx() even though the
- * `ret` return value was successful. If everything is good, GetLastError()
- * returns `ERROR_SUCCESS` and nothing happens.
- *
- * XXX(ahf): I have not managed to trigger this code while stress-testing
- * this code. */
- error_code = GetLastError();
- if (error_code != ERROR_SUCCESS) {
- /* LCOV_EXCL_START */
- log_warn(LD_PROCESS, "WriteFileEx() failed after returning success: %s",
- format_win32_error(error_code));
- win32_process->stdin_handle.reached_eof = true;
- return 0;
- /* LCOV_EXCL_STOP */
- }
- /* 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. */
- 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. */
- 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. */
- 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);
- /* Move the process into an alertable state. */
- process_win32_trigger_completion_callbacks();
- /* Check if our processes are still alive. */
- /* Since the call to process_win32_timer_test_process() might call
- * process_notify_event_exit() which again might call process_free() which
- * updates the list of processes returned by process_get_all_processes() it
- * is important here that we make sure to not touch the list of processes if
- * the call to process_win32_timer_test_process() returns true. */
- bool done;
- do {
- const smartlist_t *processes = process_get_all_processes();
- done = true;
- SMARTLIST_FOREACH_BEGIN(processes, process_t *, process) {
- /* If process_win32_timer_test_process() returns true, it means that
- * smartlist_remove() might have been called on the list returned by
- * process_get_all_processes(). We start the loop over again until we
- * have a succesful run over the entire list where the list was not
- * modified. */
- if (process_win32_timer_test_process(process)) {
- done = false;
- break;
- }
- } SMARTLIST_FOREACH_END(process);
- } while (! done);
- }
- /** Test whether a given process is still alive. Notify the Process subsystem
- * if our process have died. Returns true iff the given process have
- * terminated. */
- STATIC bool
- 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 false;
- process_win32_t *win32_process = process_get_win32_process(process);
- BOOL ret = FALSE;
- DWORD exit_code = 0;
- /* Sometimes the Windows kernel wont give us the EOF/Broken Pipe error
- * message until some time after the process have actually terminated. We
- * make sure that our ReadFileEx() calls for the process have *all* returned
- * and both standard out and error have been marked as EOF before we try to
- * see if the process terminated.
- *
- * This ensures that we *never* call the exit callback of the `process_t`,
- * which potentially ends up calling `process_free()` on our `process_t`,
- * before all data have been received from the process.
- *
- * We do NOT have a check here for whether standard in reached EOF since
- * standard in's WriteFileEx() function is only called on-demand when we have
- * something to write and is thus usually not awaiting to finish any
- * operations. If we WriteFileEx() to a file that has terminated we'll simply
- * get an error from ReadFileEx() or its completion routine and move on with
- * life. */
- if (! win32_process->stdout_handle.reached_eof)
- return false;
- if (! win32_process->stderr_handle.reached_eof)
- return false;
- /* 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 false;
- }
- /* Notify our process_t that our process have terminated. Since our
- * exit_callback might decide to process_free() our process handle it is very
- * important that we do not touch the process_t after the call to
- * process_notify_event_exit(). */
- if (exit_code != STILL_ACTIVE) {
- process_notify_event_exit(process, exit_code);
- return true;
- }
- return false;
- }
- /** 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
- BOOL ret;
- DWORD error_code;
- /* Cancel any pending I/O requests: This means that instead of getting
- * ERROR_BROKEN_PIPE we get ERROR_OPERATION_ABORTED, but it doesn't seem
- * like this is needed. */
- ret = CancelIo(handle->pipe);
- 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, "CancelIo() failed: %s",
- format_win32_error(error_code));
- }
- }
- #endif
- /* Close our handle. */
- if (handle->pipe != INVALID_HANDLE_VALUE) {
- CloseHandle(handle->pipe);
- handle->pipe = INVALID_HANDLE_VALUE;
- handle->reached_eof = true;
- }
- }
- /** 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);
- /* 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);
- /* 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;
- 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 ||
- error_code == ERROR_BROKEN_PIPE) {
- /* 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;
- DWORD error_code = 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));
- }
- /* Because of the slightly weird API for ReadFileEx() we must set this to 0
- * before we call ReadFileEx() because ReadFileEx() does not reset the last
- * error itself when it's succesful. See comment below after the call to
- * GetLastError(). */
- SetLastError(0);
- /* 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) {
- error_code = GetLastError();
- /* No need to log at warning level for these two. */
- if (error_code == ERROR_HANDLE_EOF || error_code == ERROR_BROKEN_PIPE) {
- log_debug(LD_PROCESS, "ReadFileEx() returned EOF from pipe: %s",
- format_win32_error(error_code));
- } else {
- log_warn(LD_PROCESS, "ReadFileEx() failed: %s",
- format_win32_error(error_code));
- }
- handle->reached_eof = true;
- return bytes_available;
- }
- /* Here be dragons: According to MSDN's documentation for ReadFileEx() we
- * should check GetLastError() after a call to ReadFileEx() even though the
- * `ret` return value was successful. If everything is good, GetLastError()
- * returns `ERROR_SUCCESS` and nothing happens.
- *
- * XXX(ahf): I have not managed to trigger this code while stress-testing
- * this code. */
- error_code = GetLastError();
- if (error_code != ERROR_SUCCESS) {
- /* LCOV_EXCL_START */
- log_warn(LD_PROCESS, "ReadFileEx() failed after returning success: %s",
- format_win32_error(error_code));
- handle->reached_eof = true;
- return bytes_available;
- /* LCOV_EXCL_STOP */
- }
- /* 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 ||
- error_code == ERROR_BROKEN_PIPE) {
- /* Our ReadFileEx() finished, 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;
- }
- /** Format a single argument for being put on a Windows command line.
- * Returns a newly allocated string */
- STATIC char *
- format_win_cmdline_argument(const char *arg)
- {
- char *formatted_arg;
- char need_quotes;
- const char *c;
- int i;
- int bs_counter = 0;
- /* Backslash we can point to when one is inserted into the string */
- const char backslash = '\\';
- /* Smartlist of *char */
- smartlist_t *arg_chars;
- arg_chars = smartlist_new();
- /* Quote string if it contains whitespace or is empty */
- need_quotes = (strchr(arg, ' ') || strchr(arg, '\t') || '\0' == arg[0]);
- /* Build up smartlist of *chars */
- for (c=arg; *c != '\0'; c++) {
- if ('"' == *c) {
- /* Double up backslashes preceding a quote */
- for (i=0; i<(bs_counter*2); i++)
- smartlist_add(arg_chars, (void*)&backslash);
- bs_counter = 0;
- /* Escape the quote */
- smartlist_add(arg_chars, (void*)&backslash);
- smartlist_add(arg_chars, (void*)c);
- } else if ('\\' == *c) {
- /* Count backslashes until we know whether to double up */
- bs_counter++;
- } else {
- /* Don't double up slashes preceding a non-quote */
- for (i=0; i<bs_counter; i++)
- smartlist_add(arg_chars, (void*)&backslash);
- bs_counter = 0;
- smartlist_add(arg_chars, (void*)c);
- }
- }
- /* Don't double up trailing backslashes */
- for (i=0; i<bs_counter; i++)
- smartlist_add(arg_chars, (void*)&backslash);
- /* Allocate space for argument, quotes (if needed), and terminator */
- const size_t formatted_arg_len = smartlist_len(arg_chars) +
- (need_quotes ? 2 : 0) + 1;
- formatted_arg = tor_malloc_zero(formatted_arg_len);
- /* Add leading quote */
- i=0;
- if (need_quotes)
- formatted_arg[i++] = '"';
- /* Add characters */
- SMARTLIST_FOREACH(arg_chars, char*, ch,
- {
- formatted_arg[i++] = *ch;
- });
- /* Add trailing quote */
- if (need_quotes)
- formatted_arg[i++] = '"';
- formatted_arg[i] = '\0';
- smartlist_free(arg_chars);
- return formatted_arg;
- }
- /** Format a command line for use on Windows, which takes the command as a
- * string rather than string array. Follows the rules from "Parsing C++
- * Command-Line Arguments" in MSDN. Algorithm based on list2cmdline in the
- * Python subprocess module. Returns a newly allocated string */
- STATIC char *
- tor_join_win_cmdline(const char *argv[])
- {
- smartlist_t *argv_list;
- char *joined_argv;
- int i;
- /* Format each argument and put the result in a smartlist */
- argv_list = smartlist_new();
- for (i=0; argv[i] != NULL; i++) {
- smartlist_add(argv_list, (void *)format_win_cmdline_argument(argv[i]));
- }
- /* Join the arguments with whitespace */
- joined_argv = smartlist_join_strings(argv_list, " ", 0, NULL);
- /* Free the newly allocated arguments, and the smartlist */
- SMARTLIST_FOREACH(argv_list, char *, arg,
- {
- tor_free(arg);
- });
- smartlist_free(argv_list);
- return joined_argv;
- }
- #endif /* ! defined(_WIN32). */
|