/* 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 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/torlog.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 #endif #ifdef HAVE_SYS_PRCTL_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_SIGNAL_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_SYS_WAIT_H #include #endif #include #include /** 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; ichild_state and saved_errno as a hex string placed in * hex_errno. Called between fork and _exit, so must be signal-handler * safe. * * hex_errno must have at least HEX_ERRNO_SIZE+1 bytes available. * * The format of hex_errno 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; /* Fill hex_errno with spaces, and a trailing newline (memset may 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'; /* Convert errno to be unsigned for hex conversion */ if (saved_errno < 0) { // Avoid overflow on the cast to unsigned int when result is INT_MIN // by adding 1 to the signed int negative value, // then, after it has been negated and cast to unsigned, // adding the original 1 back (the double-addition is intentional). // Otherwise, the cast to signed could cause a temporary int // to equal INT_MAX + 1, which is undefined. 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; /* Emit child_state */ written = format_hex_number_sigsafe(child_state, cur, left); if (written <= 0) goto err; /* Adjust left and cur */ left -= written; cur += written; if (left <= 0) goto err; /* Now the '/' */ *cur = '/'; /* Adjust left and cur */ ++cur; --left; if (left <= 0) goto err; /* Need minus? */ if (saved_errno < 0) { *cur = '-'; ++cur; --left; if (left <= 0) goto err; } /* Emit unsigned_errno */ written = format_hex_number_sigsafe(unsigned_errno, cur, left); if (written <= 0) goto err; /* Adjust left and cur */ left -= written; cur += written; /* Check that we have enough space left for a newline and a NUL */ if (left <= 1) goto err; /* Emit the newline and NUL */ *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 /* !defined(_WIN32) */ /* Maximum number of file descriptors, if we cannot get it via sysconf() */ #define DEFAULT_MAX_FD 256 /** Terminate the process of process_handle, if that process has not * 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 /* !(defined(_WIN32)) */ if (process_handle->waitpid_cb) { /* We haven't got a waitpid yet, so we can just kill off the process. */ return kill(process_handle->pid, SIGTERM); } #endif /* defined(_WIN32) */ return 0; /* We didn't need to kill the process, so report success */ } /** Return the Process ID of process_handle. */ 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 /* !(defined(_WIN32)) */ /* DOCDOC tor_process_get_stdout_pipe */ int tor_process_get_stdout_pipe(process_handle_t *process_handle) { return process_handle->stdout_pipe; } #endif /* defined(_WIN32) */ /* DOCDOC process_handle_new */ 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 /* defined(_WIN32) */ return out; } #ifndef _WIN32 /** Invoked when a process that we've launched via tor_spawn_background() has * 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 /* !defined(_WIN32) */ /** * @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; } /** Start a program in the background. If filename contains a '/', then * it will be treated as an absolute or relative path. Otherwise, on * non-Windows systems, the system path will be searched for filename. * 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 argv 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 filename 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://msdn.microsoft.com/en-us/library/ms682499%28v=vs.85%29.aspx. */ 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)) { /* We should never reach this point if we're forbidden to spawn * 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; /* TODO: should we set explicit security attributes? (#2046, comment 5) */ saAttr.lpSecurityDescriptor = NULL; /* Assume failure to start process */ status = PROCESS_STATUS_ERROR; /* Set up pipe for stdout */ 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; } /* Set up pipe for stderr */ 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; } /* Set up pipe for stdin */ 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; } /* Create the child process */ /* Windows expects argv to be a whitespace delimited string, so join argv up */ 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; /* Create the child process */ retval = CreateProcessA(filename, // module name joined_argv, // command line /* TODO: should we set explicit security attributes? (#2046, comment 5) */ NULL, // process security attributes NULL, // primary thread security attributes TRUE, // handles are inherited /*(TODO: set CREATE_NEW CONSOLE/PROCESS_GROUP to make GetExitCodeProcess() * work?) */ CREATE_NO_WINDOW, // creation flags (env==NULL) ? NULL : env->windows_environment_block, NULL, // use parent's current directory &siStartInfo, // STARTUPINFO pointer &(process_handle->pid)); // receives PROCESS_INFORMATION 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 { /* TODO: Close hProcess and hThread in process_handle->pid? */ 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; } /* TODO: Close pipes on exit */ *process_handle_out = process_handle; return status; #else /* !(defined(_WIN32)) */ 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; /* Represents where in the process of spawning the program is; this is used for printing out the error message */ unsigned char child_state = CHILD_STATE_INIT; char hex_errno[HEX_ERRNO_SIZE + 2]; /* + 1 should be sufficient actually */ static int max_fd = -1; status = PROCESS_STATUS_ERROR; /* We do the strlen here because strlen() is not signal handler safe, and we are not allowed to use unsafe functions between fork and exec */ error_message_length = strlen(error_message); // child_state = CHILD_STATE_PIPE; /* Set up pipe for redirecting stdout, stderr, and stdin of child */ 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; } // child_state = CHILD_STATE_MAXFD; #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 /* !(defined(_SC_OPEN_MAX)) */ max_fd = DEFAULT_MAX_FD; #endif /* defined(_SC_OPEN_MAX) */ // child_state = CHILD_STATE_FORK; pid = fork(); if (0 == pid) { /* In child */ #if defined(HAVE_SYS_PRCTL_H) && defined(__linux__) /* Attempt to have the kernel issue a SIGTERM if the parent * goes away. Certain attributes of the binary being execve()ed * will clear this during the execve() call, but it's better * than nothing. */ prctl(PR_SET_PDEATHSIG, SIGTERM); #endif /* defined(HAVE_SYS_PRCTL_H) && defined(__linux__) */ child_state = CHILD_STATE_DUPOUT; /* Link child stdout to the write end of the pipe */ retval = dup2(stdout_pipe[1], STDOUT_FILENO); if (-1 == retval) goto error; child_state = CHILD_STATE_DUPERR; /* Link child stderr to the write end of the pipe */ retval = dup2(stderr_pipe[1], STDERR_FILENO); if (-1 == retval) goto error; child_state = CHILD_STATE_DUPIN; /* Link child stdin to the read end of the pipe */ retval = dup2(stdin_pipe[0], STDIN_FILENO); if (-1 == retval) goto error; // child_state = CHILD_STATE_CLOSEFD; close(stderr_pipe[0]); close(stderr_pipe[1]); close(stdout_pipe[0]); close(stdout_pipe[1]); close(stdin_pipe[0]); close(stdin_pipe[1]); /* Close all other fds, including the read end of the pipe */ /* XXX: We should now be doing enough FD_CLOEXEC setting to make * this needless. */ for (fd = STDERR_FILENO + 1; fd < max_fd; fd++) { close(fd); } // child_state = CHILD_STATE_EXEC; /* Call the requested program. We need the cast because 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); } /* If we got here, the exec or open(/dev/null) failed */ child_state = CHILD_STATE_FAILEXEC; error: { /* XXX: are we leaking fds from the pipe? */ int n, err=0; ssize_t nbytes; n = format_helper_exit_status(child_state, errno, hex_errno); if (n >= 0) { /* Write the error message. GCC requires that we check the return value, but there is nothing we can do if it fails */ /* TODO: Don't use STDOUT, use a pipe set up just for this purpose */ 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); // exit ok: in child. } /* Never reached, but avoids compiler warning */ return status; // LCOV_EXCL_LINE } /* In parent */ 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; /* TODO: If the child process forked but failed to exec, waitpid it */ /* Return read end of the pipes to caller, and close write end */ 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)); } /* Return write end of the stdin pipe to caller, and close the read end */ 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; /* Set stdin/stdout/stderr pipes to be non-blocking */ 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 /* defined(_WIN32) */ } /** Destroy all resources allocated by the process handle in * process_handle. * If also_terminate_process 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 /* !(defined(_WIN32)) */ close(process_handle->stdout_pipe); close(process_handle->stderr_pipe); close(process_handle->stdin_pipe); clear_waitpid_callback(process_handle->waitpid_cb); #endif /* defined(_WIN32) */ memset(process_handle, 0x0f, sizeof(process_handle_t)); tor_free(process_handle); } /** Get the exit code of a process specified by process_handle and store * it in exit_code, if set to a non-NULL value. If block 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) { /* Wait for the process to exit */ 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) { /* Process has not exited */ 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 /* !(defined(_WIN32)) */ int stat_loc; int retval; if (process_handle->waitpid_cb) { /* We haven't processed a SIGCHLD yet. */ 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 { /* We already got a SIGCHLD for this process, and handled it. */ retval = process_handle->pid; stat_loc = process_handle->waitpid_exit_status; } if (!block && 0 == retval) { /* Process has not exited */ 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 /* defined(_WIN32) */ return PROCESS_EXIT_EXITED; } #ifdef _WIN32 /** Read from a handle h into buf, up to count bytes. If * hProcess is NULL, the function will return immediately if there is * nothing more to read. Otherwise hProcess should be set to the handle * to the process owning the h. In this case, the function will exit * only once the process has exited, or count 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) { /* Check if there is anything to read */ 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) { /* Nothing available: process exited or it is busy */ /* Exit if we don't know whether the process is running */ if (NULL == process) break; /* The process exited and there's nothing left to read from it */ if (process_exited) break; /* If process is not running, check for output one more time in case 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; } /* There is data to read; read it */ 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) { /* End of file */ break; } numread += byte_count; } return (ssize_t)numread; } #else /* !(defined(_WIN32)) */ /** Read from a handle fd into buf, up to count bytes. If * process is NULL, the function will return immediately if there is * nothing more to read. Otherwise data will be read until end of file, or * count bytes are read. Returns the number of bytes read, or -1 on * error. Sets eof to true if eof 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 /* defined(_WIN32) */ /** Read from stdout of a process until the process exits. */ 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 /* defined(_WIN32) */ } /** Read from stdout of a process until the process exits. */ 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 /* defined(_WIN32) */ } /** Return a string corresponding to stream_status. */ 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"; } } /** Split buf into lines, and add to smartlist. The buffer buf will be * modified. The resulting smartlist will consist of pointers to buf, so there * is no need to free the contents of sl. buf must be a NUL-terminated * string. len 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) { /* Index in buf of the start of the current line */ int start = 0; /* Index in buf of the current character being processed */ int cur = 0; /* Are we currently in a line */ char in_line = 0; /* Loop over string */ while (cur < len) { /* Loop until end of line or end of string */ for (; cur < len; cur++) { if (in_line) { if ('\r' == buf[cur] || '\n' == buf[cur]) { /* End of line */ buf[cur] = '\0'; /* Point cur to the next line */ cur++; /* Line starts at start and ends with a nul */ break; } else { if (!TOR_ISPRINT(buf[cur])) buf[cur] = '.'; } } else { if ('\r' == buf[cur] || '\n' == buf[cur]) { /* Skip leading vertical space */ ; } else { in_line = 1; start = cur; if (!TOR_ISPRINT(buf[cur])) buf[cur] = '.'; } } } /* We are at the end of the line or end of string. If in_line is true there * 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 /** Return a smartlist containing lines outputted from * handle. Return NULL on error, and set * stream_status_out 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; } /* End with a null even if there isn't a \r\n at the end */ /* TODO: What if this is a partial line? */ stdout_buf[pos] = '\0'; /* Split up the buffer */ lines = smartlist_new(); tor_split_lines(lines, stdout_buf, pos); /* Currently 'lines' is populated with strings residing on the 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 /* !(defined(_WIN32)) */ /** Return a smartlist containing lines outputted from * fd. Return NULL on error, and set * stream_status_out 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 /* defined(_WIN32) */ /** Reads from fd and stores input in buf_out making * sure it's below count 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 buf_out. */ 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') { /* Remove the trailing newline */ buf_out[ret - 1] = '\0'; } else buf_out[ret] = '\0'; return IO_STREAM_OKAY; }