/* 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/env.h" #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_STRING_H #include #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 process_t (see process.h) and is * reachable from process_t via the process_get_win32_process() * 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 process_win32_t. * * This function constructs a new process_win32_t 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 process_win32_t. * * This function deinitializes and frees up the resources allocated for the * given process_win32_t. */ 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 * PROCESS_STATUS_RUNNING 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; } /** 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 process. */ 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 buffer 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 buffer. * */ 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 buffer. * */ 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; /* 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 *read_pipe and *write_pipe 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 handle. */ 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 handle's internal buffer and * moves it into the given buffer. Additionally, we start the next * ReadFileEx() background operation with the given callback 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 * error_code and byte_count 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; } /** 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