mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-12-06 19:41:15 +01:00
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.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
+1
-1538
File diff suppressed because it is too large
Load Diff
@@ -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) */
|
||||
|
||||
@@ -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) */
|
||||
|
||||
@@ -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) */
|
||||
|
||||
Reference in New Issue
Block a user