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
#define FOLLY_INTRINSIC_CONSTEXPR const
#endif
#define FOLLY_INTRINSIC_CONSTEXPR const
#endif
-#ifndef FOLLY_NO_CONFIG
-#include <folly/folly-config.h>
-#endif
+#include <folly/Portability.h>
#include <folly/detail/BitsDetail.h>
#include <folly/detail/BitIteratorDetail.h>
#include <folly/detail/BitsDetail.h>
#include <folly/detail/BitIteratorDetail.h>
#include "folly-config.h"
#endif
#include "folly-config.h"
#endif
+#ifdef FOLLY_PLATFORM_CONFIG
+#include FOLLY_PLATFORM_CONFIG
+#endif
+
#if FOLLY_HAVE_FEATURES_H
#include <features.h>
#endif
#if FOLLY_HAVE_FEATURES_H
#include <features.h>
#endif
* limitations under the License.
*/
* limitations under the License.
*/
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
#include <folly/Subprocess.h>
#if __linux__
#include <folly/Subprocess.h>
#if __linux__
#include <fcntl.h>
#include <poll.h>
#include <fcntl.h>
#include <poll.h>
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
#include <unistd.h>
#include <array>
#include <unistd.h>
#include <array>
// Create a pipe to use to receive error information from the child,
// in case it fails before calling exec()
int errFds[2];
// Create a pipe to use to receive error information from the child,
// in case it fails before calling exec()
int errFds[2];
+#if FOLLY_HAVE_PIPE2
+ int r = ::pipe2(errFds, O_CLOEXEC);
+#else
checkUnixError(r, "pipe");
SCOPE_EXIT {
CHECK_ERR(::close(errFds[0]));
checkUnixError(r, "pipe");
SCOPE_EXIT {
CHECK_ERR(::close(errFds[0]));
};
// Ask the child to close the read end of the error pipe.
options.fdActions_[errFds[0]] = CLOSE;
};
// Ask the child to close the read end of the error pipe.
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.
// 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
// pipe.
r = fcntl(errFds[1], F_SETFD, FD_CLOEXEC);
checkUnixError(r, "set FD_CLOEXEC");
// 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()
// succeeds. If the exec fails the child can write error information to the
// pipe.
r = fcntl(errFds[1], F_SETFD, FD_CLOEXEC);
checkUnixError(r, "set FD_CLOEXEC");
// Perform the actual work of setting up pipes then forking and
// executing the child.
// Perform the actual work of setting up pipes then forking and
// executing the child.
for (auto& p : options.fdActions_) {
if (p.second == PIPE_IN || p.second == PIPE_OUT) {
int fds[2];
for (auto& p : options.fdActions_) {
if (p.second == PIPE_IN || p.second == PIPE_OUT) {
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);
checkUnixError(r, "pipe");
r = ::pipe(fds);
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;
pinfo.direction = p.second;
int cfd;
PipeInfo pinfo;
pinfo.direction = p.second;
int cfd;
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();
if (pid == 0) {
pid_t pid = vfork();
if (pid == 0) {
- int errnoValue = prepareChild(options, &oldSignals);
+ int errnoValue = prepareChild(options, &oldSignals, childDir);
if (errnoValue != 0) {
childError(errFd, kChildFailure, errnoValue);
}
if (errnoValue != 0) {
childError(errFd, kChildFailure, errnoValue);
}
}
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
// dispositions to default.
for (int sig = 1; sig < NSIG; ++sig) {
::signal(sig, SIG_DFL);
}
// While all signals are blocked, we must reset their
// dispositions to default.
for (int sig = 1; sig < NSIG; ++sig) {
::signal(sig, SIG_DFL);
}
- // Unblock signals; restore signal mask.
- int r = pthread_sigmask(SIG_SETMASK, sigmask, nullptr);
- if (r != 0) {
- return r; // pthread_sigmask() returns an errno value
+
+ {
+ // Unblock signals; restore signal mask.
+ 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()) {
- r = ::chdir(options.childDir_.c_str());
- if (r == -1) {
+ if (childDir) {
+ if (::chdir(childDir) == -1) {
return errno;
}
}
// Close parent's ends of all pipes
for (auto& p : pipes_) {
return errno;
}
}
// Close parent's ends of all pipes
for (auto& p : pipes_) {
- r = ::close(p.parentFd);
- if (r == -1) {
+ if (::close(p.parentFd) == -1) {
return errno;
}
}
// Close all fds that we're supposed to close.
return errno;
}
}
// 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_) {
if (p.second == CLOSE) {
for (auto& p : options.fdActions_) {
if (p.second == CLOSE) {
- ::close(p.first);
- } else {
- r = ::dup2(p.second, p.first);
- if (r == -1) {
+ if (::close(p.first) == -1) {
+ return errno;
+ }
+ } else if (p.second != p.first) {
+ if (::dup2(p.second, p.first) == -1) {
#if __linux__
// Opt to receive signal on parent death, if requested
if (options.parentDeathSignal_ != 0) {
#if __linux__
// Opt to receive signal on parent death, if requested
if (options.parentDeathSignal_ != 0) {
- r = prctl(PR_SET_PDEATHSIG, options.parentDeathSignal_, 0, 0, 0);
- if (r == -1) {
+ if (prctl(PR_SET_PDEATHSIG, options.parentDeathSignal_, 0, 0, 0) == -1) {
// Actions to run in child.
// Note that this runs after vfork(), so tread lightly.
// Returns 0 on success, or an errno value on failure.
// Actions to run in child.
// Note that this runs after vfork(), so tread lightly.
// 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,
const Options& options) const;
int runChild(const char* executable, char** argv, char** env,
const Options& options) const;
rallocm \
malloc_size \
malloc_usable_size \
rallocm \
malloc_size \
malloc_usable_size \
if test "$ac_cv_func_pthread_yield" = "no"; then
AC_CHECK_HEADERS([sched.h])
if test "$ac_cv_func_pthread_yield" = "no"; then
AC_CHECK_HEADERS([sched.h])
#include <ctime>
#include <cstdint>
#include <ctime>
#include <cstdint>
-#ifndef FOLLY_NO_CONFIG
-#include <folly/folly-config.h>
-#endif
+#include <folly/Portability.h>
#if FOLLY_HAVE_CLOCK_GETTIME
#error This should only be used as a workaround for platforms \
#if FOLLY_HAVE_CLOCK_GETTIME
#error This should only be used as a workaround for platforms \
-#ifndef FOLLY_NO_CONFIG
-#include <folly/folly-config.h>
-#endif
+#include <folly/Portability.h>