|
@@ -142,236 +142,6 @@ tor_join_win_cmdline(const char *argv[])
|
|
return joined_argv;
|
|
return joined_argv;
|
|
}
|
|
}
|
|
|
|
|
|
-#ifndef _WIN32
|
|
|
|
-/** Format <b>child_state</b> and <b>saved_errno</b> as a hex string placed in
|
|
|
|
- * <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;
|
|
|
|
-
|
|
|
|
- /* 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 <b>process_handle</b>, 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 <b>process_handle</b>. */
|
|
|
|
-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
|
|
* Boolean. If true, then Tor may call execve or CreateProcess via
|
|
* tor_spawn_background.
|
|
* tor_spawn_background.
|
|
@@ -386,851 +156,3 @@ tor_disable_spawning_background_processes(void)
|
|
{
|
|
{
|
|
may_spawn_background_process = 0;
|
|
may_spawn_background_process = 0;
|
|
}
|
|
}
|
|
-/** Start a program in the background. If <b>filename</b> 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 <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://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
|
|
|
|
- * <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 /* !(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 <b>process_handle</b> and store
|
|
|
|
- * 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) {
|
|
|
|
- /* 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 <b>h</b> into <b>buf</b>, up to <b>count</b> bytes. If
|
|
|
|
- * <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) {
|
|
|
|
- /* 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 <b>fd</b> into <b>buf</b>, up to <b>count</b> bytes. If
|
|
|
|
- * <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 /* 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 <b>stream_status</b>. */
|
|
|
|
-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 <b>buf</b> will be
|
|
|
|
- * 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)
|
|
|
|
-{
|
|
|
|
- /* 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
|
|
|
|
- * <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;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* 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
|
|
|
|
- * <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 /* defined(_WIN32) */
|
|
|
|
-
|
|
|
|
-/** Reads from <b>fd</b> and stores input in <b>buf_out</b> making
|
|
|
|
- * 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') {
|
|
|
|
- /* Remove the trailing newline */
|
|
|
|
- buf_out[ret - 1] = '\0';
|
|
|
|
- } else
|
|
|
|
- buf_out[ret] = '\0';
|
|
|
|
-
|
|
|
|
- return IO_STREAM_OKAY;
|
|
|
|
-}
|
|
|