123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575 |
- //===- llvm/Support/Unix/Program.cpp -----------------------------*- C++ -*-===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- //
- // This file implements the Unix specific portion of the Program class.
- //
- //===----------------------------------------------------------------------===//
- //===----------------------------------------------------------------------===//
- //=== WARNING: Implementation here must contain only generic UNIX code that
- //=== is guaranteed to work on *all* UNIX variants.
- //===----------------------------------------------------------------------===//
- #include "llvm/Support/Program.h"
- #include "Unix.h"
- #include "llvm/ADT/StringExtras.h"
- #include "llvm/Config/config.h"
- #include "llvm/Support/Compiler.h"
- #include "llvm/Support/Errc.h"
- #include "llvm/Support/FileSystem.h"
- #include "llvm/Support/Path.h"
- #include "llvm/Support/StringSaver.h"
- #include "llvm/Support/raw_ostream.h"
- #if HAVE_SYS_STAT_H
- #include <sys/stat.h>
- #endif
- #if HAVE_SYS_RESOURCE_H
- #include <sys/resource.h>
- #endif
- #if HAVE_SIGNAL_H
- #include <signal.h>
- #endif
- #if HAVE_FCNTL_H
- #include <fcntl.h>
- #endif
- #if HAVE_UNISTD_H
- #include <unistd.h>
- #endif
- #ifdef HAVE_POSIX_SPAWN
- #include <spawn.h>
- #if defined(__APPLE__)
- #include <TargetConditionals.h>
- #endif
- #if defined(__APPLE__) && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE)
- #define USE_NSGETENVIRON 1
- #else
- #define USE_NSGETENVIRON 0
- #endif
- #if !USE_NSGETENVIRON
- extern char **environ;
- #else
- #include <crt_externs.h> // _NSGetEnviron
- #endif
- #endif
- using namespace llvm;
- using namespace sys;
- ProcessInfo::ProcessInfo() : Pid(0), ReturnCode(0) {}
- ErrorOr<std::string> sys::findProgramByName(StringRef Name,
- ArrayRef<StringRef> Paths) {
- assert(!Name.empty() && "Must have a name!");
- // Use the given path verbatim if it contains any slashes; this matches
- // the behavior of sh(1) and friends.
- if (Name.contains('/'))
- return std::string(Name);
- SmallVector<StringRef, 16> EnvironmentPaths;
- if (Paths.empty())
- if (const char *PathEnv = std::getenv("PATH")) {
- SplitString(PathEnv, EnvironmentPaths, ":");
- Paths = EnvironmentPaths;
- }
- for (auto Path : Paths) {
- if (Path.empty())
- continue;
- // Check to see if this first directory contains the executable...
- SmallString<128> FilePath(Path);
- sys::path::append(FilePath, Name);
- if (sys::fs::can_execute(FilePath.c_str()))
- return std::string(FilePath.str()); // Found the executable!
- }
- return errc::no_such_file_or_directory;
- }
- static bool RedirectIO(Optional<StringRef> Path, int FD, std::string* ErrMsg) {
- if (!Path) // Noop
- return false;
- std::string File;
- if (Path->empty())
- // Redirect empty paths to /dev/null
- File = "/dev/null";
- else
- File = std::string(*Path);
- // Open the file
- int InFD = open(File.c_str(), FD == 0 ? O_RDONLY : O_WRONLY|O_CREAT, 0666);
- if (InFD == -1) {
- MakeErrMsg(ErrMsg, "Cannot open file '" + File + "' for "
- + (FD == 0 ? "input" : "output"));
- return true;
- }
- // Install it as the requested FD
- if (dup2(InFD, FD) == -1) {
- MakeErrMsg(ErrMsg, "Cannot dup2");
- close(InFD);
- return true;
- }
- close(InFD); // Close the original FD
- return false;
- }
- #ifdef HAVE_POSIX_SPAWN
- static bool RedirectIO_PS(const std::string *Path, int FD, std::string *ErrMsg,
- posix_spawn_file_actions_t *FileActions) {
- if (!Path) // Noop
- return false;
- const char *File;
- if (Path->empty())
- // Redirect empty paths to /dev/null
- File = "/dev/null";
- else
- File = Path->c_str();
- if (int Err = posix_spawn_file_actions_addopen(
- FileActions, FD, File,
- FD == 0 ? O_RDONLY : O_WRONLY | O_CREAT, 0666))
- return MakeErrMsg(ErrMsg, "Cannot posix_spawn_file_actions_addopen", Err);
- return false;
- }
- #endif
- static void TimeOutHandler(int Sig) {
- }
- static void SetMemoryLimits(unsigned size) {
- #if HAVE_SYS_RESOURCE_H && HAVE_GETRLIMIT && HAVE_SETRLIMIT
- struct rlimit r;
- __typeof__ (r.rlim_cur) limit = (__typeof__ (r.rlim_cur)) (size) * 1048576;
- // Heap size
- getrlimit (RLIMIT_DATA, &r);
- r.rlim_cur = limit;
- setrlimit (RLIMIT_DATA, &r);
- #ifdef RLIMIT_RSS
- // Resident set size.
- getrlimit (RLIMIT_RSS, &r);
- r.rlim_cur = limit;
- setrlimit (RLIMIT_RSS, &r);
- #endif
- #endif
- }
- static std::vector<const char *>
- toNullTerminatedCStringArray(ArrayRef<StringRef> Strings, StringSaver &Saver) {
- std::vector<const char *> Result;
- for (StringRef S : Strings)
- Result.push_back(Saver.save(S).data());
- Result.push_back(nullptr);
- return Result;
- }
- static bool Execute(ProcessInfo &PI, StringRef Program,
- ArrayRef<StringRef> Args, Optional<ArrayRef<StringRef>> Env,
- ArrayRef<Optional<StringRef>> Redirects,
- unsigned MemoryLimit, std::string *ErrMsg,
- BitVector *AffinityMask) {
- if (!llvm::sys::fs::exists(Program)) {
- if (ErrMsg)
- *ErrMsg = std::string("Executable \"") + Program.str() +
- std::string("\" doesn't exist!");
- return false;
- }
- assert(!AffinityMask && "Starting a process with an affinity mask is "
- "currently not supported on Unix!");
- BumpPtrAllocator Allocator;
- StringSaver Saver(Allocator);
- std::vector<const char *> ArgVector, EnvVector;
- const char **Argv = nullptr;
- const char **Envp = nullptr;
- ArgVector = toNullTerminatedCStringArray(Args, Saver);
- Argv = ArgVector.data();
- if (Env) {
- EnvVector = toNullTerminatedCStringArray(*Env, Saver);
- Envp = EnvVector.data();
- }
- // If this OS has posix_spawn and there is no memory limit being implied, use
- // posix_spawn. It is more efficient than fork/exec.
- #ifdef HAVE_POSIX_SPAWN
- if (MemoryLimit == 0) {
- posix_spawn_file_actions_t FileActionsStore;
- posix_spawn_file_actions_t *FileActions = nullptr;
- // If we call posix_spawn_file_actions_addopen we have to make sure the
- // c strings we pass to it stay alive until the call to posix_spawn,
- // so we copy any StringRefs into this variable.
- std::string RedirectsStorage[3];
- if (!Redirects.empty()) {
- assert(Redirects.size() == 3);
- std::string *RedirectsStr[3] = {nullptr, nullptr, nullptr};
- for (int I = 0; I < 3; ++I) {
- if (Redirects[I]) {
- RedirectsStorage[I] = std::string(*Redirects[I]);
- RedirectsStr[I] = &RedirectsStorage[I];
- }
- }
- FileActions = &FileActionsStore;
- posix_spawn_file_actions_init(FileActions);
- // Redirect stdin/stdout.
- if (RedirectIO_PS(RedirectsStr[0], 0, ErrMsg, FileActions) ||
- RedirectIO_PS(RedirectsStr[1], 1, ErrMsg, FileActions))
- return false;
- if (!Redirects[1] || !Redirects[2] || *Redirects[1] != *Redirects[2]) {
- // Just redirect stderr
- if (RedirectIO_PS(RedirectsStr[2], 2, ErrMsg, FileActions))
- return false;
- } else {
- // If stdout and stderr should go to the same place, redirect stderr
- // to the FD already open for stdout.
- if (int Err = posix_spawn_file_actions_adddup2(FileActions, 1, 2))
- return !MakeErrMsg(ErrMsg, "Can't redirect stderr to stdout", Err);
- }
- }
- if (!Envp)
- #if !USE_NSGETENVIRON
- Envp = const_cast<const char **>(environ);
- #else
- // environ is missing in dylibs.
- Envp = const_cast<const char **>(*_NSGetEnviron());
- #endif
- constexpr int maxRetries = 8;
- int retries = 0;
- pid_t PID;
- int Err;
- do {
- PID = 0; // Make Valgrind happy.
- Err = posix_spawn(&PID, Program.str().c_str(), FileActions,
- /*attrp*/ nullptr, const_cast<char **>(Argv),
- const_cast<char **>(Envp));
- } while (Err == EINTR && ++retries < maxRetries);
- if (FileActions)
- posix_spawn_file_actions_destroy(FileActions);
- if (Err)
- return !MakeErrMsg(ErrMsg, "posix_spawn failed", Err);
- PI.Pid = PID;
- PI.Process = PID;
- return true;
- }
- #endif
- // Create a child process.
- int child = fork();
- switch (child) {
- // An error occurred: Return to the caller.
- case -1:
- MakeErrMsg(ErrMsg, "Couldn't fork");
- return false;
- // Child process: Execute the program.
- case 0: {
- // Redirect file descriptors...
- if (!Redirects.empty()) {
- // Redirect stdin
- if (RedirectIO(Redirects[0], 0, ErrMsg)) { return false; }
- // Redirect stdout
- if (RedirectIO(Redirects[1], 1, ErrMsg)) { return false; }
- if (Redirects[1] && Redirects[2] && *Redirects[1] == *Redirects[2]) {
- // If stdout and stderr should go to the same place, redirect stderr
- // to the FD already open for stdout.
- if (-1 == dup2(1,2)) {
- MakeErrMsg(ErrMsg, "Can't redirect stderr to stdout");
- return false;
- }
- } else {
- // Just redirect stderr
- if (RedirectIO(Redirects[2], 2, ErrMsg)) { return false; }
- }
- }
- // Set memory limits
- if (MemoryLimit!=0) {
- SetMemoryLimits(MemoryLimit);
- }
- // Execute!
- std::string PathStr = std::string(Program);
- if (Envp != nullptr)
- execve(PathStr.c_str(), const_cast<char **>(Argv),
- const_cast<char **>(Envp));
- else
- execv(PathStr.c_str(), const_cast<char **>(Argv));
- // If the execve() failed, we should exit. Follow Unix protocol and
- // return 127 if the executable was not found, and 126 otherwise.
- // Use _exit rather than exit so that atexit functions and static
- // object destructors cloned from the parent process aren't
- // redundantly run, and so that any data buffered in stdio buffers
- // cloned from the parent aren't redundantly written out.
- _exit(errno == ENOENT ? 127 : 126);
- }
- // Parent process: Break out of the switch to do our processing.
- default:
- break;
- }
- PI.Pid = child;
- PI.Process = child;
- return true;
- }
- namespace llvm {
- namespace sys {
- #ifndef _AIX
- using ::wait4;
- #else
- static pid_t (wait4)(pid_t pid, int *status, int options, struct rusage *usage);
- #endif
- } // namespace sys
- } // namespace llvm
- #ifdef _AIX
- #ifndef _ALL_SOURCE
- extern "C" pid_t (wait4)(pid_t pid, int *status, int options,
- struct rusage *usage);
- #endif
- pid_t (llvm::sys::wait4)(pid_t pid, int *status, int options,
- struct rusage *usage) {
- assert(pid > 0 && "Only expecting to handle actual PID values!");
- assert((options & ~WNOHANG) == 0 && "Expecting WNOHANG at most!");
- assert(usage && "Expecting usage collection!");
- // AIX wait4 does not work well with WNOHANG.
- if (!(options & WNOHANG))
- return ::wait4(pid, status, options, usage);
- // For WNOHANG, we use waitid (which supports WNOWAIT) until the child process
- // has terminated.
- siginfo_t WaitIdInfo;
- WaitIdInfo.si_pid = 0;
- int WaitIdRetVal =
- waitid(P_PID, pid, &WaitIdInfo, WNOWAIT | WEXITED | options);
- if (WaitIdRetVal == -1 || WaitIdInfo.si_pid == 0)
- return WaitIdRetVal;
- assert(WaitIdInfo.si_pid == pid);
- // The child has already terminated, so a blocking wait on it is okay in the
- // absence of indiscriminate `wait` calls from the current process (which
- // would cause the call here to fail with ECHILD).
- return ::wait4(pid, status, options & ~WNOHANG, usage);
- }
- #endif
- ProcessInfo llvm::sys::Wait(const ProcessInfo &PI, unsigned SecondsToWait,
- bool WaitUntilTerminates, std::string *ErrMsg,
- Optional<ProcessStatistics> *ProcStat) {
- struct sigaction Act, Old;
- assert(PI.Pid && "invalid pid to wait on, process not started?");
- int WaitPidOptions = 0;
- pid_t ChildPid = PI.Pid;
- if (WaitUntilTerminates) {
- SecondsToWait = 0;
- } else if (SecondsToWait) {
- // Install a timeout handler. The handler itself does nothing, but the
- // simple fact of having a handler at all causes the wait below to return
- // with EINTR, unlike if we used SIG_IGN.
- memset(&Act, 0, sizeof(Act));
- Act.sa_handler = TimeOutHandler;
- sigemptyset(&Act.sa_mask);
- sigaction(SIGALRM, &Act, &Old);
- // FIXME The alarm signal may be delivered to another thread.
- alarm(SecondsToWait);
- } else if (SecondsToWait == 0)
- WaitPidOptions = WNOHANG;
- // Parent process: Wait for the child process to terminate.
- int status;
- ProcessInfo WaitResult;
- rusage Info;
- if (ProcStat)
- ProcStat->reset();
- do {
- WaitResult.Pid = sys::wait4(ChildPid, &status, WaitPidOptions, &Info);
- } while (WaitUntilTerminates && WaitResult.Pid == -1 && errno == EINTR);
- if (WaitResult.Pid != PI.Pid) {
- if (WaitResult.Pid == 0) {
- // Non-blocking wait.
- return WaitResult;
- } else {
- if (SecondsToWait && errno == EINTR) {
- // Kill the child.
- kill(PI.Pid, SIGKILL);
- // Turn off the alarm and restore the signal handler
- alarm(0);
- sigaction(SIGALRM, &Old, nullptr);
- // Wait for child to die
- // FIXME This could grab some other child process out from another
- // waiting thread and then leave a zombie anyway.
- if (wait(&status) != ChildPid)
- MakeErrMsg(ErrMsg, "Child timed out but wouldn't die");
- else
- MakeErrMsg(ErrMsg, "Child timed out", 0);
- WaitResult.ReturnCode = -2; // Timeout detected
- return WaitResult;
- } else if (errno != EINTR) {
- MakeErrMsg(ErrMsg, "Error waiting for child process");
- WaitResult.ReturnCode = -1;
- return WaitResult;
- }
- }
- }
- // We exited normally without timeout, so turn off the timer.
- if (SecondsToWait && !WaitUntilTerminates) {
- alarm(0);
- sigaction(SIGALRM, &Old, nullptr);
- }
- if (ProcStat) {
- std::chrono::microseconds UserT = toDuration(Info.ru_utime);
- std::chrono::microseconds KernelT = toDuration(Info.ru_stime);
- uint64_t PeakMemory = 0;
- #ifndef __HAIKU__
- PeakMemory = static_cast<uint64_t>(Info.ru_maxrss);
- #endif
- *ProcStat = ProcessStatistics{UserT + KernelT, UserT, PeakMemory};
- }
- // Return the proper exit status. Detect error conditions
- // so we can return -1 for them and set ErrMsg informatively.
- int result = 0;
- if (WIFEXITED(status)) {
- result = WEXITSTATUS(status);
- WaitResult.ReturnCode = result;
- if (result == 127) {
- if (ErrMsg)
- *ErrMsg = llvm::sys::StrError(ENOENT);
- WaitResult.ReturnCode = -1;
- return WaitResult;
- }
- if (result == 126) {
- if (ErrMsg)
- *ErrMsg = "Program could not be executed";
- WaitResult.ReturnCode = -1;
- return WaitResult;
- }
- } else if (WIFSIGNALED(status)) {
- if (ErrMsg) {
- *ErrMsg = strsignal(WTERMSIG(status));
- #ifdef WCOREDUMP
- if (WCOREDUMP(status))
- *ErrMsg += " (core dumped)";
- #endif
- }
- // Return a special value to indicate that the process received an unhandled
- // signal during execution as opposed to failing to execute.
- WaitResult.ReturnCode = -2;
- }
- return WaitResult;
- }
- std::error_code llvm::sys::ChangeStdinMode(fs::OpenFlags Flags){
- if (!(Flags & fs::OF_Text))
- return ChangeStdinToBinary();
- return std::error_code();
- }
- std::error_code llvm::sys::ChangeStdoutMode(fs::OpenFlags Flags){
- if (!(Flags & fs::OF_Text))
- return ChangeStdoutToBinary();
- return std::error_code();
- }
- std::error_code llvm::sys::ChangeStdinToBinary() {
- // Do nothing, as Unix doesn't differentiate between text and binary.
- return std::error_code();
- }
- std::error_code llvm::sys::ChangeStdoutToBinary() {
- // Do nothing, as Unix doesn't differentiate between text and binary.
- return std::error_code();
- }
- std::error_code
- llvm::sys::writeFileWithEncoding(StringRef FileName, StringRef Contents,
- WindowsEncodingMethod Encoding /*unused*/) {
- std::error_code EC;
- llvm::raw_fd_ostream OS(FileName, EC, llvm::sys::fs::OpenFlags::OF_TextWithCRLF);
- if (EC)
- return EC;
- OS << Contents;
- if (OS.has_error())
- return make_error_code(errc::io_error);
- return EC;
- }
- bool llvm::sys::commandLineFitsWithinSystemLimits(StringRef Program,
- ArrayRef<StringRef> Args) {
- static long ArgMax = sysconf(_SC_ARG_MAX);
- // POSIX requires that _POSIX_ARG_MAX is 4096, which is the lowest possible
- // value for ARG_MAX on a POSIX compliant system.
- static long ArgMin = _POSIX_ARG_MAX;
- // This the same baseline used by xargs.
- long EffectiveArgMax = 128 * 1024;
- if (EffectiveArgMax > ArgMax)
- EffectiveArgMax = ArgMax;
- else if (EffectiveArgMax < ArgMin)
- EffectiveArgMax = ArgMin;
- // System says no practical limit.
- if (ArgMax == -1)
- return true;
- // Conservatively account for space required by environment variables.
- long HalfArgMax = EffectiveArgMax / 2;
- size_t ArgLength = Program.size() + 1;
- for (StringRef Arg : Args) {
- // Ensure that we do not exceed the MAX_ARG_STRLEN constant on Linux, which
- // does not have a constant unlike what the man pages would have you
- // believe. Since this limit is pretty high, perform the check
- // unconditionally rather than trying to be aggressive and limiting it to
- // Linux only.
- if (Arg.size() >= (32 * 4096))
- return false;
- ArgLength += Arg.size() + 1;
- if (ArgLength > size_t(HalfArgMax)) {
- return false;
- }
- }
- return true;
- }
|