aboutsummaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2018-06-28 10:53:34 -0400
committerNick Mathewson <nickm@torproject.org>2018-06-28 11:18:13 -0400
commit315e6b59ddb91c19c5102625c6cf0f22b2d6f894 (patch)
tree6b8b4a72d3ebd1292307de21b9acf8c4c3810d35 /src/common
parentec4eee63561c3f6a8bb0c466f17b92e99f832c5d (diff)
downloadtor-315e6b59ddb91c19c5102625c6cf0f22b2d6f894.tar.gz
tor-315e6b59ddb91c19c5102625c6cf0f22b2d6f894.zip
Extract process-management functionality into a new lib/process
Note that procmon does *not* go here, since procmon needs to integrate with the event loop.
Diffstat (limited to 'src/common')
-rw-r--r--src/common/compat.c502
-rw-r--r--src/common/compat.h18
-rw-r--r--src/common/include.am2
-rw-r--r--src/common/util.c1539
-rw-r--r--src/common/util.h143
-rw-r--r--src/common/util_process.c158
-rw-r--r--src/common/util_process.h26
7 files changed, 1 insertions, 2387 deletions
diff --git a/src/common/compat.c b/src/common/compat.c
index 48e706456e..d03c4a501a 100644
--- a/src/common/compat.c
+++ b/src/common/compat.c
@@ -367,421 +367,6 @@ set_max_file_descriptors(rlim_t limit, int *max_out)
return 0;
}
-#ifndef _WIN32
-/** Log details of current user and group credentials. Return 0 on
- * success. Logs and return -1 on failure.
- */
-static int
-log_credential_status(void)
-{
-/** Log level to use when describing non-error UID/GID status. */
-#define CREDENTIAL_LOG_LEVEL LOG_INFO
- /* Real, effective and saved UIDs */
- uid_t ruid, euid, suid;
- /* Read, effective and saved GIDs */
- gid_t rgid, egid, sgid;
- /* Supplementary groups */
- gid_t *sup_gids = NULL;
- int sup_gids_size;
- /* Number of supplementary groups */
- int ngids;
-
- /* log UIDs */
-#ifdef HAVE_GETRESUID
- if (getresuid(&ruid, &euid, &suid) != 0 ) {
- log_warn(LD_GENERAL, "Error getting changed UIDs: %s", strerror(errno));
- return -1;
- } else {
- log_fn(CREDENTIAL_LOG_LEVEL, LD_GENERAL,
- "UID is %u (real), %u (effective), %u (saved)",
- (unsigned)ruid, (unsigned)euid, (unsigned)suid);
- }
-#else /* !(defined(HAVE_GETRESUID)) */
- /* getresuid is not present on MacOS X, so we can't get the saved (E)UID */
- ruid = getuid();
- euid = geteuid();
- (void)suid;
-
- log_fn(CREDENTIAL_LOG_LEVEL, LD_GENERAL,
- "UID is %u (real), %u (effective), unknown (saved)",
- (unsigned)ruid, (unsigned)euid);
-#endif /* defined(HAVE_GETRESUID) */
-
- /* log GIDs */
-#ifdef HAVE_GETRESGID
- if (getresgid(&rgid, &egid, &sgid) != 0 ) {
- log_warn(LD_GENERAL, "Error getting changed GIDs: %s", strerror(errno));
- return -1;
- } else {
- log_fn(CREDENTIAL_LOG_LEVEL, LD_GENERAL,
- "GID is %u (real), %u (effective), %u (saved)",
- (unsigned)rgid, (unsigned)egid, (unsigned)sgid);
- }
-#else /* !(defined(HAVE_GETRESGID)) */
- /* getresgid is not present on MacOS X, so we can't get the saved (E)GID */
- rgid = getgid();
- egid = getegid();
- (void)sgid;
- log_fn(CREDENTIAL_LOG_LEVEL, LD_GENERAL,
- "GID is %u (real), %u (effective), unknown (saved)",
- (unsigned)rgid, (unsigned)egid);
-#endif /* defined(HAVE_GETRESGID) */
-
- /* log supplementary groups */
- sup_gids_size = 64;
- sup_gids = tor_calloc(64, sizeof(gid_t));
- while ((ngids = getgroups(sup_gids_size, sup_gids)) < 0 &&
- errno == EINVAL &&
- sup_gids_size < NGROUPS_MAX) {
- sup_gids_size *= 2;
- sup_gids = tor_reallocarray(sup_gids, sizeof(gid_t), sup_gids_size);
- }
-
- if (ngids < 0) {
- log_warn(LD_GENERAL, "Error getting supplementary GIDs: %s",
- strerror(errno));
- tor_free(sup_gids);
- return -1;
- } else {
- int i, retval = 0;
- char *s = NULL;
- smartlist_t *elts = smartlist_new();
-
- for (i = 0; i<ngids; i++) {
- smartlist_add_asprintf(elts, "%u", (unsigned)sup_gids[i]);
- }
-
- s = smartlist_join_strings(elts, " ", 0, NULL);
-
- log_fn(CREDENTIAL_LOG_LEVEL, LD_GENERAL, "Supplementary groups are: %s",s);
-
- tor_free(s);
- SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp));
- smartlist_free(elts);
- tor_free(sup_gids);
-
- return retval;
- }
-
- return 0;
-}
-#endif /* !defined(_WIN32) */
-
-/** Return true iff we were compiled with capability support, and capabilities
- * seem to work. **/
-int
-have_capability_support(void)
-{
-#ifdef HAVE_LINUX_CAPABILITIES
- cap_t caps = cap_get_proc();
- if (caps == NULL)
- return 0;
- cap_free(caps);
- return 1;
-#else /* !(defined(HAVE_LINUX_CAPABILITIES)) */
- return 0;
-#endif /* defined(HAVE_LINUX_CAPABILITIES) */
-}
-
-#ifdef HAVE_LINUX_CAPABILITIES
-/** Helper. Drop all capabilities but a small set, and set PR_KEEPCAPS as
- * appropriate.
- *
- * If pre_setuid, retain only CAP_NET_BIND_SERVICE, CAP_SETUID, and
- * CAP_SETGID, and use PR_KEEPCAPS to ensure that capabilities persist across
- * setuid().
- *
- * If not pre_setuid, retain only CAP_NET_BIND_SERVICE, and disable
- * PR_KEEPCAPS.
- *
- * Return 0 on success, and -1 on failure.
- */
-static int
-drop_capabilities(int pre_setuid)
-{
- /* We keep these three capabilities, and these only, as we setuid.
- * After we setuid, we drop all but the first. */
- const cap_value_t caplist[] = {
- CAP_NET_BIND_SERVICE, CAP_SETUID, CAP_SETGID
- };
- const char *where = pre_setuid ? "pre-setuid" : "post-setuid";
- const int n_effective = pre_setuid ? 3 : 1;
- const int n_permitted = pre_setuid ? 3 : 1;
- const int n_inheritable = 1;
- const int keepcaps = pre_setuid ? 1 : 0;
-
- /* Sets whether we keep capabilities across a setuid. */
- if (prctl(PR_SET_KEEPCAPS, keepcaps) < 0) {
- log_warn(LD_CONFIG, "Unable to call prctl() %s: %s",
- where, strerror(errno));
- return -1;
- }
-
- cap_t caps = cap_get_proc();
- if (!caps) {
- log_warn(LD_CONFIG, "Unable to call cap_get_proc() %s: %s",
- where, strerror(errno));
- return -1;
- }
- cap_clear(caps);
-
- cap_set_flag(caps, CAP_EFFECTIVE, n_effective, caplist, CAP_SET);
- cap_set_flag(caps, CAP_PERMITTED, n_permitted, caplist, CAP_SET);
- cap_set_flag(caps, CAP_INHERITABLE, n_inheritable, caplist, CAP_SET);
-
- int r = cap_set_proc(caps);
- cap_free(caps);
- if (r < 0) {
- log_warn(LD_CONFIG, "No permission to set capabilities %s: %s",
- where, strerror(errno));
- return -1;
- }
-
- return 0;
-}
-#endif /* defined(HAVE_LINUX_CAPABILITIES) */
-
-/** Call setuid and setgid to run as <b>user</b> and switch to their
- * primary group. Return 0 on success. On failure, log and return -1.
- *
- * If SWITCH_ID_KEEP_BINDLOW is set in 'flags', try to use the capability
- * system to retain the abilitity to bind low ports.
- *
- * If SWITCH_ID_WARN_IF_NO_CAPS is set in flags, also warn if we have
- * don't have capability support.
- */
-int
-switch_id(const char *user, const unsigned flags)
-{
-#ifndef _WIN32
- const struct passwd *pw = NULL;
- uid_t old_uid;
- gid_t old_gid;
- static int have_already_switched_id = 0;
- const int keep_bindlow = !!(flags & SWITCH_ID_KEEP_BINDLOW);
- const int warn_if_no_caps = !!(flags & SWITCH_ID_WARN_IF_NO_CAPS);
-
- tor_assert(user);
-
- if (have_already_switched_id)
- return 0;
-
- /* Log the initial credential state */
- if (log_credential_status())
- return -1;
-
- log_fn(CREDENTIAL_LOG_LEVEL, LD_GENERAL, "Changing user and groups");
-
- /* Get old UID/GID to check if we changed correctly */
- old_uid = getuid();
- old_gid = getgid();
-
- /* Lookup the user and group information, if we have a problem, bail out. */
- pw = tor_getpwnam(user);
- if (pw == NULL) {
- log_warn(LD_CONFIG, "Error setting configured user: %s not found", user);
- return -1;
- }
-
-#ifdef HAVE_LINUX_CAPABILITIES
- (void) warn_if_no_caps;
- if (keep_bindlow) {
- if (drop_capabilities(1))
- return -1;
- }
-#else /* !(defined(HAVE_LINUX_CAPABILITIES)) */
- (void) keep_bindlow;
- if (warn_if_no_caps) {
- log_warn(LD_CONFIG, "KeepBindCapabilities set, but no capability support "
- "on this system.");
- }
-#endif /* defined(HAVE_LINUX_CAPABILITIES) */
-
- /* Properly switch egid,gid,euid,uid here or bail out */
- if (setgroups(1, &pw->pw_gid)) {
- log_warn(LD_GENERAL, "Error setting groups to gid %d: \"%s\".",
- (int)pw->pw_gid, strerror(errno));
- if (old_uid == pw->pw_uid) {
- log_warn(LD_GENERAL, "Tor is already running as %s. You do not need "
- "the \"User\" option if you are already running as the user "
- "you want to be. (If you did not set the User option in your "
- "torrc, check whether it was specified on the command line "
- "by a startup script.)", user);
- } else {
- log_warn(LD_GENERAL, "If you set the \"User\" option, you must start Tor"
- " as root.");
- }
- return -1;
- }
-
- if (setegid(pw->pw_gid)) {
- log_warn(LD_GENERAL, "Error setting egid to %d: %s",
- (int)pw->pw_gid, strerror(errno));
- return -1;
- }
-
- if (setgid(pw->pw_gid)) {
- log_warn(LD_GENERAL, "Error setting gid to %d: %s",
- (int)pw->pw_gid, strerror(errno));
- return -1;
- }
-
- if (setuid(pw->pw_uid)) {
- log_warn(LD_GENERAL, "Error setting configured uid to %s (%d): %s",
- user, (int)pw->pw_uid, strerror(errno));
- return -1;
- }
-
- if (seteuid(pw->pw_uid)) {
- log_warn(LD_GENERAL, "Error setting configured euid to %s (%d): %s",
- user, (int)pw->pw_uid, strerror(errno));
- return -1;
- }
-
- /* This is how OpenBSD rolls:
- if (setgroups(1, &pw->pw_gid) || setegid(pw->pw_gid) ||
- setgid(pw->pw_gid) || setuid(pw->pw_uid) || seteuid(pw->pw_uid)) {
- setgid(pw->pw_gid) || seteuid(pw->pw_uid) || setuid(pw->pw_uid)) {
- log_warn(LD_GENERAL, "Error setting configured UID/GID: %s",
- strerror(errno));
- return -1;
- }
- */
-
- /* We've properly switched egid, gid, euid, uid, and supplementary groups if
- * we're here. */
-#ifdef HAVE_LINUX_CAPABILITIES
- if (keep_bindlow) {
- if (drop_capabilities(0))
- return -1;
- }
-#endif /* defined(HAVE_LINUX_CAPABILITIES) */
-
-#if !defined(CYGWIN) && !defined(__CYGWIN__)
- /* If we tried to drop privilege to a group/user other than root, attempt to
- * restore root (E)(U|G)ID, and abort if the operation succeeds */
-
- /* Only check for privilege dropping if we were asked to be non-root */
- if (pw->pw_uid) {
- /* Try changing GID/EGID */
- if (pw->pw_gid != old_gid &&
- (setgid(old_gid) != -1 || setegid(old_gid) != -1)) {
- log_warn(LD_GENERAL, "Was able to restore group credentials even after "
- "switching GID: this means that the setgid code didn't work.");
- return -1;
- }
-
- /* Try changing UID/EUID */
- if (pw->pw_uid != old_uid &&
- (setuid(old_uid) != -1 || seteuid(old_uid) != -1)) {
- log_warn(LD_GENERAL, "Was able to restore user credentials even after "
- "switching UID: this means that the setuid code didn't work.");
- return -1;
- }
- }
-#endif /* !defined(CYGWIN) && !defined(__CYGWIN__) */
-
- /* Check what really happened */
- if (log_credential_status()) {
- return -1;
- }
-
- have_already_switched_id = 1; /* mark success so we never try again */
-
-#if defined(__linux__) && defined(HAVE_SYS_PRCTL_H) && \
- defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE)
- if (pw->pw_uid) {
- /* Re-enable core dumps if we're not running as root. */
- log_info(LD_CONFIG, "Re-enabling coredumps");
- if (prctl(PR_SET_DUMPABLE, 1)) {
- log_warn(LD_CONFIG, "Unable to re-enable coredumps: %s",strerror(errno));
- }
- }
-#endif /* defined(__linux__) && defined(HAVE_SYS_PRCTL_H) && ... */
- return 0;
-
-#else /* !(!defined(_WIN32)) */
- (void)user;
- (void)flags;
-
- log_warn(LD_CONFIG, "Switching users is unsupported on your OS.");
- return -1;
-#endif /* !defined(_WIN32) */
-}
-
-/* We only use the linux prctl for now. There is no Win32 support; this may
- * also work on various BSD systems and Mac OS X - send testing feedback!
- *
- * On recent Gnu/Linux kernels it is possible to create a system-wide policy
- * that will prevent non-root processes from attaching to other processes
- * unless they are the parent process; thus gdb can attach to programs that
- * they execute but they cannot attach to other processes running as the same
- * user. The system wide policy may be set with the sysctl
- * kernel.yama.ptrace_scope or by inspecting
- * /proc/sys/kernel/yama/ptrace_scope and it is 1 by default on Ubuntu 11.04.
- *
- * This ptrace scope will be ignored on Gnu/Linux for users with
- * CAP_SYS_PTRACE and so it is very likely that root will still be able to
- * attach to the Tor process.
- */
-/** Attempt to disable debugger attachment: return 1 on success, -1 on
- * failure, and 0 if we don't know how to try on this platform. */
-int
-tor_disable_debugger_attach(void)
-{
- int r = -1;
- log_debug(LD_CONFIG,
- "Attemping to disable debugger attachment to Tor for "
- "unprivileged users.");
-#if defined(__linux__) && defined(HAVE_SYS_PRCTL_H) \
- && defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE)
-#define TRIED_TO_DISABLE
- r = prctl(PR_SET_DUMPABLE, 0);
-#elif defined(__APPLE__) && defined(PT_DENY_ATTACH)
-#define TRIED_TO_ATTACH
- r = ptrace(PT_DENY_ATTACH, 0, 0, 0);
-#endif /* defined(__linux__) && defined(HAVE_SYS_PRCTL_H) ... || ... */
-
- // XXX: TODO - Mac OS X has dtrace and this may be disabled.
- // XXX: TODO - Windows probably has something similar
-#ifdef TRIED_TO_DISABLE
- if (r == 0) {
- log_debug(LD_CONFIG,"Debugger attachment disabled for "
- "unprivileged users.");
- return 1;
- } else {
- log_warn(LD_CONFIG, "Unable to disable debugger attaching: %s",
- strerror(errno));
- }
-#endif /* defined(TRIED_TO_DISABLE) */
-#undef TRIED_TO_DISABLE
- return r;
-}
-
-#ifndef HAVE__NSGETENVIRON
-#ifndef HAVE_EXTERN_ENVIRON_DECLARED
-/* Some platforms declare environ under some circumstances, others don't. */
-#ifndef RUNNING_DOXYGEN
-extern char **environ;
-#endif
-#endif /* !defined(HAVE_EXTERN_ENVIRON_DECLARED) */
-#endif /* !defined(HAVE__NSGETENVIRON) */
-
-/** Return the current environment. This is a portable replacement for
- * 'environ'. */
-char **
-get_environment(void)
-{
-#ifdef HAVE__NSGETENVIRON
- /* This is for compatibility between OSX versions. Otherwise (for example)
- * when we do a mostly-static build on OSX 10.7, the resulting binary won't
- * work on OSX 10.6. */
- return *_NSGetEnviron();
-#else /* !(defined(HAVE__NSGETENVIRON)) */
- return environ;
-#endif /* defined(HAVE__NSGETENVIRON) */
-}
-
/** Get name of current host and write it to <b>name</b> array, whose
* length is specified by <b>namelen</b> argument. Return 0 upon
* successful completion; otherwise return return -1. (Currently,
@@ -965,93 +550,6 @@ compute_num_cpus(void)
return num_cpus;
}
-#if defined(HAVE_MLOCKALL) && HAVE_DECL_MLOCKALL && defined(RLIMIT_MEMLOCK)
-#define HAVE_UNIX_MLOCKALL
-#endif
-
-#ifdef HAVE_UNIX_MLOCKALL
-/** Attempt to raise the current and max rlimit to infinity for our process.
- * This only needs to be done once and can probably only be done when we have
- * not already dropped privileges.
- */
-static int
-tor_set_max_memlock(void)
-{
- /* Future consideration for Windows is probably SetProcessWorkingSetSize
- * This is similar to setting the memory rlimit of RLIMIT_MEMLOCK
- * http://msdn.microsoft.com/en-us/library/ms686234(VS.85).aspx
- */
-
- struct rlimit limit;
-
- /* RLIM_INFINITY is -1 on some platforms. */
- limit.rlim_cur = RLIM_INFINITY;
- limit.rlim_max = RLIM_INFINITY;
-
- if (setrlimit(RLIMIT_MEMLOCK, &limit) == -1) {
- if (errno == EPERM) {
- log_warn(LD_GENERAL, "You appear to lack permissions to change memory "
- "limits. Are you root?");
- }
- log_warn(LD_GENERAL, "Unable to raise RLIMIT_MEMLOCK: %s",
- strerror(errno));
- return -1;
- }
-
- return 0;
-}
-#endif /* defined(HAVE_UNIX_MLOCKALL) */
-
-/** Attempt to lock all current and all future memory pages.
- * This should only be called once and while we're privileged.
- * Like mlockall() we return 0 when we're successful and -1 when we're not.
- * Unlike mlockall() we return 1 if we've already attempted to lock memory.
- */
-int
-tor_mlockall(void)
-{
- static int memory_lock_attempted = 0;
-
- if (memory_lock_attempted) {
- return 1;
- }
-
- memory_lock_attempted = 1;
-
- /*
- * Future consideration for Windows may be VirtualLock
- * VirtualLock appears to implement mlock() but not mlockall()
- *
- * http://msdn.microsoft.com/en-us/library/aa366895(VS.85).aspx
- */
-
-#ifdef HAVE_UNIX_MLOCKALL
- if (tor_set_max_memlock() == 0) {
- log_debug(LD_GENERAL, "RLIMIT_MEMLOCK is now set to RLIM_INFINITY.");
- }
-
- if (mlockall(MCL_CURRENT|MCL_FUTURE) == 0) {
- log_info(LD_GENERAL, "Insecure OS paging is effectively disabled.");
- return 0;
- } else {
- if (errno == ENOSYS) {
- /* Apple - it's 2009! I'm looking at you. Grrr. */
- log_notice(LD_GENERAL, "It appears that mlockall() is not available on "
- "your platform.");
- } else if (errno == EPERM) {
- log_notice(LD_GENERAL, "It appears that you lack the permissions to "
- "lock memory. Are you root?");
- }
- log_notice(LD_GENERAL, "Unable to lock all current and future memory "
- "pages: %s", strerror(errno));
- return -1;
- }
-#else /* !(defined(HAVE_UNIX_MLOCKALL)) */
- log_warn(LD_GENERAL, "Unable to lock memory pages. mlockall() unsupported?");
- return -1;
-#endif /* defined(HAVE_UNIX_MLOCKALL) */
-}
-
/**
* On Windows, WSAEWOULDBLOCK is not always correct: when you see it,
* you need to ask the socket for its actual errno. Also, you need to
diff --git a/src/common/compat.h b/src/common/compat.h
index 574dc57201..018caab240 100644
--- a/src/common/compat.h
+++ b/src/common/compat.h
@@ -137,28 +137,10 @@ MOCK_DECL(const char *, get_uname, (void));
typedef unsigned long rlim_t;
#endif
int set_max_file_descriptors(rlim_t limit, int *max);
-int tor_disable_debugger_attach(void);
-
-#if defined(HAVE_SYS_CAPABILITY_H) && defined(HAVE_CAP_SET_PROC)
-#define HAVE_LINUX_CAPABILITIES
-#endif
-
-int have_capability_support(void);
-
-/** Flag for switch_id; see switch_id() for documentation */
-#define SWITCH_ID_KEEP_BINDLOW (1<<0)
-/** Flag for switch_id; see switch_id() for documentation */
-#define SWITCH_ID_WARN_IF_NO_CAPS (1<<1)
-int switch_id(const char *user, unsigned flags);
-
-char **get_environment(void);
-
MOCK_DECL(int, get_total_system_memory, (size_t *mem_out));
int compute_num_cpus(void);
-int tor_mlockall(void);
-
/** Macros for MIN/MAX. Never use these when the arguments could have
* side-effects.
* {With GCC extensions we could probably define a safer MIN/MAX. But
diff --git a/src/common/include.am b/src/common/include.am
index ee35b9d56a..2d7297665c 100644
--- a/src/common/include.am
+++ b/src/common/include.am
@@ -29,7 +29,6 @@ LIBOR_A_SRC = \
src/common/compat.c \
src/common/compat_time.c \
src/common/util.c \
- src/common/util_process.c \
src/common/token_bucket.c \
src/common/workqueue.c \
$(libor_extra_source) \
@@ -71,7 +70,6 @@ COMMONHEADERS = \
src/common/timers.h \
src/common/token_bucket.h \
src/common/util.h \
- src/common/util_process.h \
src/common/workqueue.h
noinst_HEADERS+= $(COMMONHEADERS)
diff --git a/src/common/util.c b/src/common/util.c
index f641c1081c..4a26998c19 100644
--- a/src/common/util.c
+++ b/src/common/util.c
@@ -23,7 +23,7 @@
#include "lib/net/address.h"
#include "lib/sandbox/sandbox.h"
#include "lib/err/backtrace.h"
-#include "common/util_process.h"
+#include "lib/process/waitpid.h"
#include "lib/encoding/binascii.h"
#ifdef _WIN32
@@ -445,179 +445,6 @@ tv_to_msec(const struct timeval *tv)
return conv;
}
-/* =====
- * File helpers
- * ===== */
-
-/*
- * Filesystem operations.
- */
-
-/* =====
- * Process helpers
- * ===== */
-
-#ifndef _WIN32
-/* Based on code contributed by christian grothoff */
-/** True iff we've called start_daemon(). */
-static int start_daemon_called = 0;
-/** True iff we've called finish_daemon(). */
-static int finish_daemon_called = 0;
-/** Socketpair used to communicate between parent and child process while
- * daemonizing. */
-static int daemon_filedes[2];
-/** Start putting the process into daemon mode: fork and drop all resources
- * except standard fds. The parent process never returns, but stays around
- * until finish_daemon is called. (Note: it's safe to call this more
- * than once: calls after the first are ignored.)
- */
-void
-start_daemon(void)
-{
- pid_t pid;
-
- if (start_daemon_called)
- return;
- start_daemon_called = 1;
-
- if (pipe(daemon_filedes)) {
- /* LCOV_EXCL_START */
- log_err(LD_GENERAL,"pipe failed; exiting. Error was %s", strerror(errno));
- exit(1); // exit ok: during daemonize, pipe failed.
- /* LCOV_EXCL_STOP */
- }
- pid = fork();
- if (pid < 0) {
- /* LCOV_EXCL_START */
- log_err(LD_GENERAL,"fork failed. Exiting.");
- exit(1); // exit ok: during daemonize, fork failed
- /* LCOV_EXCL_STOP */
- }
- if (pid) { /* Parent */
- int ok;
- char c;
-
- close(daemon_filedes[1]); /* we only read */
- ok = -1;
- while (0 < read(daemon_filedes[0], &c, sizeof(char))) {
- if (c == '.')
- ok = 1;
- }
- fflush(stdout);
- if (ok == 1)
- exit(0); // exit ok: during daemonize, daemonizing.
- else
- exit(1); /* child reported error. exit ok: daemonize failed. */
- } else { /* Child */
- close(daemon_filedes[0]); /* we only write */
-
- (void) setsid(); /* Detach from controlling terminal */
- /*
- * Fork one more time, so the parent (the session group leader) can exit.
- * This means that we, as a non-session group leader, can never regain a
- * controlling terminal. This part is recommended by Stevens's
- * _Advanced Programming in the Unix Environment_.
- */
- if (fork() != 0) {
- exit(0); // exit ok: during daemonize, fork failed (2)
- }
- set_main_thread(); /* We are now the main thread. */
-
- return;
- }
-}
-
-/** Finish putting the process into daemon mode: drop standard fds, and tell
- * the parent process to exit. (Note: it's safe to call this more than once:
- * calls after the first are ignored. Calls start_daemon first if it hasn't
- * been called already.)
- */
-void
-finish_daemon(const char *desired_cwd)
-{
- int nullfd;
- char c = '.';
- if (finish_daemon_called)
- return;
- if (!start_daemon_called)
- start_daemon();
- finish_daemon_called = 1;
-
- if (!desired_cwd)
- desired_cwd = "/";
- /* Don't hold the wrong FS mounted */
- if (chdir(desired_cwd) < 0) {
- log_err(LD_GENERAL,"chdir to \"%s\" failed. Exiting.",desired_cwd);
- exit(1); // exit ok: during daemonize, chdir failed.
- }
-
- nullfd = tor_open_cloexec("/dev/null", O_RDWR, 0);
- if (nullfd < 0) {
- /* LCOV_EXCL_START */
- log_err(LD_GENERAL,"/dev/null can't be opened. Exiting.");
- exit(1); // exit ok: during daemonize, couldn't open /dev/null
- /* LCOV_EXCL_STOP */
- }
- /* close fds linking to invoking terminal, but
- * close usual incoming fds, but redirect them somewhere
- * useful so the fds don't get reallocated elsewhere.
- */
- if (dup2(nullfd,0) < 0 ||
- dup2(nullfd,1) < 0 ||
- dup2(nullfd,2) < 0) {
- /* LCOV_EXCL_START */
- log_err(LD_GENERAL,"dup2 failed. Exiting.");
- exit(1); // exit ok: during daemonize, dup2 failed.
- /* LCOV_EXCL_STOP */
- }
- if (nullfd > 2)
- close(nullfd);
- /* signal success */
- if (write(daemon_filedes[1], &c, sizeof(char)) != sizeof(char)) {
- log_err(LD_GENERAL,"write failed. Exiting.");
- }
- close(daemon_filedes[1]);
-}
-#else /* !(!defined(_WIN32)) */
-/* defined(_WIN32) */
-void
-start_daemon(void)
-{
-}
-void
-finish_daemon(const char *cp)
-{
- (void)cp;
-}
-#endif /* !defined(_WIN32) */
-
-/** Write the current process ID, followed by NL, into <b>filename</b>.
- * Return 0 on success, -1 on failure.
- */
-int
-write_pidfile(const char *filename)
-{
- FILE *pidfile;
-
- if ((pidfile = fopen(filename, "w")) == NULL) {
- log_warn(LD_FS, "Unable to open \"%s\" for writing: %s", filename,
- strerror(errno));
- return -1;
- } else {
-#ifdef _WIN32
- int pid = (int)_getpid();
-#else
- int pid = (int)getpid();
-#endif
- int rv = 0;
- if (fprintf(pidfile, "%d\n", pid) < 0)
- rv = -1;
- if (fclose(pidfile) < 0)
- rv = -1;
- return rv;
- }
-}
-
#ifdef _WIN32
HANDLE
load_windows_system_library(const TCHAR *library_name)
@@ -633,1370 +460,6 @@ load_windows_system_library(const TCHAR *library_name)
}
#endif /* defined(_WIN32) */
-/** Format a single argument for being put on a Windows command line.
- * Returns a newly allocated string */
-static char *
-format_win_cmdline_argument(const char *arg)
-{
- char *formatted_arg;
- char need_quotes;
- const char *c;
- int i;
- int bs_counter = 0;
- /* Backslash we can point to when one is inserted into the string */
- const char backslash = '\\';
-
- /* Smartlist of *char */
- smartlist_t *arg_chars;
- arg_chars = smartlist_new();
-
- /* Quote string if it contains whitespace or is empty */
- need_quotes = (strchr(arg, ' ') || strchr(arg, '\t') || '\0' == arg[0]);
-
- /* Build up smartlist of *chars */
- for (c=arg; *c != '\0'; c++) {
- if ('"' == *c) {
- /* Double up backslashes preceding a quote */
- for (i=0; i<(bs_counter*2); i++)
- smartlist_add(arg_chars, (void*)&backslash);
- bs_counter = 0;
- /* Escape the quote */
- smartlist_add(arg_chars, (void*)&backslash);
- smartlist_add(arg_chars, (void*)c);
- } else if ('\\' == *c) {
- /* Count backslashes until we know whether to double up */
- bs_counter++;
- } else {
- /* Don't double up slashes preceding a non-quote */
- for (i=0; i<bs_counter; i++)
- smartlist_add(arg_chars, (void*)&backslash);
- bs_counter = 0;
- smartlist_add(arg_chars, (void*)c);
- }
- }
- /* Don't double up trailing backslashes */
- for (i=0; i<bs_counter; i++)
- smartlist_add(arg_chars, (void*)&backslash);
-
- /* Allocate space for argument, quotes (if needed), and terminator */
- const size_t formatted_arg_len = smartlist_len(arg_chars) +
- (need_quotes ? 2 : 0) + 1;
- formatted_arg = tor_malloc_zero(formatted_arg_len);
-
- /* Add leading quote */
- i=0;
- if (need_quotes)
- formatted_arg[i++] = '"';
-
- /* Add characters */
- SMARTLIST_FOREACH(arg_chars, char*, ch,
- {
- formatted_arg[i++] = *ch;
- });
-
- /* Add trailing quote */
- if (need_quotes)
- formatted_arg[i++] = '"';
- formatted_arg[i] = '\0';
-
- smartlist_free(arg_chars);
- return formatted_arg;
-}
-
-/** Format a command line for use on Windows, which takes the command as a
- * string rather than string array. Follows the rules from "Parsing C++
- * Command-Line Arguments" in MSDN. Algorithm based on list2cmdline in the
- * Python subprocess module. Returns a newly allocated string */
-char *
-tor_join_win_cmdline(const char *argv[])
-{
- smartlist_t *argv_list;
- char *joined_argv;
- int i;
-
- /* Format each argument and put the result in a smartlist */
- argv_list = smartlist_new();
- for (i=0; argv[i] != NULL; i++) {
- smartlist_add(argv_list, (void *)format_win_cmdline_argument(argv[i]));
- }
-
- /* Join the arguments with whitespace */
- joined_argv = smartlist_join_strings(argv_list, " ", 0, NULL);
-
- /* Free the newly allocated arguments, and the smartlist */
- SMARTLIST_FOREACH(argv_list, char *, arg,
- {
- tor_free(arg);
- });
- smartlist_free(argv_list);
-
- return joined_argv;
-}
-
-#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
- * 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 <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;
-}
-
-/** Helper: return the number of characters in <b>s</b> preceding the first
- * occurrence of <b>ch</b>. If <b>ch</b> does not occur in <b>s</b>, return
- * the length of <b>s</b>. Should be equivalent to strspn(s, "ch"). */
-static inline size_t
-str_num_before(const char *s, char ch)
-{
- const char *cp = strchr(s, ch);
- if (cp)
- return cp - s;
- else
- return strlen(s);
-}
-
-/** Return non-zero iff getenv would consider <b>s1</b> and <b>s2</b>
- * to have the same name as strings in a process's environment. */
-int
-environment_variable_names_equal(const char *s1, const char *s2)
-{
- size_t s1_name_len = str_num_before(s1, '=');
- size_t s2_name_len = str_num_before(s2, '=');
-
- return (s1_name_len == s2_name_len &&
- tor_memeq(s1, s2, s1_name_len));
-}
-
-/** Free <b>env</b> (assuming it was produced by
- * process_environment_make). */
-void
-process_environment_free_(process_environment_t *env)
-{
- if (env == NULL) return;
-
- /* As both an optimization hack to reduce consing on Unixoid systems
- * and a nice way to ensure that some otherwise-Windows-specific
- * code will always get tested before changes to it get merged, the
- * strings which env->unixoid_environment_block points to are packed
- * into env->windows_environment_block. */
- tor_free(env->unixoid_environment_block);
- tor_free(env->windows_environment_block);
-
- tor_free(env);
-}
-
-/** Make a process_environment_t containing the environment variables
- * specified in <b>env_vars</b> (as C strings of the form
- * "NAME=VALUE"). */
-process_environment_t *
-process_environment_make(struct smartlist_t *env_vars)
-{
- process_environment_t *env = tor_malloc_zero(sizeof(process_environment_t));
- int n_env_vars = smartlist_len(env_vars);
- int i;
- size_t total_env_length;
- smartlist_t *env_vars_sorted;
-
- tor_assert(n_env_vars + 1 != 0);
- env->unixoid_environment_block = tor_calloc(n_env_vars + 1, sizeof(char *));
- /* env->unixoid_environment_block is already NULL-terminated,
- * because we assume that NULL == 0 (and check that during compilation). */
-
- total_env_length = 1; /* terminating NUL of terminating empty string */
- for (i = 0; i < n_env_vars; ++i) {
- const char *s = smartlist_get(env_vars, (int)i);
- size_t slen = strlen(s);
-
- tor_assert(slen + 1 != 0);
- tor_assert(slen + 1 < SIZE_MAX - total_env_length);
- total_env_length += slen + 1;
- }
-
- env->windows_environment_block = tor_malloc_zero(total_env_length);
- /* env->windows_environment_block is already
- * (NUL-terminated-empty-string)-terminated. */
-
- /* Some versions of Windows supposedly require that environment
- * blocks be sorted. Or maybe some Windows programs (or their
- * runtime libraries) fail to look up strings in non-sorted
- * environment blocks.
- *
- * Also, sorting strings makes it easy to find duplicate environment
- * variables and environment-variable strings without an '=' on all
- * OSes, and they can cause badness. Let's complain about those. */
- env_vars_sorted = smartlist_new();
- smartlist_add_all(env_vars_sorted, env_vars);
- smartlist_sort_strings(env_vars_sorted);
-
- /* Now copy the strings into the environment blocks. */
- {
- char *cp = env->windows_environment_block;
- const char *prev_env_var = NULL;
-
- for (i = 0; i < n_env_vars; ++i) {
- const char *s = smartlist_get(env_vars_sorted, (int)i);
- size_t slen = strlen(s);
- size_t s_name_len = str_num_before(s, '=');
-
- if (s_name_len == slen) {
- log_warn(LD_GENERAL,
- "Preparing an environment containing a variable "
- "without a value: %s",
- s);
- }
- if (prev_env_var != NULL &&
- environment_variable_names_equal(s, prev_env_var)) {
- log_warn(LD_GENERAL,
- "Preparing an environment containing two variables "
- "with the same name: %s and %s",
- prev_env_var, s);
- }
-
- prev_env_var = s;
-
- /* Actually copy the string into the environment. */
- memcpy(cp, s, slen+1);
- env->unixoid_environment_block[i] = cp;
- cp += slen+1;
- }
-
- tor_assert(cp == env->windows_environment_block + total_env_length - 1);
- }
-
- smartlist_free(env_vars_sorted);
-
- return env;
-}
-
-/** Return a newly allocated smartlist containing every variable in
- * this process's environment, as a NUL-terminated string of the form
- * "NAME=VALUE". Note that on some/many/most/all OSes, the parent
- * process can put strings not of that form in our environment;
- * callers should try to not get crashed by that.
- *
- * The returned strings are heap-allocated, and must be freed by the
- * caller. */
-struct smartlist_t *
-get_current_process_environment_variables(void)
-{
- smartlist_t *sl = smartlist_new();
-
- char **environ_tmp; /* Not const char ** ? Really? */
- for (environ_tmp = get_environment(); *environ_tmp; ++environ_tmp) {
- smartlist_add_strdup(sl, *environ_tmp);
- }
-
- return sl;
-}
-
-/** For each string s in <b>env_vars</b> such that
- * environment_variable_names_equal(s, <b>new_var</b>), remove it; if
- * <b>free_p</b> is non-zero, call <b>free_old</b>(s). If
- * <b>new_var</b> contains '=', insert it into <b>env_vars</b>. */
-void
-set_environment_variable_in_smartlist(struct smartlist_t *env_vars,
- const char *new_var,
- void (*free_old)(void*),
- int free_p)
-{
- SMARTLIST_FOREACH_BEGIN(env_vars, const char *, s) {
- if (environment_variable_names_equal(s, new_var)) {
- SMARTLIST_DEL_CURRENT(env_vars, s);
- if (free_p) {
- free_old((void *)s);
- }
- }
- } SMARTLIST_FOREACH_END(s);
-
- if (strchr(new_var, '=') != NULL) {
- smartlist_add(env_vars, (void *)new_var);
- }
-}
-
-#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) */
-}
-
-/** 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);
-}
-
-/** 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";
- }
-}
-
-#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;
-}
-
/** Initialize the insecure RNG <b>rng</b> from a seed value <b>seed</b>. */
void
tor_init_weak_random(tor_weak_rng_t *rng, unsigned seed)
diff --git a/src/common/util.h b/src/common/util.h
index 549bbf9aaa..1967d23e41 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -91,138 +91,10 @@ int64_t tv_to_msec(const struct timeval *tv);
((isSock) ? read_all_from_socket((fd), (buf), (count)) \
: read_all_from_fd((int)(fd), (buf), (count)))
-/** Status of an I/O stream. */
-enum stream_status {
- IO_STREAM_OKAY,
- IO_STREAM_EAGAIN,
- IO_STREAM_TERM,
- IO_STREAM_CLOSED
-};
-
-const char *stream_status_to_string(enum stream_status stream_status);
-
-enum stream_status get_string_from_pipe(int fd, char *buf, size_t count);
-
-/* Process helpers */
-void start_daemon(void);
-void finish_daemon(const char *desired_cwd);
-int write_pidfile(const char *filename);
-
-void tor_disable_spawning_background_processes(void);
-
-typedef struct process_handle_t process_handle_t;
-typedef struct process_environment_t process_environment_t;
-int tor_spawn_background(const char *const filename, const char **argv,
- process_environment_t *env,
- process_handle_t **process_handle_out);
-
-#define SPAWN_ERROR_MESSAGE "ERR: Failed to spawn background process - code "
-
#ifdef _WIN32
HANDLE load_windows_system_library(const TCHAR *library_name);
#endif
-int environment_variable_names_equal(const char *s1, const char *s2);
-
-/* DOCDOC process_environment_t */
-struct process_environment_t {
- /** A pointer to a sorted empty-string-terminated sequence of
- * NUL-terminated strings of the form "NAME=VALUE". */
- char *windows_environment_block;
- /** A pointer to a NULL-terminated array of pointers to
- * NUL-terminated strings of the form "NAME=VALUE". */
- char **unixoid_environment_block;
-};
-
-process_environment_t *process_environment_make(struct smartlist_t *env_vars);
-void process_environment_free_(process_environment_t *env);
-#define process_environment_free(env) \
- FREE_AND_NULL(process_environment_t, process_environment_free_, (env))
-
-struct smartlist_t *get_current_process_environment_variables(void);
-
-void set_environment_variable_in_smartlist(struct smartlist_t *env_vars,
- const char *new_var,
- void (*free_old)(void*),
- int free_p);
-
-/* Values of process_handle_t.status. */
-#define PROCESS_STATUS_NOTRUNNING 0
-#define PROCESS_STATUS_RUNNING 1
-#define PROCESS_STATUS_ERROR -1
-
-#ifdef UTIL_PRIVATE
-struct waitpid_callback_t;
-/** Structure to represent the state of a process with which Tor is
- * communicating. The contents of this structure are private to util.c */
-struct process_handle_t {
- /** One of the PROCESS_STATUS_* values */
- int status;
-#ifdef _WIN32
- HANDLE stdin_pipe;
- HANDLE stdout_pipe;
- HANDLE stderr_pipe;
- PROCESS_INFORMATION pid;
-#else /* !(defined(_WIN32)) */
- int stdin_pipe;
- int stdout_pipe;
- int stderr_pipe;
- pid_t pid;
- /** If the process has not given us a SIGCHLD yet, this has the
- * waitpid_callback_t that gets invoked once it has. Otherwise this
- * contains NULL. */
- struct waitpid_callback_t *waitpid_cb;
- /** The exit status reported by waitpid. */
- int waitpid_exit_status;
-#endif /* defined(_WIN32) */
-};
-#endif /* defined(UTIL_PRIVATE) */
-
-/* Return values of tor_get_exit_code() */
-#define PROCESS_EXIT_RUNNING 1
-#define PROCESS_EXIT_EXITED 0
-#define PROCESS_EXIT_ERROR -1
-int tor_get_exit_code(process_handle_t *process_handle,
- int block, int *exit_code);
-int tor_split_lines(struct smartlist_t *sl, char *buf, int len);
-#ifdef _WIN32
-ssize_t tor_read_all_handle(HANDLE h, char *buf, size_t count,
- const process_handle_t *process);
-#else
-ssize_t tor_read_all_handle(int fd, char *buf, size_t count,
- const process_handle_t *process,
- int *eof);
-#endif /* defined(_WIN32) */
-ssize_t tor_read_all_from_process_stdout(
- const process_handle_t *process_handle, char *buf, size_t count);
-ssize_t tor_read_all_from_process_stderr(
- const process_handle_t *process_handle, char *buf, size_t count);
-char *tor_join_win_cmdline(const char *argv[]);
-
-int tor_process_get_pid(process_handle_t *process_handle);
-#ifdef _WIN32
-HANDLE tor_process_get_stdout_pipe(process_handle_t *process_handle);
-#else
-int tor_process_get_stdout_pipe(process_handle_t *process_handle);
-#endif
-
-#ifdef _WIN32
-MOCK_DECL(struct smartlist_t *,
-tor_get_lines_from_handle,(HANDLE *handle,
- enum stream_status *stream_status));
-#else
-MOCK_DECL(struct smartlist_t *,
-tor_get_lines_from_handle,(int fd,
- enum stream_status *stream_status));
-#endif /* defined(_WIN32) */
-
-int
-tor_terminate_process(process_handle_t *process_handle);
-
-MOCK_DECL(void,
-tor_process_handle_destroy,(process_handle_t *process_handle,
- int also_terminate_process));
-
/* ===== Insecure rng */
typedef struct tor_weak_rng_t {
uint32_t state;
@@ -237,19 +109,4 @@ int32_t tor_weak_random_range(tor_weak_rng_t *rng, int32_t top);
* <b>n</b> */
#define tor_weak_random_one_in_n(rng, n) (0==tor_weak_random_range((rng),(n)))
-#ifdef UTIL_PRIVATE
-/* Prototypes for private functions only used by util.c (and unit tests) */
-
-#ifndef _WIN32
-STATIC int format_helper_exit_status(unsigned char child_state,
- int saved_errno, char *hex_errno);
-
-/* Space for hex values of child state, a slash, saved_errno (with
- leading minus) and newline (no null) */
-#define HEX_ERRNO_SIZE (sizeof(char) * 2 + 1 + \
- 1 + sizeof(int) * 2 + 1)
-#endif /* !defined(_WIN32) */
-
-#endif /* defined(UTIL_PRIVATE) */
-
#endif /* !defined(TOR_UTIL_H) */
diff --git a/src/common/util_process.c b/src/common/util_process.c
deleted file mode 100644
index 321258b694..0000000000
--- a/src/common/util_process.c
+++ /dev/null
@@ -1,158 +0,0 @@
-/* Copyright (c) 2003-2004, Roger Dingledine
- * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2018, The Tor Project, Inc. */
-/* See LICENSE for licensing information */
-
-/**
- * \file util_process.c
- * \brief utility functions for launching processes and checking their
- * status. These functions are kept separately from procmon so that they
- * won't require linking against libevent.
- **/
-
-#include "orconfig.h"
-
-#ifdef HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-#ifdef HAVE_SYS_WAIT_H
-#include <sys/wait.h>
-#endif
-
-#include "common/compat.h"
-#include "common/util.h"
-#include "lib/log/torlog.h"
-#include "common/util_process.h"
-#include "ht.h"
-
-/* ================================================== */
-/* Convenience structures for handlers for waitpid().
- *
- * The tor_process_monitor*() code above doesn't use them, since it is for
- * monitoring a non-child process.
- */
-
-#ifndef _WIN32
-
-/** Mapping from a PID to a userfn/userdata pair. */
-struct waitpid_callback_t {
- HT_ENTRY(waitpid_callback_t) node;
- pid_t pid;
-
- void (*userfn)(int, void *userdata);
- void *userdata;
-
- unsigned running;
-};
-
-static inline unsigned int
-process_map_entry_hash_(const waitpid_callback_t *ent)
-{
- return (unsigned) ent->pid;
-}
-
-static inline unsigned int
-process_map_entries_eq_(const waitpid_callback_t *a,
- const waitpid_callback_t *b)
-{
- return a->pid == b->pid;
-}
-
-static HT_HEAD(process_map, waitpid_callback_t) process_map = HT_INITIALIZER();
-
-HT_PROTOTYPE(process_map, waitpid_callback_t, node, process_map_entry_hash_,
- process_map_entries_eq_)
-HT_GENERATE2(process_map, waitpid_callback_t, node, process_map_entry_hash_,
- process_map_entries_eq_, 0.6, tor_reallocarray_, tor_free_)
-
-/**
- * Begin monitoring the child pid <b>pid</b> to see if we get a SIGCHLD for
- * it. If we eventually do, call <b>fn</b>, passing it the exit status (as
- * yielded by waitpid) and the pointer <b>arg</b>.
- *
- * To cancel this, or clean up after it has triggered, call
- * clear_waitpid_callback().
- */
-waitpid_callback_t *
-set_waitpid_callback(pid_t pid, void (*fn)(int, void *), void *arg)
-{
- waitpid_callback_t *old_ent;
- waitpid_callback_t *ent = tor_malloc_zero(sizeof(waitpid_callback_t));
- ent->pid = pid;
- ent->userfn = fn;
- ent->userdata = arg;
- ent->running = 1;
-
- old_ent = HT_REPLACE(process_map, &process_map, ent);
- if (old_ent) {
- log_warn(LD_BUG, "Replaced a waitpid monitor on pid %u. That should be "
- "impossible.", (unsigned) pid);
- old_ent->running = 0;
- }
-
- return ent;
-}
-
-/**
- * Cancel a waitpid_callback_t, or clean up after one has triggered. Releases
- * all storage held by <b>ent</b>.
- */
-void
-clear_waitpid_callback(waitpid_callback_t *ent)
-{
- waitpid_callback_t *old_ent;
- if (ent == NULL)
- return;
-
- if (ent->running) {
- old_ent = HT_REMOVE(process_map, &process_map, ent);
- if (old_ent != ent) {
- log_warn(LD_BUG, "Couldn't remove waitpid monitor for pid %u.",
- (unsigned) ent->pid);
- return;
- }
- }
-
- tor_free(ent);
-}
-
-/** Helper: find the callack for <b>pid</b>; if there is one, run it,
- * reporting the exit status as <b>status</b>. */
-static void
-notify_waitpid_callback_by_pid(pid_t pid, int status)
-{
- waitpid_callback_t search, *ent;
-
- search.pid = pid;
- ent = HT_REMOVE(process_map, &process_map, &search);
- if (!ent || !ent->running) {
- log_info(LD_GENERAL, "Child process %u has exited; no callback was "
- "registered", (unsigned)pid);
- return;
- }
-
- log_info(LD_GENERAL, "Child process %u has exited; running callback.",
- (unsigned)pid);
-
- ent->running = 0;
- ent->userfn(status, ent->userdata);
-}
-
-/** Use waitpid() to wait for all children that have exited, and invoke any
- * callbacks registered for them. */
-void
-notify_pending_waitpid_callbacks(void)
-{
- /* I was going to call this function reap_zombie_children(), but
- * that makes it sound way more exciting than it really is. */
- pid_t child;
- int status = 0;
-
- while ((child = waitpid(-1, &status, WNOHANG)) > 0) {
- notify_waitpid_callback_by_pid(child, status);
- status = 0; /* should be needless */
- }
-}
-
-#endif /* !defined(_WIN32) */
-
diff --git a/src/common/util_process.h b/src/common/util_process.h
deleted file mode 100644
index f637881940..0000000000
--- a/src/common/util_process.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/* Copyright (c) 2011-2018, The Tor Project, Inc. */
-/* See LICENSE for licensing information */
-
-/**
- * \file util_process.h
- * \brief Headers for util_process.c
- **/
-
-#ifndef TOR_UTIL_PROCESS_H
-#define TOR_UTIL_PROCESS_H
-
-#ifndef _WIN32
-/** A callback structure waiting for us to get a SIGCHLD informing us that a
- * PID has been closed. Created by set_waitpid_callback. Cancelled or cleaned-
- * up from clear_waitpid_callback(). Do not access outside of the main thread;
- * do not access from inside a signal handler. */
-typedef struct waitpid_callback_t waitpid_callback_t;
-
-waitpid_callback_t *set_waitpid_callback(pid_t pid,
- void (*fn)(int, void *), void *arg);
-void clear_waitpid_callback(waitpid_callback_t *ent);
-void notify_pending_waitpid_callbacks(void);
-#endif /* !defined(_WIN32) */
-
-#endif /* !defined(TOR_UTIL_PROCESS_H) */
-