Commit 217e88e6 authored by Tudor Bosman's avatar Tudor Bosman

Use pipe2 in Subprocess; platform-specific config

Summary:
We've wanted to use pipe2 in Subprocess for a while, but that's not supported
on glibc 2.5.1. This is not a problem in OSS (autoconf can solve this),
but, internally, we don't run autoconf; add an internal platform include file
with such per-platform differences.

Test Plan: fbconfig -r folly && fbmake runtests_opt

Reviewed By: meyering@fb.com

Subscribers: jhj, lesha, kma, agallagher

FB internal diff: D1422128
parent 2015ae71
...@@ -68,9 +68,7 @@ ...@@ -68,9 +68,7 @@
#define FOLLY_INTRINSIC_CONSTEXPR const #define FOLLY_INTRINSIC_CONSTEXPR const
#endif #endif
#ifndef FOLLY_NO_CONFIG #include <folly/Portability.h>
#include <folly/folly-config.h>
#endif
#include <folly/detail/BitsDetail.h> #include <folly/detail/BitsDetail.h>
#include <folly/detail/BitIteratorDetail.h> #include <folly/detail/BitIteratorDetail.h>
......
...@@ -21,6 +21,10 @@ ...@@ -21,6 +21,10 @@
#include "folly-config.h" #include "folly-config.h"
#endif #endif
#ifdef FOLLY_PLATFORM_CONFIG
#include FOLLY_PLATFORM_CONFIG
#endif
#if FOLLY_HAVE_FEATURES_H #if FOLLY_HAVE_FEATURES_H
#include <features.h> #include <features.h>
#endif #endif
......
...@@ -14,6 +14,10 @@ ...@@ -14,6 +14,10 @@
* limitations under the License. * limitations under the License.
*/ */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <folly/Subprocess.h> #include <folly/Subprocess.h>
#if __linux__ #if __linux__
...@@ -22,9 +26,6 @@ ...@@ -22,9 +26,6 @@
#include <fcntl.h> #include <fcntl.h>
#include <poll.h> #include <poll.h>
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <unistd.h> #include <unistd.h>
#include <array> #include <array>
...@@ -254,7 +255,11 @@ void Subprocess::spawn( ...@@ -254,7 +255,11 @@ void Subprocess::spawn(
// Create a pipe to use to receive error information from the child, // Create a pipe to use to receive error information from the child,
// in case it fails before calling exec() // in case it fails before calling exec()
int errFds[2]; int errFds[2];
#if FOLLY_HAVE_PIPE2
int r = ::pipe2(errFds, O_CLOEXEC);
#else
int r = ::pipe(errFds); int r = ::pipe(errFds);
#endif
checkUnixError(r, "pipe"); checkUnixError(r, "pipe");
SCOPE_EXIT { SCOPE_EXIT {
CHECK_ERR(::close(errFds[0])); CHECK_ERR(::close(errFds[0]));
...@@ -264,12 +269,17 @@ void Subprocess::spawn( ...@@ -264,12 +269,17 @@ void Subprocess::spawn(
}; };
// Ask the child to close the read end of the error pipe. // Ask the child to close the read end of the error pipe.
options.fdActions_[errFds[0]] = CLOSE; options.fdActions_[errFds[0]] = CLOSE;
#if !FOLLY_HAVE_PIPE2
r = fcntl(errFds[0], F_SETFD, FD_CLOEXEC);
checkUnixError(r, "set FD_CLOEXEC");
// Set the close-on-exec flag on the write side of the pipe. // Set the close-on-exec flag on the write side of the pipe.
// This way the pipe will be closed automatically in the child if execve() // This way the pipe will be closed automatically in the child if execve()
// succeeds. If the exec fails the child can write error information to the // succeeds. If the exec fails the child can write error information to the
// pipe. // pipe.
r = fcntl(errFds[1], F_SETFD, FD_CLOEXEC); r = fcntl(errFds[1], F_SETFD, FD_CLOEXEC);
checkUnixError(r, "set FD_CLOEXEC"); checkUnixError(r, "set FD_CLOEXEC");
#endif
// Perform the actual work of setting up pipes then forking and // Perform the actual work of setting up pipes then forking and
// executing the child. // executing the child.
...@@ -316,8 +326,20 @@ void Subprocess::spawnInternal( ...@@ -316,8 +326,20 @@ void Subprocess::spawnInternal(
for (auto& p : options.fdActions_) { for (auto& p : options.fdActions_) {
if (p.second == PIPE_IN || p.second == PIPE_OUT) { if (p.second == PIPE_IN || p.second == PIPE_OUT) {
int fds[2]; int fds[2];
// We're setting both ends of the pipe as close-on-exec. The child
// doesn't need to reset the flag on its end, as we always dup2() the fd,
// and dup2() fds don't share the close-on-exec flag.
#if FOLLY_HAVE_PIPE2
r = ::pipe2(fds, O_CLOEXEC);
checkUnixError(r, "pipe2");
#else
r = ::pipe(fds); r = ::pipe(fds);
checkUnixError(r, "pipe"); checkUnixError(r, "pipe");
r = fcntl(fds[0], F_SETFD, FD_CLOEXEC);
checkUnixError(r, "set FD_CLOEXEC");
r = fcntl(fds[1], F_SETFD, FD_CLOEXEC);
checkUnixError(r, "set FD_CLOEXEC");
#endif
PipeInfo pinfo; PipeInfo pinfo;
pinfo.direction = p.second; pinfo.direction = p.second;
int cfd; int cfd;
...@@ -380,9 +402,12 @@ void Subprocess::spawnInternal( ...@@ -380,9 +402,12 @@ void Subprocess::spawnInternal(
CHECK_EQ(r, 0) << "pthread_sigmask: " << errnoStr(r); // shouldn't fail CHECK_EQ(r, 0) << "pthread_sigmask: " << errnoStr(r); // shouldn't fail
}; };
// Call c_str() here, as it's not necessarily safe after fork.
const char* childDir =
options.childDir_.empty() ? nullptr : options.childDir_.c_str();
pid_t pid = vfork(); pid_t pid = vfork();
if (pid == 0) { if (pid == 0) {
int errnoValue = prepareChild(options, &oldSignals); int errnoValue = prepareChild(options, &oldSignals, childDir);
if (errnoValue != 0) { if (errnoValue != 0) {
childError(errFd, kChildFailure, errnoValue); childError(errFd, kChildFailure, errnoValue);
} }
...@@ -406,43 +431,44 @@ void Subprocess::spawnInternal( ...@@ -406,43 +431,44 @@ void Subprocess::spawnInternal(
} }
int Subprocess::prepareChild(const Options& options, int Subprocess::prepareChild(const Options& options,
const sigset_t* sigmask) const { const sigset_t* sigmask,
const char* childDir) const {
// While all signals are blocked, we must reset their // While all signals are blocked, we must reset their
// dispositions to default. // dispositions to default.
for (int sig = 1; sig < NSIG; ++sig) { for (int sig = 1; sig < NSIG; ++sig) {
::signal(sig, SIG_DFL); ::signal(sig, SIG_DFL);
} }
// Unblock signals; restore signal mask.
int r = pthread_sigmask(SIG_SETMASK, sigmask, nullptr); {
if (r != 0) { // Unblock signals; restore signal mask.
return r; // pthread_sigmask() returns an errno value int r = pthread_sigmask(SIG_SETMASK, sigmask, nullptr);
if (r != 0) {
return r; // pthread_sigmask() returns an errno value
}
} }
// Change the working directory, if one is given // Change the working directory, if one is given
if (!options.childDir_.empty()) { if (childDir) {
r = ::chdir(options.childDir_.c_str()); if (::chdir(childDir) == -1) {
if (r == -1) {
return errno; return errno;
} }
} }
// Close parent's ends of all pipes // Close parent's ends of all pipes
for (auto& p : pipes_) { for (auto& p : pipes_) {
r = ::close(p.parentFd); if (::close(p.parentFd) == -1) {
if (r == -1) {
return errno; return errno;
} }
} }
// Close all fds that we're supposed to close. // Close all fds that we're supposed to close.
// Note that we're ignoring errors here, in case some of these
// fds were set to close on exec.
for (auto& p : options.fdActions_) { for (auto& p : options.fdActions_) {
if (p.second == CLOSE) { if (p.second == CLOSE) {
::close(p.first); if (::close(p.first) == -1) {
} else { return errno;
r = ::dup2(p.second, p.first); }
if (r == -1) { } else if (p.second != p.first) {
if (::dup2(p.second, p.first) == -1) {
return errno; return errno;
} }
} }
...@@ -462,8 +488,7 @@ int Subprocess::prepareChild(const Options& options, ...@@ -462,8 +488,7 @@ int Subprocess::prepareChild(const Options& options,
#if __linux__ #if __linux__
// Opt to receive signal on parent death, if requested // Opt to receive signal on parent death, if requested
if (options.parentDeathSignal_ != 0) { if (options.parentDeathSignal_ != 0) {
r = prctl(PR_SET_PDEATHSIG, options.parentDeathSignal_, 0, 0, 0); if (prctl(PR_SET_PDEATHSIG, options.parentDeathSignal_, 0, 0, 0) == -1) {
if (r == -1) {
return errno; return errno;
} }
} }
......
...@@ -500,7 +500,9 @@ class Subprocess : private boost::noncopyable { ...@@ -500,7 +500,9 @@ class Subprocess : private boost::noncopyable {
// Actions to run in child. // Actions to run in child.
// Note that this runs after vfork(), so tread lightly. // Note that this runs after vfork(), so tread lightly.
// Returns 0 on success, or an errno value on failure. // Returns 0 on success, or an errno value on failure.
int prepareChild(const Options& options, const sigset_t* sigmask) const; int prepareChild(const Options& options,
const sigset_t* sigmask,
const char* childDir) const;
int runChild(const char* executable, char** argv, char** env, int runChild(const char* executable, char** argv, char** env,
const Options& options) const; const Options& options) const;
......
...@@ -271,7 +271,8 @@ AC_CHECK_FUNCS([getdelim \ ...@@ -271,7 +271,8 @@ AC_CHECK_FUNCS([getdelim \
rallocm \ rallocm \
malloc_size \ malloc_size \
malloc_usable_size \ malloc_usable_size \
memrchr]) memrchr \
pipe2])
if test "$ac_cv_func_pthread_yield" = "no"; then if test "$ac_cv_func_pthread_yield" = "no"; then
AC_CHECK_HEADERS([sched.h]) AC_CHECK_HEADERS([sched.h])
......
...@@ -20,9 +20,7 @@ ...@@ -20,9 +20,7 @@
#include <ctime> #include <ctime>
#include <cstdint> #include <cstdint>
#ifndef FOLLY_NO_CONFIG #include <folly/Portability.h>
#include <folly/folly-config.h>
#endif
#if FOLLY_HAVE_CLOCK_GETTIME #if FOLLY_HAVE_CLOCK_GETTIME
#error This should only be used as a workaround for platforms \ #error This should only be used as a workaround for platforms \
......
...@@ -19,9 +19,7 @@ ...@@ -19,9 +19,7 @@
#include <stdlib.h> #include <stdlib.h>
#ifndef FOLLY_NO_CONFIG #include <folly/Portability.h>
#include <folly/folly-config.h>
#endif
extern "C" { extern "C" {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment