diff --git a/include/llvm/Support/Process.h b/include/llvm/Support/Process.h index 30973de3aac..bb7467bfc83 100644 --- a/include/llvm/Support/Process.h +++ b/include/llvm/Support/Process.h @@ -186,6 +186,12 @@ public: ArrayRef ArgsFromMain, SpecificBumpPtrAllocator &ArgAllocator); + // This functions ensures that the standard file descriptors (input, output, + // and error) are properly mapped to a file descriptor before we use any of + // them. This should only be called by standalone programs, library + // components should not call this. + static std::error_code FixupStandardFileDescriptors(); + /// This function determines if the standard input is connected directly /// to a user's input (keyboard probably), rather than coming from a file /// or pipe. diff --git a/lib/Support/Unix/Process.inc b/lib/Support/Unix/Process.inc index 4d272fd6b4b..01c78cbc0da 100644 --- a/lib/Support/Unix/Process.inc +++ b/lib/Support/Unix/Process.inc @@ -18,6 +18,9 @@ #include "llvm/Support/Mutex.h" #include "llvm/Support/MutexGuard.h" #include "llvm/Support/TimeValue.h" +#if HAVE_FCNTL_H +#include +#endif #ifdef HAVE_SYS_TIME_H #include #endif @@ -199,6 +202,62 @@ Process::GetArgumentVector(SmallVectorImpl &ArgsOut, return std::error_code(); } +namespace { +class FDCloser { +public: + FDCloser(int &FD) : FD(FD), KeepOpen(false) {} + void keepOpen() { KeepOpen = true; } + ~FDCloser() { + if (!KeepOpen && FD >= 0) + ::close(FD); + } + +private: + FDCloser(const FDCloser &) LLVM_DELETED_FUNCTION; + void operator=(const FDCloser &) LLVM_DELETED_FUNCTION; + + int &FD; + bool KeepOpen; +}; +} + +std::error_code Process::FixupStandardFileDescriptors() { + int NullFD = -1; + FDCloser FDC(NullFD); + const int StandardFDs[] = {STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO}; + for (int StandardFD : StandardFDs) { + struct stat st; + errno = 0; + while (fstat(StandardFD, &st) < 0) { + assert(errno && "expected errno to be set if fstat failed!"); + // fstat should return EBADF if the file descriptor is closed. + if (errno == EBADF) + break; + // retry fstat if we got EINTR, otherwise bubble up the failure. + if (errno != EINTR) + return std::error_code(errno, std::generic_category()); + } + // if fstat succeeds, move on to the next FD. + if (!errno) + continue; + assert(errno == EBADF && "expected errno to have EBADF at this point!"); + + if (NullFD < 0) { + while ((NullFD = open("/dev/null", O_RDWR)) < 0) { + if (errno == EINTR) + continue; + return std::error_code(errno, std::generic_category()); + } + } + + if (NullFD == StandardFD) + FDC.keepOpen(); + else if (dup2(NullFD, StandardFD) < 0) + return std::error_code(errno, std::generic_category()); + } + return std::error_code(); +} + bool Process::StandardInIsUserInput() { return FileDescriptorIsDisplayed(STDIN_FILENO); } diff --git a/lib/Support/Windows/Process.inc b/lib/Support/Windows/Process.inc index 61749a72727..db87d8ed60e 100644 --- a/lib/Support/Windows/Process.inc +++ b/lib/Support/Windows/Process.inc @@ -273,6 +273,10 @@ Process::GetArgumentVector(SmallVectorImpl &Args, return ec; } +std::error_code Process::FixupStandardFileDescriptors() { + return std::error_code(); +} + bool Process::StandardInIsUserInput() { return FileDescriptorIsDisplayed(0); }