|
@@ -1,1236 +0,0 @@
|
|
|
-
|
|
|
- * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
|
|
- * Copyright (c) 2007-2018, The Tor Project, Inc. */
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- * \file subprocess.c
|
|
|
- * \brief Launch and monitor other processes.
|
|
|
- **/
|
|
|
-
|
|
|
-#define SUBPROCESS_PRIVATE
|
|
|
-#include "lib/process/subprocess.h"
|
|
|
-
|
|
|
-#include "lib/container/smartlist.h"
|
|
|
-#include "lib/err/torerr.h"
|
|
|
-#include "lib/log/log.h"
|
|
|
-#include "lib/log/util_bug.h"
|
|
|
-#include "lib/log/win32err.h"
|
|
|
-#include "lib/malloc/malloc.h"
|
|
|
-#include "lib/process/env.h"
|
|
|
-#include "lib/process/waitpid.h"
|
|
|
-#include "lib/string/compat_ctype.h"
|
|
|
-
|
|
|
-#ifdef HAVE_SYS_TYPES_H
|
|
|
-#include <sys/types.h>
|
|
|
-#endif
|
|
|
-#ifdef HAVE_SYS_PRCTL_H
|
|
|
-#include <sys/prctl.h>
|
|
|
-#endif
|
|
|
-#ifdef HAVE_UNISTD_H
|
|
|
-#include <unistd.h>
|
|
|
-#endif
|
|
|
-#ifdef HAVE_SIGNAL_H
|
|
|
-#include <signal.h>
|
|
|
-#endif
|
|
|
-#ifdef HAVE_FCNTL_H
|
|
|
-#include <fcntl.h>
|
|
|
-#endif
|
|
|
-#ifdef HAVE_SYS_WAIT_H
|
|
|
-#include <sys/wait.h>
|
|
|
-#endif
|
|
|
-#include <errno.h>
|
|
|
-#include <string.h>
|
|
|
-
|
|
|
-
|
|
|
- * 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;
|
|
|
-
|
|
|
- const char backslash = '\\';
|
|
|
-
|
|
|
-
|
|
|
- smartlist_t *arg_chars;
|
|
|
- arg_chars = smartlist_new();
|
|
|
-
|
|
|
-
|
|
|
- need_quotes = (strchr(arg, ' ') || strchr(arg, '\t') || '\0' == arg[0]);
|
|
|
-
|
|
|
-
|
|
|
- for (c=arg; *c != '\0'; c++) {
|
|
|
- if ('"' == *c) {
|
|
|
-
|
|
|
- for (i=0; i<(bs_counter*2); i++)
|
|
|
- smartlist_add(arg_chars, (void*)&backslash);
|
|
|
- bs_counter = 0;
|
|
|
-
|
|
|
- smartlist_add(arg_chars, (void*)&backslash);
|
|
|
- smartlist_add(arg_chars, (void*)c);
|
|
|
- } else if ('\\' == *c) {
|
|
|
-
|
|
|
- bs_counter++;
|
|
|
- } else {
|
|
|
-
|
|
|
- for (i=0; i<bs_counter; i++)
|
|
|
- smartlist_add(arg_chars, (void*)&backslash);
|
|
|
- bs_counter = 0;
|
|
|
- smartlist_add(arg_chars, (void*)c);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- for (i=0; i<bs_counter; i++)
|
|
|
- smartlist_add(arg_chars, (void*)&backslash);
|
|
|
-
|
|
|
-
|
|
|
- const size_t formatted_arg_len = smartlist_len(arg_chars) +
|
|
|
- (need_quotes ? 2 : 0) + 1;
|
|
|
- formatted_arg = tor_malloc_zero(formatted_arg_len);
|
|
|
-
|
|
|
-
|
|
|
- i=0;
|
|
|
- if (need_quotes)
|
|
|
- formatted_arg[i++] = '"';
|
|
|
-
|
|
|
-
|
|
|
- SMARTLIST_FOREACH(arg_chars, char*, ch,
|
|
|
- {
|
|
|
- formatted_arg[i++] = *ch;
|
|
|
- });
|
|
|
-
|
|
|
-
|
|
|
- if (need_quotes)
|
|
|
- formatted_arg[i++] = '"';
|
|
|
- formatted_arg[i] = '\0';
|
|
|
-
|
|
|
- smartlist_free(arg_chars);
|
|
|
- return formatted_arg;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
- * 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 */
|
|
|
-char *
|
|
|
-tor_join_win_cmdline(const char *argv[])
|
|
|
-{
|
|
|
- smartlist_t *argv_list;
|
|
|
- char *joined_argv;
|
|
|
- int i;
|
|
|
-
|
|
|
-
|
|
|
- argv_list = smartlist_new();
|
|
|
- for (i=0; argv[i] != NULL; i++) {
|
|
|
- smartlist_add(argv_list, (void *)format_win_cmdline_argument(argv[i]));
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- joined_argv = smartlist_join_strings(argv_list, " ", 0, NULL);
|
|
|
-
|
|
|
-
|
|
|
- SMARTLIST_FOREACH(argv_list, char *, arg,
|
|
|
- {
|
|
|
- tor_free(arg);
|
|
|
- });
|
|
|
- smartlist_free(argv_list);
|
|
|
-
|
|
|
- return joined_argv;
|
|
|
-}
|
|
|
-
|
|
|
-#ifndef _WIN32
|
|
|
-
|
|
|
- * <b>hex_errno</b>. Called between fork and _exit, so must be signal-handler
|
|
|
- * safe.
|
|
|
- *
|
|
|
- * <b>hex_errno</b> must have at least HEX_ERRNO_SIZE+1 bytes available.
|
|
|
- *
|
|
|
- * The format of <b>hex_errno</b> is: "CHILD_STATE/ERRNO\n", left-padded
|
|
|
- * with spaces. CHILD_STATE indicates where
|
|
|
- * in the process of starting the child process did the failure occur (see
|
|
|
- * CHILD_STATE_* macros for definition), and SAVED_ERRNO is the value of
|
|
|
- * errno when the failure occurred.
|
|
|
- *
|
|
|
- * On success return the number of characters added to hex_errno, not counting
|
|
|
- * the terminating NUL; return -1 on error.
|
|
|
- */
|
|
|
-STATIC int
|
|
|
-format_helper_exit_status(unsigned char child_state, int saved_errno,
|
|
|
- char *hex_errno)
|
|
|
-{
|
|
|
- unsigned int unsigned_errno;
|
|
|
- int written, left;
|
|
|
- char *cur;
|
|
|
- size_t i;
|
|
|
- int res = -1;
|
|
|
-
|
|
|
-
|
|
|
- not be signal handler safe, so we can't use it) */
|
|
|
- for (i = 0; i < (HEX_ERRNO_SIZE - 1); i++)
|
|
|
- hex_errno[i] = ' ';
|
|
|
- hex_errno[HEX_ERRNO_SIZE - 1] = '\n';
|
|
|
-
|
|
|
-
|
|
|
- if (saved_errno < 0) {
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- unsigned_errno = ((unsigned int) -(saved_errno + 1)) + 1;
|
|
|
- } else {
|
|
|
- unsigned_errno = (unsigned int) saved_errno;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- * Count how many chars of space we have left, and keep a pointer into the
|
|
|
- * current point in the buffer.
|
|
|
- */
|
|
|
- left = HEX_ERRNO_SIZE+1;
|
|
|
- cur = hex_errno;
|
|
|
-
|
|
|
-
|
|
|
- written = format_hex_number_sigsafe(child_state, cur, left);
|
|
|
-
|
|
|
- if (written <= 0)
|
|
|
- goto err;
|
|
|
-
|
|
|
-
|
|
|
- left -= written;
|
|
|
- cur += written;
|
|
|
- if (left <= 0)
|
|
|
- goto err;
|
|
|
-
|
|
|
-
|
|
|
- *cur = '/';
|
|
|
-
|
|
|
-
|
|
|
- ++cur;
|
|
|
- --left;
|
|
|
- if (left <= 0)
|
|
|
- goto err;
|
|
|
-
|
|
|
-
|
|
|
- if (saved_errno < 0) {
|
|
|
- *cur = '-';
|
|
|
- ++cur;
|
|
|
- --left;
|
|
|
- if (left <= 0)
|
|
|
- goto err;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- written = format_hex_number_sigsafe(unsigned_errno, cur, left);
|
|
|
-
|
|
|
- if (written <= 0)
|
|
|
- goto err;
|
|
|
-
|
|
|
-
|
|
|
- left -= written;
|
|
|
- cur += written;
|
|
|
-
|
|
|
-
|
|
|
- if (left <= 1)
|
|
|
- goto err;
|
|
|
-
|
|
|
-
|
|
|
- *cur++ = '\n';
|
|
|
- *cur++ = '\0';
|
|
|
-
|
|
|
- res = (int)(cur - hex_errno - 1);
|
|
|
-
|
|
|
- goto done;
|
|
|
-
|
|
|
- err:
|
|
|
-
|
|
|
- * In error exit, just write a '\0' in the first char so whatever called
|
|
|
- * this at least won't fall off the end.
|
|
|
- */
|
|
|
- *hex_errno = '\0';
|
|
|
-
|
|
|
- done:
|
|
|
- return res;
|
|
|
-}
|
|
|
-#endif
|
|
|
-
|
|
|
-
|
|
|
-#define DEFAULT_MAX_FD 256
|
|
|
-
|
|
|
-
|
|
|
- * already exited.
|
|
|
- *
|
|
|
- * Return 0 if we succeeded in terminating the process (or if the process
|
|
|
- * already exited), and -1 if we tried to kill the process but failed.
|
|
|
- *
|
|
|
- * Based on code originally borrowed from Python's os.kill. */
|
|
|
-int
|
|
|
-tor_terminate_process(process_handle_t *process_handle)
|
|
|
-{
|
|
|
-#ifdef _WIN32
|
|
|
- if (tor_get_exit_code(process_handle, 0, NULL) == PROCESS_EXIT_RUNNING) {
|
|
|
- HANDLE handle = process_handle->pid.hProcess;
|
|
|
-
|
|
|
- if (!TerminateProcess(handle, 0))
|
|
|
- return -1;
|
|
|
- else
|
|
|
- return 0;
|
|
|
- }
|
|
|
-#else
|
|
|
- if (process_handle->waitpid_cb) {
|
|
|
-
|
|
|
- return kill(process_handle->pid, SIGTERM);
|
|
|
- }
|
|
|
-#endif
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-int
|
|
|
-tor_process_get_pid(process_handle_t *process_handle)
|
|
|
-{
|
|
|
-#ifdef _WIN32
|
|
|
- return (int) process_handle->pid.dwProcessId;
|
|
|
-#else
|
|
|
- return (int) process_handle->pid;
|
|
|
-#endif
|
|
|
-}
|
|
|
-
|
|
|
-#ifdef _WIN32
|
|
|
-HANDLE
|
|
|
-tor_process_get_stdout_pipe(process_handle_t *process_handle)
|
|
|
-{
|
|
|
- return process_handle->stdout_pipe;
|
|
|
-}
|
|
|
-#else
|
|
|
-
|
|
|
-int
|
|
|
-tor_process_get_stdout_pipe(process_handle_t *process_handle)
|
|
|
-{
|
|
|
- return process_handle->stdout_pipe;
|
|
|
-}
|
|
|
-#endif
|
|
|
-
|
|
|
-
|
|
|
-static process_handle_t *
|
|
|
-process_handle_new(void)
|
|
|
-{
|
|
|
- process_handle_t *out = tor_malloc_zero(sizeof(process_handle_t));
|
|
|
-
|
|
|
-#ifdef _WIN32
|
|
|
- out->stdin_pipe = INVALID_HANDLE_VALUE;
|
|
|
- out->stdout_pipe = INVALID_HANDLE_VALUE;
|
|
|
- out->stderr_pipe = INVALID_HANDLE_VALUE;
|
|
|
-#else
|
|
|
- out->stdin_pipe = -1;
|
|
|
- out->stdout_pipe = -1;
|
|
|
- out->stderr_pipe = -1;
|
|
|
-#endif
|
|
|
-
|
|
|
- return out;
|
|
|
-}
|
|
|
-
|
|
|
-#ifndef _WIN32
|
|
|
-
|
|
|
- * been found to have terminated.
|
|
|
- */
|
|
|
-static void
|
|
|
-process_handle_waitpid_cb(int status, void *arg)
|
|
|
-{
|
|
|
- process_handle_t *process_handle = arg;
|
|
|
-
|
|
|
- process_handle->waitpid_exit_status = status;
|
|
|
- clear_waitpid_callback(process_handle->waitpid_cb);
|
|
|
- if (process_handle->status == PROCESS_STATUS_RUNNING)
|
|
|
- process_handle->status = PROCESS_STATUS_NOTRUNNING;
|
|
|
- process_handle->waitpid_cb = 0;
|
|
|
-}
|
|
|
-#endif
|
|
|
-
|
|
|
-
|
|
|
- * @name child-process states
|
|
|
- *
|
|
|
- * Each of these values represents a possible state that a child process can
|
|
|
- * be in. They're used to determine what to say when telling the parent how
|
|
|
- * far along we were before failure.
|
|
|
- *
|
|
|
- * @{
|
|
|
- */
|
|
|
-#define CHILD_STATE_INIT 0
|
|
|
-#define CHILD_STATE_PIPE 1
|
|
|
-#define CHILD_STATE_MAXFD 2
|
|
|
-#define CHILD_STATE_FORK 3
|
|
|
-#define CHILD_STATE_DUPOUT 4
|
|
|
-#define CHILD_STATE_DUPERR 5
|
|
|
-#define CHILD_STATE_DUPIN 6
|
|
|
-#define CHILD_STATE_CLOSEFD 7
|
|
|
-#define CHILD_STATE_EXEC 8
|
|
|
-#define CHILD_STATE_FAILEXEC 9
|
|
|
-
|
|
|
-
|
|
|
- * Boolean. If true, then Tor may call execve or CreateProcess via
|
|
|
- * tor_spawn_background.
|
|
|
- **/
|
|
|
-static int may_spawn_background_process = 1;
|
|
|
-
|
|
|
- * Turn off may_spawn_background_process, so that all future calls to
|
|
|
- * tor_spawn_background are guaranteed to fail.
|
|
|
- **/
|
|
|
-void
|
|
|
-tor_disable_spawning_background_processes(void)
|
|
|
-{
|
|
|
- may_spawn_background_process = 0;
|
|
|
-}
|
|
|
-
|
|
|
- * it will be treated as an absolute or relative path. Otherwise, on
|
|
|
- * non-Windows systems, the system path will be searched for <b>filename</b>.
|
|
|
- * On Windows, only the current directory will be searched. Here, to search the
|
|
|
- * system path (as well as the application directory, current working
|
|
|
- * directory, and system directories), set filename to NULL.
|
|
|
- *
|
|
|
- * The strings in <b>argv</b> will be passed as the command line arguments of
|
|
|
- * the child program (following convention, argv[0] should normally be the
|
|
|
- * filename of the executable, and this must be the case if <b>filename</b> is
|
|
|
- * NULL). The last element of argv must be NULL. A handle to the child process
|
|
|
- * will be returned in process_handle (which must be non-NULL). Read
|
|
|
- * process_handle.status to find out if the process was successfully launched.
|
|
|
- * For convenience, process_handle.status is returned by this function.
|
|
|
- *
|
|
|
- * Some parts of this code are based on the POSIX subprocess module from
|
|
|
- * Python, and example code from
|
|
|
- * http:
|
|
|
- */
|
|
|
-int
|
|
|
-tor_spawn_background(const char *const filename, const char **argv,
|
|
|
- process_environment_t *env,
|
|
|
- process_handle_t **process_handle_out)
|
|
|
-{
|
|
|
- if (BUG(may_spawn_background_process == 0)) {
|
|
|
-
|
|
|
- * processes. Instead we should have caught the attempt earlier. */
|
|
|
- return PROCESS_STATUS_ERROR;
|
|
|
- }
|
|
|
-
|
|
|
-#ifdef _WIN32
|
|
|
- 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;
|
|
|
- process_handle_t *process_handle;
|
|
|
- int status;
|
|
|
-
|
|
|
- STARTUPINFOA siStartInfo;
|
|
|
- BOOL retval = FALSE;
|
|
|
-
|
|
|
- SECURITY_ATTRIBUTES saAttr;
|
|
|
- char *joined_argv;
|
|
|
-
|
|
|
- saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
|
- saAttr.bInheritHandle = TRUE;
|
|
|
-
|
|
|
- saAttr.lpSecurityDescriptor = NULL;
|
|
|
-
|
|
|
-
|
|
|
- status = PROCESS_STATUS_ERROR;
|
|
|
-
|
|
|
-
|
|
|
- if (!CreatePipe(&stdout_pipe_read, &stdout_pipe_write, &saAttr, 0)) {
|
|
|
- log_warn(LD_GENERAL,
|
|
|
- "Failed to create pipe for stdout communication with child process: %s",
|
|
|
- format_win32_error(GetLastError()));
|
|
|
- return status;
|
|
|
- }
|
|
|
- if (!SetHandleInformation(stdout_pipe_read, HANDLE_FLAG_INHERIT, 0)) {
|
|
|
- log_warn(LD_GENERAL,
|
|
|
- "Failed to configure pipe for stdout communication with child "
|
|
|
- "process: %s", format_win32_error(GetLastError()));
|
|
|
- return status;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- if (!CreatePipe(&stderr_pipe_read, &stderr_pipe_write, &saAttr, 0)) {
|
|
|
- log_warn(LD_GENERAL,
|
|
|
- "Failed to create pipe for stderr communication with child process: %s",
|
|
|
- format_win32_error(GetLastError()));
|
|
|
- return status;
|
|
|
- }
|
|
|
- if (!SetHandleInformation(stderr_pipe_read, HANDLE_FLAG_INHERIT, 0)) {
|
|
|
- log_warn(LD_GENERAL,
|
|
|
- "Failed to configure pipe for stderr communication with child "
|
|
|
- "process: %s", format_win32_error(GetLastError()));
|
|
|
- return status;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- if (!CreatePipe(&stdin_pipe_read, &stdin_pipe_write, &saAttr, 0)) {
|
|
|
- log_warn(LD_GENERAL,
|
|
|
- "Failed to create pipe for stdin communication with child process: %s",
|
|
|
- format_win32_error(GetLastError()));
|
|
|
- return status;
|
|
|
- }
|
|
|
- if (!SetHandleInformation(stdin_pipe_write, HANDLE_FLAG_INHERIT, 0)) {
|
|
|
- log_warn(LD_GENERAL,
|
|
|
- "Failed to configure pipe for stdin communication with child "
|
|
|
- "process: %s", format_win32_error(GetLastError()));
|
|
|
- return status;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- */
|
|
|
- joined_argv = tor_join_win_cmdline(argv);
|
|
|
-
|
|
|
- process_handle = process_handle_new();
|
|
|
- process_handle->status = status;
|
|
|
-
|
|
|
- ZeroMemory(&(process_handle->pid), sizeof(PROCESS_INFORMATION));
|
|
|
- ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
|
|
|
- siStartInfo.cb = sizeof(STARTUPINFO);
|
|
|
- siStartInfo.hStdError = stderr_pipe_write;
|
|
|
- siStartInfo.hStdOutput = stdout_pipe_write;
|
|
|
- siStartInfo.hStdInput = stdin_pipe_read;
|
|
|
- siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- retval = CreateProcessA(filename,
|
|
|
- joined_argv,
|
|
|
-
|
|
|
- NULL,
|
|
|
- NULL,
|
|
|
- TRUE,
|
|
|
-
|
|
|
- * work?) */
|
|
|
- CREATE_NO_WINDOW,
|
|
|
- (env==NULL) ? NULL : env->windows_environment_block,
|
|
|
- NULL,
|
|
|
- &siStartInfo,
|
|
|
- &(process_handle->pid));
|
|
|
-
|
|
|
- tor_free(joined_argv);
|
|
|
-
|
|
|
- if (!retval) {
|
|
|
- log_warn(LD_GENERAL,
|
|
|
- "Failed to create child process %s: %s", filename?filename:argv[0],
|
|
|
- format_win32_error(GetLastError()));
|
|
|
- tor_free(process_handle);
|
|
|
- } else {
|
|
|
-
|
|
|
- process_handle->stdout_pipe = stdout_pipe_read;
|
|
|
- process_handle->stderr_pipe = stderr_pipe_read;
|
|
|
- process_handle->stdin_pipe = stdin_pipe_write;
|
|
|
- status = process_handle->status = PROCESS_STATUS_RUNNING;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- *process_handle_out = process_handle;
|
|
|
- return status;
|
|
|
-#else
|
|
|
- pid_t pid;
|
|
|
- int stdout_pipe[2];
|
|
|
- int stderr_pipe[2];
|
|
|
- int stdin_pipe[2];
|
|
|
- int fd, retval;
|
|
|
- process_handle_t *process_handle;
|
|
|
- int status;
|
|
|
-
|
|
|
- const char *error_message = SPAWN_ERROR_MESSAGE;
|
|
|
- size_t error_message_length;
|
|
|
-
|
|
|
-
|
|
|
- this is used for printing out the error message */
|
|
|
- unsigned char child_state = CHILD_STATE_INIT;
|
|
|
-
|
|
|
- char hex_errno[HEX_ERRNO_SIZE + 2];
|
|
|
-
|
|
|
- static int max_fd = -1;
|
|
|
-
|
|
|
- status = PROCESS_STATUS_ERROR;
|
|
|
-
|
|
|
-
|
|
|
- and we are not allowed to use unsafe functions between fork and exec */
|
|
|
- error_message_length = strlen(error_message);
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- retval = pipe(stdout_pipe);
|
|
|
- if (-1 == retval) {
|
|
|
- log_warn(LD_GENERAL,
|
|
|
- "Failed to set up pipe for stdout communication with child process: %s",
|
|
|
- strerror(errno));
|
|
|
- return status;
|
|
|
- }
|
|
|
-
|
|
|
- retval = pipe(stderr_pipe);
|
|
|
- if (-1 == retval) {
|
|
|
- log_warn(LD_GENERAL,
|
|
|
- "Failed to set up pipe for stderr communication with child process: %s",
|
|
|
- strerror(errno));
|
|
|
-
|
|
|
- close(stdout_pipe[0]);
|
|
|
- close(stdout_pipe[1]);
|
|
|
-
|
|
|
- return status;
|
|
|
- }
|
|
|
-
|
|
|
- retval = pipe(stdin_pipe);
|
|
|
- if (-1 == retval) {
|
|
|
- log_warn(LD_GENERAL,
|
|
|
- "Failed to set up pipe for stdin communication with child process: %s",
|
|
|
- strerror(errno));
|
|
|
-
|
|
|
- close(stdout_pipe[0]);
|
|
|
- close(stdout_pipe[1]);
|
|
|
- close(stderr_pipe[0]);
|
|
|
- close(stderr_pipe[1]);
|
|
|
-
|
|
|
- return status;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-#ifdef _SC_OPEN_MAX
|
|
|
- if (-1 == max_fd) {
|
|
|
- max_fd = (int) sysconf(_SC_OPEN_MAX);
|
|
|
- if (max_fd == -1) {
|
|
|
- max_fd = DEFAULT_MAX_FD;
|
|
|
- log_warn(LD_GENERAL,
|
|
|
- "Cannot find maximum file descriptor, assuming %d", max_fd);
|
|
|
- }
|
|
|
- }
|
|
|
-#else
|
|
|
- max_fd = DEFAULT_MAX_FD;
|
|
|
-#endif
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- pid = fork();
|
|
|
- if (0 == pid) {
|
|
|
-
|
|
|
-
|
|
|
-#if defined(HAVE_SYS_PRCTL_H) && defined(__linux__)
|
|
|
-
|
|
|
- * goes away. Certain attributes of the binary being execve()ed
|
|
|
- * will clear this during the execve() call, but it's better
|
|
|
- * than nothing.
|
|
|
- */
|
|
|
- prctl(PR_SET_PDEATHSIG, SIGTERM);
|
|
|
-#endif
|
|
|
-
|
|
|
- child_state = CHILD_STATE_DUPOUT;
|
|
|
-
|
|
|
-
|
|
|
- retval = dup2(stdout_pipe[1], STDOUT_FILENO);
|
|
|
- if (-1 == retval)
|
|
|
- goto error;
|
|
|
-
|
|
|
- child_state = CHILD_STATE_DUPERR;
|
|
|
-
|
|
|
-
|
|
|
- retval = dup2(stderr_pipe[1], STDERR_FILENO);
|
|
|
- if (-1 == retval)
|
|
|
- goto error;
|
|
|
-
|
|
|
- child_state = CHILD_STATE_DUPIN;
|
|
|
-
|
|
|
-
|
|
|
- retval = dup2(stdin_pipe[0], STDIN_FILENO);
|
|
|
- if (-1 == retval)
|
|
|
- goto error;
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- close(stderr_pipe[0]);
|
|
|
- close(stderr_pipe[1]);
|
|
|
- close(stdout_pipe[0]);
|
|
|
- close(stdout_pipe[1]);
|
|
|
- close(stdin_pipe[0]);
|
|
|
- close(stdin_pipe[1]);
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- * this needless. */
|
|
|
- for (fd = STDERR_FILENO + 1; fd < max_fd; fd++) {
|
|
|
- close(fd);
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- execvp doesn't define argv as const, even though it
|
|
|
- does not modify the arguments */
|
|
|
- if (env)
|
|
|
- execve(filename, (char *const *) argv, env->unixoid_environment_block);
|
|
|
- else {
|
|
|
- static char *new_env[] = { NULL };
|
|
|
- execve(filename, (char *const *) argv, new_env);
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- child_state = CHILD_STATE_FAILEXEC;
|
|
|
-
|
|
|
- error:
|
|
|
- {
|
|
|
-
|
|
|
- int n, err=0;
|
|
|
- ssize_t nbytes;
|
|
|
-
|
|
|
- n = format_helper_exit_status(child_state, errno, hex_errno);
|
|
|
-
|
|
|
- if (n >= 0) {
|
|
|
-
|
|
|
- value, but there is nothing we can do if it fails */
|
|
|
-
|
|
|
- nbytes = write(STDOUT_FILENO, error_message, error_message_length);
|
|
|
- err = (nbytes < 0);
|
|
|
- nbytes = write(STDOUT_FILENO, hex_errno, n);
|
|
|
- err += (nbytes < 0);
|
|
|
- }
|
|
|
-
|
|
|
- _exit(err?254:255);
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- return status;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- if (-1 == pid) {
|
|
|
- log_warn(LD_GENERAL, "Failed to fork child process: %s", strerror(errno));
|
|
|
- close(stdin_pipe[0]);
|
|
|
- close(stdin_pipe[1]);
|
|
|
- close(stdout_pipe[0]);
|
|
|
- close(stdout_pipe[1]);
|
|
|
- close(stderr_pipe[0]);
|
|
|
- close(stderr_pipe[1]);
|
|
|
- return status;
|
|
|
- }
|
|
|
-
|
|
|
- process_handle = process_handle_new();
|
|
|
- process_handle->status = status;
|
|
|
- process_handle->pid = pid;
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- process_handle->stdout_pipe = stdout_pipe[0];
|
|
|
- retval = close(stdout_pipe[1]);
|
|
|
-
|
|
|
- if (-1 == retval) {
|
|
|
- log_warn(LD_GENERAL,
|
|
|
- "Failed to close write end of stdout pipe in parent process: %s",
|
|
|
- strerror(errno));
|
|
|
- }
|
|
|
-
|
|
|
- process_handle->waitpid_cb = set_waitpid_callback(pid,
|
|
|
- process_handle_waitpid_cb,
|
|
|
- process_handle);
|
|
|
-
|
|
|
- process_handle->stderr_pipe = stderr_pipe[0];
|
|
|
- retval = close(stderr_pipe[1]);
|
|
|
-
|
|
|
- if (-1 == retval) {
|
|
|
- log_warn(LD_GENERAL,
|
|
|
- "Failed to close write end of stderr pipe in parent process: %s",
|
|
|
- strerror(errno));
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- process_handle->stdin_pipe = stdin_pipe[1];
|
|
|
- retval = close(stdin_pipe[0]);
|
|
|
-
|
|
|
- if (-1 == retval) {
|
|
|
- log_warn(LD_GENERAL,
|
|
|
- "Failed to close read end of stdin pipe in parent process: %s",
|
|
|
- strerror(errno));
|
|
|
- }
|
|
|
-
|
|
|
- status = process_handle->status = PROCESS_STATUS_RUNNING;
|
|
|
-
|
|
|
- if (fcntl(process_handle->stdout_pipe, F_SETFL, O_NONBLOCK) < 0 ||
|
|
|
- fcntl(process_handle->stderr_pipe, F_SETFL, O_NONBLOCK) < 0 ||
|
|
|
- fcntl(process_handle->stdin_pipe, F_SETFL, O_NONBLOCK) < 0) {
|
|
|
- log_warn(LD_GENERAL, "Failed to set stderror/stdout/stdin pipes "
|
|
|
- "nonblocking in parent process: %s", strerror(errno));
|
|
|
- }
|
|
|
-
|
|
|
- *process_handle_out = process_handle;
|
|
|
- return status;
|
|
|
-#endif
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
- * <b>process_handle</b>.
|
|
|
- * If <b>also_terminate_process</b> is true, also terminate the
|
|
|
- * process of the process handle. */
|
|
|
-MOCK_IMPL(void,
|
|
|
-tor_process_handle_destroy,(process_handle_t *process_handle,
|
|
|
- int also_terminate_process))
|
|
|
-{
|
|
|
- if (!process_handle)
|
|
|
- return;
|
|
|
-
|
|
|
- if (also_terminate_process) {
|
|
|
- if (tor_terminate_process(process_handle) < 0) {
|
|
|
- const char *errstr =
|
|
|
-#ifdef _WIN32
|
|
|
- format_win32_error(GetLastError());
|
|
|
-#else
|
|
|
- strerror(errno);
|
|
|
-#endif
|
|
|
- log_notice(LD_GENERAL, "Failed to terminate process with "
|
|
|
- "PID '%d' ('%s').", tor_process_get_pid(process_handle),
|
|
|
- errstr);
|
|
|
- } else {
|
|
|
- log_info(LD_GENERAL, "Terminated process with PID '%d'.",
|
|
|
- tor_process_get_pid(process_handle));
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- process_handle->status = PROCESS_STATUS_NOTRUNNING;
|
|
|
-
|
|
|
-#ifdef _WIN32
|
|
|
- if (process_handle->stdout_pipe)
|
|
|
- CloseHandle(process_handle->stdout_pipe);
|
|
|
-
|
|
|
- if (process_handle->stderr_pipe)
|
|
|
- CloseHandle(process_handle->stderr_pipe);
|
|
|
-
|
|
|
- if (process_handle->stdin_pipe)
|
|
|
- CloseHandle(process_handle->stdin_pipe);
|
|
|
-#else
|
|
|
- close(process_handle->stdout_pipe);
|
|
|
- close(process_handle->stderr_pipe);
|
|
|
- close(process_handle->stdin_pipe);
|
|
|
-
|
|
|
- clear_waitpid_callback(process_handle->waitpid_cb);
|
|
|
-#endif
|
|
|
-
|
|
|
- memset(process_handle, 0x0f, sizeof(process_handle_t));
|
|
|
- tor_free(process_handle);
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
- * it in <b>exit_code</b>, if set to a non-NULL value. If <b>block</b> is set
|
|
|
- * to true, the call will block until the process has exited. Otherwise if
|
|
|
- * the process is still running, the function will return
|
|
|
- * PROCESS_EXIT_RUNNING, and exit_code will be left unchanged. Returns
|
|
|
- * PROCESS_EXIT_EXITED if the process did exit. If there is a failure,
|
|
|
- * PROCESS_EXIT_ERROR will be returned and the contents of exit_code (if
|
|
|
- * non-NULL) will be undefined. N.B. Under *nix operating systems, this will
|
|
|
- * probably not work in Tor, because waitpid() is called in main.c to reap any
|
|
|
- * terminated child processes.*/
|
|
|
-int
|
|
|
-tor_get_exit_code(process_handle_t *process_handle,
|
|
|
- int block, int *exit_code)
|
|
|
-{
|
|
|
-#ifdef _WIN32
|
|
|
- DWORD retval;
|
|
|
- BOOL success;
|
|
|
-
|
|
|
- if (block) {
|
|
|
-
|
|
|
- retval = WaitForSingleObject(process_handle->pid.hProcess, INFINITE);
|
|
|
- if (retval != WAIT_OBJECT_0) {
|
|
|
- log_warn(LD_GENERAL, "WaitForSingleObject() failed (%d): %s",
|
|
|
- (int)retval, format_win32_error(GetLastError()));
|
|
|
- return PROCESS_EXIT_ERROR;
|
|
|
- }
|
|
|
- } else {
|
|
|
- retval = WaitForSingleObject(process_handle->pid.hProcess, 0);
|
|
|
- if (WAIT_TIMEOUT == retval) {
|
|
|
-
|
|
|
- return PROCESS_EXIT_RUNNING;
|
|
|
- } else if (retval != WAIT_OBJECT_0) {
|
|
|
- log_warn(LD_GENERAL, "WaitForSingleObject() failed (%d): %s",
|
|
|
- (int)retval, format_win32_error(GetLastError()));
|
|
|
- return PROCESS_EXIT_ERROR;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (exit_code != NULL) {
|
|
|
- success = GetExitCodeProcess(process_handle->pid.hProcess,
|
|
|
- (PDWORD)exit_code);
|
|
|
- if (!success) {
|
|
|
- log_warn(LD_GENERAL, "GetExitCodeProcess() failed: %s",
|
|
|
- format_win32_error(GetLastError()));
|
|
|
- return PROCESS_EXIT_ERROR;
|
|
|
- }
|
|
|
- }
|
|
|
-#else
|
|
|
- int stat_loc;
|
|
|
- int retval;
|
|
|
-
|
|
|
- if (process_handle->waitpid_cb) {
|
|
|
-
|
|
|
- retval = waitpid(process_handle->pid, &stat_loc, block?0:WNOHANG);
|
|
|
- if (retval == process_handle->pid) {
|
|
|
- clear_waitpid_callback(process_handle->waitpid_cb);
|
|
|
- process_handle->waitpid_cb = NULL;
|
|
|
- process_handle->waitpid_exit_status = stat_loc;
|
|
|
- }
|
|
|
- } else {
|
|
|
-
|
|
|
- retval = process_handle->pid;
|
|
|
- stat_loc = process_handle->waitpid_exit_status;
|
|
|
- }
|
|
|
-
|
|
|
- if (!block && 0 == retval) {
|
|
|
-
|
|
|
- return PROCESS_EXIT_RUNNING;
|
|
|
- } else if (retval != process_handle->pid) {
|
|
|
- log_warn(LD_GENERAL, "waitpid() failed for PID %d: %s",
|
|
|
- (int)process_handle->pid, strerror(errno));
|
|
|
- return PROCESS_EXIT_ERROR;
|
|
|
- }
|
|
|
-
|
|
|
- if (!WIFEXITED(stat_loc)) {
|
|
|
- log_warn(LD_GENERAL, "Process %d did not exit normally",
|
|
|
- (int)process_handle->pid);
|
|
|
- return PROCESS_EXIT_ERROR;
|
|
|
- }
|
|
|
-
|
|
|
- if (exit_code != NULL)
|
|
|
- *exit_code = WEXITSTATUS(stat_loc);
|
|
|
-#endif
|
|
|
-
|
|
|
- return PROCESS_EXIT_EXITED;
|
|
|
-}
|
|
|
-
|
|
|
-#ifdef _WIN32
|
|
|
-
|
|
|
- * <b>hProcess</b> is NULL, the function will return immediately if there is
|
|
|
- * nothing more to read. Otherwise <b>hProcess</b> should be set to the handle
|
|
|
- * to the process owning the <b>h</b>. In this case, the function will exit
|
|
|
- * only once the process has exited, or <b>count</b> bytes are read. Returns
|
|
|
- * the number of bytes read, or -1 on error. */
|
|
|
-ssize_t
|
|
|
-tor_read_all_handle(HANDLE h, char *buf, size_t count,
|
|
|
- const process_handle_t *process)
|
|
|
-{
|
|
|
- size_t numread = 0;
|
|
|
- BOOL retval;
|
|
|
- DWORD byte_count;
|
|
|
- BOOL process_exited = FALSE;
|
|
|
-
|
|
|
- if (count > SIZE_T_CEILING || count > SSIZE_MAX)
|
|
|
- return -1;
|
|
|
-
|
|
|
- while (numread < count) {
|
|
|
-
|
|
|
- retval = PeekNamedPipe(h, NULL, 0, NULL, &byte_count, NULL);
|
|
|
- if (!retval) {
|
|
|
- log_warn(LD_GENERAL,
|
|
|
- "Failed to peek from handle: %s",
|
|
|
- format_win32_error(GetLastError()));
|
|
|
- return -1;
|
|
|
- } else if (0 == byte_count) {
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- if (NULL == process)
|
|
|
- break;
|
|
|
-
|
|
|
-
|
|
|
- if (process_exited)
|
|
|
- break;
|
|
|
-
|
|
|
-
|
|
|
- it wrote something after the peek was performed. Otherwise keep on
|
|
|
- waiting for output */
|
|
|
- tor_assert(process != NULL);
|
|
|
- byte_count = WaitForSingleObject(process->pid.hProcess, 0);
|
|
|
- if (WAIT_TIMEOUT != byte_count)
|
|
|
- process_exited = TRUE;
|
|
|
-
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- retval = ReadFile(h, buf+numread, count-numread, &byte_count, NULL);
|
|
|
- tor_assert(byte_count + numread <= count);
|
|
|
- if (!retval) {
|
|
|
- log_warn(LD_GENERAL, "Failed to read from handle: %s",
|
|
|
- format_win32_error(GetLastError()));
|
|
|
- return -1;
|
|
|
- } else if (0 == byte_count) {
|
|
|
-
|
|
|
- break;
|
|
|
- }
|
|
|
- numread += byte_count;
|
|
|
- }
|
|
|
- return (ssize_t)numread;
|
|
|
-}
|
|
|
-#else
|
|
|
-
|
|
|
- * <b>process</b> is NULL, the function will return immediately if there is
|
|
|
- * nothing more to read. Otherwise data will be read until end of file, or
|
|
|
- * <b>count</b> bytes are read. Returns the number of bytes read, or -1 on
|
|
|
- * error. Sets <b>eof</b> to true if <b>eof</b> is not NULL and the end of the
|
|
|
- * file has been reached. */
|
|
|
-ssize_t
|
|
|
-tor_read_all_handle(int fd, char *buf, size_t count,
|
|
|
- const process_handle_t *process,
|
|
|
- int *eof)
|
|
|
-{
|
|
|
- size_t numread = 0;
|
|
|
- ssize_t result;
|
|
|
-
|
|
|
- if (eof)
|
|
|
- *eof = 0;
|
|
|
-
|
|
|
- if (count > SIZE_T_CEILING || count > SSIZE_MAX)
|
|
|
- return -1;
|
|
|
-
|
|
|
- while (numread < count) {
|
|
|
- result = read(fd, buf+numread, count-numread);
|
|
|
-
|
|
|
- if (result == 0) {
|
|
|
- log_debug(LD_GENERAL, "read() reached end of file");
|
|
|
- if (eof)
|
|
|
- *eof = 1;
|
|
|
- break;
|
|
|
- } else if (result < 0 && errno == EAGAIN) {
|
|
|
- if (process)
|
|
|
- continue;
|
|
|
- else
|
|
|
- break;
|
|
|
- } else if (result < 0) {
|
|
|
- log_warn(LD_GENERAL, "read() failed: %s", strerror(errno));
|
|
|
- return -1;
|
|
|
- }
|
|
|
-
|
|
|
- numread += result;
|
|
|
- }
|
|
|
-
|
|
|
- log_debug(LD_GENERAL, "read() read %d bytes from handle", (int)numread);
|
|
|
- return (ssize_t)numread;
|
|
|
-}
|
|
|
-#endif
|
|
|
-
|
|
|
-
|
|
|
-ssize_t
|
|
|
-tor_read_all_from_process_stdout(const process_handle_t *process_handle,
|
|
|
- char *buf, size_t count)
|
|
|
-{
|
|
|
-#ifdef _WIN32
|
|
|
- return tor_read_all_handle(process_handle->stdout_pipe, buf, count,
|
|
|
- process_handle);
|
|
|
-#else
|
|
|
- return tor_read_all_handle(process_handle->stdout_pipe, buf, count,
|
|
|
- process_handle, NULL);
|
|
|
-#endif
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-ssize_t
|
|
|
-tor_read_all_from_process_stderr(const process_handle_t *process_handle,
|
|
|
- char *buf, size_t count)
|
|
|
-{
|
|
|
-#ifdef _WIN32
|
|
|
- return tor_read_all_handle(process_handle->stderr_pipe, buf, count,
|
|
|
- process_handle);
|
|
|
-#else
|
|
|
- return tor_read_all_handle(process_handle->stderr_pipe, buf, count,
|
|
|
- process_handle, NULL);
|
|
|
-#endif
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-const char *
|
|
|
-stream_status_to_string(enum stream_status stream_status)
|
|
|
-{
|
|
|
- switch (stream_status) {
|
|
|
- case IO_STREAM_OKAY:
|
|
|
- return "okay";
|
|
|
- case IO_STREAM_EAGAIN:
|
|
|
- return "temporarily unavailable";
|
|
|
- case IO_STREAM_TERM:
|
|
|
- return "terminated";
|
|
|
- case IO_STREAM_CLOSED:
|
|
|
- return "closed";
|
|
|
- default:
|
|
|
- tor_fragile_assert();
|
|
|
- return "unknown";
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
- * modified. The resulting smartlist will consist of pointers to buf, so there
|
|
|
- * is no need to free the contents of sl. <b>buf</b> must be a NUL-terminated
|
|
|
- * string. <b>len</b> should be set to the length of the buffer excluding the
|
|
|
- * NUL. Non-printable characters (including NUL) will be replaced with "." */
|
|
|
-int
|
|
|
-tor_split_lines(smartlist_t *sl, char *buf, int len)
|
|
|
-{
|
|
|
-
|
|
|
- int start = 0;
|
|
|
-
|
|
|
- int cur = 0;
|
|
|
-
|
|
|
- char in_line = 0;
|
|
|
-
|
|
|
-
|
|
|
- while (cur < len) {
|
|
|
-
|
|
|
- for (; cur < len; cur++) {
|
|
|
- if (in_line) {
|
|
|
- if ('\r' == buf[cur] || '\n' == buf[cur]) {
|
|
|
-
|
|
|
- buf[cur] = '\0';
|
|
|
-
|
|
|
- cur++;
|
|
|
-
|
|
|
- break;
|
|
|
- } else {
|
|
|
- if (!TOR_ISPRINT(buf[cur]))
|
|
|
- buf[cur] = '.';
|
|
|
- }
|
|
|
- } else {
|
|
|
- if ('\r' == buf[cur] || '\n' == buf[cur]) {
|
|
|
-
|
|
|
- ;
|
|
|
- } else {
|
|
|
- in_line = 1;
|
|
|
- start = cur;
|
|
|
- if (!TOR_ISPRINT(buf[cur]))
|
|
|
- buf[cur] = '.';
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- * is a line which starts at buf+start and ends at a NUL. cur points to
|
|
|
- * the character after the NUL. */
|
|
|
- if (in_line)
|
|
|
- smartlist_add(sl, (void *)(buf+start));
|
|
|
- in_line = 0;
|
|
|
- }
|
|
|
- return smartlist_len(sl);
|
|
|
-}
|
|
|
-
|
|
|
-#ifdef _WIN32
|
|
|
-
|
|
|
-
|
|
|
- * <b>handle</b>. Return NULL on error, and set
|
|
|
- * <b>stream_status_out</b> appropriately. */
|
|
|
-MOCK_IMPL(smartlist_t *,
|
|
|
-tor_get_lines_from_handle, (HANDLE *handle,
|
|
|
- enum stream_status *stream_status_out))
|
|
|
-{
|
|
|
- int pos;
|
|
|
- char stdout_buf[600] = {0};
|
|
|
- smartlist_t *lines = NULL;
|
|
|
-
|
|
|
- tor_assert(stream_status_out);
|
|
|
-
|
|
|
- *stream_status_out = IO_STREAM_TERM;
|
|
|
-
|
|
|
- pos = tor_read_all_handle(handle, stdout_buf, sizeof(stdout_buf) - 1, NULL);
|
|
|
- if (pos < 0) {
|
|
|
- *stream_status_out = IO_STREAM_TERM;
|
|
|
- return NULL;
|
|
|
- }
|
|
|
- if (pos == 0) {
|
|
|
- *stream_status_out = IO_STREAM_EAGAIN;
|
|
|
- return NULL;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- stdout_buf[pos] = '\0';
|
|
|
-
|
|
|
-
|
|
|
- lines = smartlist_new();
|
|
|
- tor_split_lines(lines, stdout_buf, pos);
|
|
|
-
|
|
|
-
|
|
|
- stack. Replace them with their exact copies on the heap: */
|
|
|
- SMARTLIST_FOREACH(lines, char *, line,
|
|
|
- SMARTLIST_REPLACE_CURRENT(lines, line, tor_strdup(line)));
|
|
|
-
|
|
|
- *stream_status_out = IO_STREAM_OKAY;
|
|
|
-
|
|
|
- return lines;
|
|
|
-}
|
|
|
-
|
|
|
-#else
|
|
|
-
|
|
|
-
|
|
|
- * <b>fd</b>. Return NULL on error, and set
|
|
|
- * <b>stream_status_out</b> appropriately. */
|
|
|
-MOCK_IMPL(smartlist_t *,
|
|
|
-tor_get_lines_from_handle, (int fd, enum stream_status *stream_status_out))
|
|
|
-{
|
|
|
- enum stream_status stream_status;
|
|
|
- char stdout_buf[400];
|
|
|
- smartlist_t *lines = NULL;
|
|
|
-
|
|
|
- while (1) {
|
|
|
- memset(stdout_buf, 0, sizeof(stdout_buf));
|
|
|
-
|
|
|
- stream_status = get_string_from_pipe(fd,
|
|
|
- stdout_buf, sizeof(stdout_buf) - 1);
|
|
|
- if (stream_status != IO_STREAM_OKAY)
|
|
|
- goto done;
|
|
|
-
|
|
|
- if (!lines) lines = smartlist_new();
|
|
|
- smartlist_split_string(lines, stdout_buf, "\n", 0, 0);
|
|
|
- }
|
|
|
-
|
|
|
- done:
|
|
|
- *stream_status_out = stream_status;
|
|
|
- return lines;
|
|
|
-}
|
|
|
-
|
|
|
-#endif
|
|
|
-
|
|
|
-
|
|
|
- * sure it's below <b>count</b> bytes.
|
|
|
- * If the string has a trailing newline, we strip it off.
|
|
|
- *
|
|
|
- * This function is specifically created to handle input from managed
|
|
|
- * proxies, according to the pluggable transports spec. Make sure it
|
|
|
- * fits your needs before using it.
|
|
|
- *
|
|
|
- * Returns:
|
|
|
- * IO_STREAM_CLOSED: If the stream is closed.
|
|
|
- * IO_STREAM_EAGAIN: If there is nothing to read and we should check back
|
|
|
- * later.
|
|
|
- * IO_STREAM_TERM: If something is wrong with the stream.
|
|
|
- * IO_STREAM_OKAY: If everything went okay and we got a string
|
|
|
- * in <b>buf_out</b>. */
|
|
|
-enum stream_status
|
|
|
-get_string_from_pipe(int fd, char *buf_out, size_t count)
|
|
|
-{
|
|
|
- ssize_t ret;
|
|
|
-
|
|
|
- tor_assert(count <= INT_MAX);
|
|
|
-
|
|
|
- ret = read(fd, buf_out, count);
|
|
|
-
|
|
|
- if (ret == 0)
|
|
|
- return IO_STREAM_CLOSED;
|
|
|
- else if (ret < 0 && errno == EAGAIN)
|
|
|
- return IO_STREAM_EAGAIN;
|
|
|
- else if (ret < 0)
|
|
|
- return IO_STREAM_TERM;
|
|
|
-
|
|
|
- if (buf_out[ret - 1] == '\n') {
|
|
|
-
|
|
|
- buf_out[ret - 1] = '\0';
|
|
|
- } else
|
|
|
- buf_out[ret] = '\0';
|
|
|
-
|
|
|
- return IO_STREAM_OKAY;
|
|
|
-}
|