brintos

brintos / llvm-project-archived public Read only

0
0
Text · 17.6 KiB · 489ca03 Raw
588 lines · plain
1//===- llvm/Support/Unix/Program.inc ----------------------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file implements the Unix specific portion of the Program class.10//11//===----------------------------------------------------------------------===//12 13//===----------------------------------------------------------------------===//14//=== WARNING: Implementation here must contain only generic UNIX15//===          code that is guaranteed to work on *all* UNIX variants.16//===----------------------------------------------------------------------===//17 18#include "llvm/Support/Program.h"19 20#include "Unix.h"21#include "llvm/ADT/StringExtras.h"22#include "llvm/Config/config.h"23#include "llvm/Support/AutoConvert.h"24#include "llvm/Support/Compiler.h"25#include "llvm/Support/Errc.h"26#include "llvm/Support/FileSystem.h"27#include "llvm/Support/Path.h"28#include "llvm/Support/StringSaver.h"29#include "llvm/Support/raw_ostream.h"30#include <fcntl.h>31#include <signal.h>32#include <sys/resource.h>33#include <sys/stat.h>34#if HAVE_UNISTD_H35#include <unistd.h>36#endif37#ifdef HAVE_POSIX_SPAWN38#include <spawn.h>39 40#if defined(__APPLE__)41#include <TargetConditionals.h>42#endif43 44#if defined(__APPLE__) && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE)45#define USE_NSGETENVIRON 146#else47#define USE_NSGETENVIRON 048#endif49 50#if !USE_NSGETENVIRON51extern char **environ;52#else53#include <crt_externs.h> // _NSGetEnviron54#endif55#endif56 57using namespace llvm;58using namespace sys;59 60ProcessInfo::ProcessInfo() : Pid(0), ReturnCode(0) {}61 62ErrorOr<std::string> sys::findProgramByName(StringRef Name,63                                            ArrayRef<StringRef> Paths) {64  assert(!Name.empty() && "Must have a name!");65  // Use the given path verbatim if it contains any slashes; this matches66  // the behavior of sh(1) and friends.67  if (Name.contains('/'))68    return std::string(Name);69 70  SmallVector<StringRef, 16> EnvironmentPaths;71  if (Paths.empty())72    if (const char *PathEnv = std::getenv("PATH")) {73      SplitString(PathEnv, EnvironmentPaths, ":");74      Paths = EnvironmentPaths;75    }76 77  for (auto Path : Paths) {78    if (Path.empty())79      continue;80 81    // Check to see if this first directory contains the executable...82    SmallString<128> FilePath(Path);83    sys::path::append(FilePath, Name);84    if (sys::fs::can_execute(FilePath.c_str()))85      return std::string(FilePath); // Found the executable!86  }87  return errc::no_such_file_or_directory;88}89 90static bool RedirectIO(std::optional<StringRef> Path, int FD, std::string *ErrMsg) {91  if (!Path) // Noop92    return false;93  std::string File;94  if (Path->empty())95    // Redirect empty paths to /dev/null96    File = "/dev/null";97  else98    File = std::string(*Path);99 100  // Open the file101  int InFD = open(File.c_str(), FD == 0 ? O_RDONLY : O_WRONLY | O_CREAT, 0666);102  if (InFD == -1) {103    MakeErrMsg(ErrMsg, "Cannot open file '" + File + "' for " +104                           (FD == 0 ? "input" : "output"));105    return true;106  }107 108  // Install it as the requested FD109  if (dup2(InFD, FD) == -1) {110    MakeErrMsg(ErrMsg, "Cannot dup2");111    close(InFD);112    return true;113  }114  close(InFD); // Close the original FD115  return false;116}117 118#ifdef HAVE_POSIX_SPAWN119static bool RedirectIO_PS(const std::string *Path, int FD, std::string *ErrMsg,120                          posix_spawn_file_actions_t *FileActions) {121  if (!Path) // Noop122    return false;123  const char *File;124  if (Path->empty())125    // Redirect empty paths to /dev/null126    File = "/dev/null";127  else128    File = Path->c_str();129 130  if (int Err = posix_spawn_file_actions_addopen(131          FileActions, FD, File, FD == 0 ? O_RDONLY : O_WRONLY | O_CREAT, 0666))132    return MakeErrMsg(ErrMsg, "Cannot posix_spawn_file_actions_addopen", Err);133  return false;134}135#endif136 137static void TimeOutHandler(int Sig) {}138 139static void SetMemoryLimits(unsigned size) {140  struct rlimit r;141  __typeof__(r.rlim_cur) limit = (__typeof__(r.rlim_cur))(size)*1048576;142 143  // Heap size144  getrlimit(RLIMIT_DATA, &r);145  r.rlim_cur = limit;146  setrlimit(RLIMIT_DATA, &r);147#ifdef RLIMIT_RSS148  // Resident set size.149  getrlimit(RLIMIT_RSS, &r);150  r.rlim_cur = limit;151  setrlimit(RLIMIT_RSS, &r);152#endif153}154 155static std::vector<const char *>156toNullTerminatedCStringArray(ArrayRef<StringRef> Strings, StringSaver &Saver) {157  std::vector<const char *> Result;158  for (StringRef S : Strings)159    Result.push_back(Saver.save(S).data());160  Result.push_back(nullptr);161  return Result;162}163 164static bool Execute(ProcessInfo &PI, StringRef Program,165                    ArrayRef<StringRef> Args,166                    std::optional<ArrayRef<StringRef>> Env,167                    ArrayRef<std::optional<StringRef>> Redirects,168                    unsigned MemoryLimit, std::string *ErrMsg,169                    BitVector *AffinityMask, bool DetachProcess) {170  assert(!AffinityMask && "Starting a process with an affinity mask is "171                          "currently not supported on Unix!");172 173  BumpPtrAllocator Allocator;174  StringSaver Saver(Allocator);175  std::vector<const char *> ArgVector, EnvVector;176  const char **Argv = nullptr;177  const char **Envp = nullptr;178  ArgVector = toNullTerminatedCStringArray(Args, Saver);179  Argv = ArgVector.data();180  if (Env) {181    EnvVector = toNullTerminatedCStringArray(*Env, Saver);182    Envp = EnvVector.data();183  }184 185  // If this OS has posix_spawn and there is no memory limit being implied, use186  // posix_spawn.  It is more efficient than fork/exec.187#ifdef HAVE_POSIX_SPAWN188  // Cannot use posix_spawn if you would like to detach the process189  if (MemoryLimit == 0 && !DetachProcess) {190    posix_spawn_file_actions_t FileActionsStore;191    posix_spawn_file_actions_t *FileActions = nullptr;192 193    // If we call posix_spawn_file_actions_addopen we have to make sure the194    // c strings we pass to it stay alive until the call to posix_spawn,195    // so we copy any StringRefs into this variable.196    std::string RedirectsStorage[3];197 198    if (!Redirects.empty()) {199      assert(Redirects.size() == 3);200      std::string *RedirectsStr[3] = {nullptr, nullptr, nullptr};201      for (int I = 0; I < 3; ++I) {202        if (Redirects[I]) {203          RedirectsStorage[I] = std::string(*Redirects[I]);204          RedirectsStr[I] = &RedirectsStorage[I];205        }206      }207 208      FileActions = &FileActionsStore;209      posix_spawn_file_actions_init(FileActions);210 211      // Redirect stdin/stdout.212      if (RedirectIO_PS(RedirectsStr[0], 0, ErrMsg, FileActions) ||213          RedirectIO_PS(RedirectsStr[1], 1, ErrMsg, FileActions))214        return false;215      if (!Redirects[1] || !Redirects[2] || *Redirects[1] != *Redirects[2]) {216        // Just redirect stderr217        if (RedirectIO_PS(RedirectsStr[2], 2, ErrMsg, FileActions))218          return false;219      } else {220        // If stdout and stderr should go to the same place, redirect stderr221        // to the FD already open for stdout.222        if (int Err = posix_spawn_file_actions_adddup2(FileActions, 1, 2))223          return !MakeErrMsg(ErrMsg, "Can't redirect stderr to stdout", Err);224      }225    }226 227    if (!Envp)228#if !USE_NSGETENVIRON229      Envp = const_cast<const char **>(environ);230#else231      // environ is missing in dylibs.232      Envp = const_cast<const char **>(*_NSGetEnviron());233#endif234 235    constexpr int maxRetries = 8;236    int retries = 0;237    pid_t PID;238    int Err;239    do {240      PID = 0; // Make Valgrind happy.241      Err = posix_spawn(&PID, Program.str().c_str(), FileActions,242                        /*attrp*/ nullptr, const_cast<char **>(Argv),243                        const_cast<char **>(Envp));244    } while (Err == EINTR && ++retries < maxRetries);245 246    if (FileActions)247      posix_spawn_file_actions_destroy(FileActions);248 249    if (Err)250      return !MakeErrMsg(ErrMsg, "posix_spawn failed", Err);251 252    PI.Pid = PID;253    PI.Process = PID;254 255    return true;256  }257#endif // HAVE_POSIX_SPAWN258 259  // Create a child process.260  int child = fork();261  switch (child) {262  // An error occurred:  Return to the caller.263  case -1:264    MakeErrMsg(ErrMsg, "Couldn't fork");265    return false;266 267  // Child process: Execute the program.268  case 0: {269    // Redirect file descriptors...270    if (!Redirects.empty()) {271      // Redirect stdin272      if (RedirectIO(Redirects[0], 0, ErrMsg)) {273        return false;274      }275      // Redirect stdout276      if (RedirectIO(Redirects[1], 1, ErrMsg)) {277        return false;278      }279      if (Redirects[1] && Redirects[2] && *Redirects[1] == *Redirects[2]) {280        // If stdout and stderr should go to the same place, redirect stderr281        // to the FD already open for stdout.282        if (-1 == dup2(1, 2)) {283          MakeErrMsg(ErrMsg, "Can't redirect stderr to stdout");284          return false;285        }286      } else {287        // Just redirect stderr288        if (RedirectIO(Redirects[2], 2, ErrMsg)) {289          return false;290        }291      }292    }293 294    if (DetachProcess) {295      // Detach from controlling terminal296      if (::setsid() == -1) {297        MakeErrMsg(ErrMsg, "Could not detach process, ::setsid failed");298        return false;299      }300    }301 302    // Set memory limits303    if (MemoryLimit != 0) {304      SetMemoryLimits(MemoryLimit);305    }306 307    // Execute!308    std::string PathStr = std::string(Program);309    if (Envp != nullptr)310      execve(PathStr.c_str(), const_cast<char **>(Argv),311             const_cast<char **>(Envp));312    else313      execv(PathStr.c_str(), const_cast<char **>(Argv));314    // If the execve() failed, we should exit. Follow Unix protocol and315    // return 127 if the executable was not found, and 126 otherwise.316    // Use _exit rather than exit so that atexit functions and static317    // object destructors cloned from the parent process aren't318    // redundantly run, and so that any data buffered in stdio buffers319    // cloned from the parent aren't redundantly written out.320    _exit(errno == ENOENT ? 127 : 126);321  }322 323  // Parent process: Break out of the switch to do our processing.324  default:325    break;326  }327 328  PI.Pid = child;329  PI.Process = child;330 331  return true;332}333 334namespace llvm {335namespace sys {336 337#if defined(_AIX)338static pid_t(wait4)(pid_t pid, int *status, int options, struct rusage *usage);339#elif !defined(__Fuchsia__)340using ::wait4;341#endif342 343} // namespace sys344} // namespace llvm345 346#ifdef _AIX347#ifndef _ALL_SOURCE348extern "C" pid_t(wait4)(pid_t pid, int *status, int options,349                        struct rusage *usage);350#endif351pid_t(llvm::sys::wait4)(pid_t pid, int *status, int options,352                        struct rusage *usage) {353  assert(pid > 0 && "Only expecting to handle actual PID values!");354  assert((options & ~WNOHANG) == 0 && "Expecting WNOHANG at most!");355  assert(usage && "Expecting usage collection!");356 357  // AIX wait4 does not work well with WNOHANG.358  if (!(options & WNOHANG))359    return ::wait4(pid, status, options, usage);360 361  // For WNOHANG, we use waitid (which supports WNOWAIT) until the child process362  // has terminated.363  siginfo_t WaitIdInfo;364  WaitIdInfo.si_pid = 0;365  int WaitIdRetVal =366      waitid(P_PID, pid, &WaitIdInfo, WNOWAIT | WEXITED | options);367 368  if (WaitIdRetVal == -1 || WaitIdInfo.si_pid == 0)369    return WaitIdRetVal;370 371  assert(WaitIdInfo.si_pid == pid);372 373  // The child has already terminated, so a blocking wait on it is okay in the374  // absence of indiscriminate `wait` calls from the current process (which375  // would cause the call here to fail with ECHILD).376  return ::wait4(pid, status, options & ~WNOHANG, usage);377}378#endif379 380ProcessInfo llvm::sys::Wait(const ProcessInfo &PI,381                            std::optional<unsigned> SecondsToWait,382                            std::string *ErrMsg,383                            std::optional<ProcessStatistics> *ProcStat,384                            bool Polling) {385  struct sigaction Act, Old;386  assert(PI.Pid && "invalid pid to wait on, process not started?");387 388  int WaitPidOptions = 0;389  pid_t ChildPid = PI.Pid;390  bool WaitUntilTerminates = false;391  if (!SecondsToWait) {392    WaitUntilTerminates = true;393  } else {394    if (*SecondsToWait == 0)395      WaitPidOptions = WNOHANG;396 397    // Install a timeout handler.  The handler itself does nothing, but the398    // simple fact of having a handler at all causes the wait below to return399    // with EINTR, unlike if we used SIG_IGN.400    memset(&Act, 0, sizeof(Act));401    Act.sa_handler = TimeOutHandler;402    sigemptyset(&Act.sa_mask);403    sigaction(SIGALRM, &Act, &Old);404    // FIXME The alarm signal may be delivered to another thread.405    alarm(*SecondsToWait);406  }407 408  // Parent process: Wait for the child process to terminate.409  int status = 0;410  ProcessInfo WaitResult;411#ifndef __Fuchsia__412  rusage Info;413  if (ProcStat)414    ProcStat->reset();415 416  do {417    WaitResult.Pid = sys::wait4(ChildPid, &status, WaitPidOptions, &Info);418  } while (WaitUntilTerminates && WaitResult.Pid == -1 && errno == EINTR);419#endif420 421  if (WaitResult.Pid != PI.Pid) {422    if (WaitResult.Pid == 0) {423      // Non-blocking wait.424      return WaitResult;425    } else {426      if (SecondsToWait && errno == EINTR && !Polling) {427        // Kill the child.428        kill(PI.Pid, SIGKILL);429 430        // Turn off the alarm and restore the signal handler431        alarm(0);432        sigaction(SIGALRM, &Old, nullptr);433 434        // Wait for child to die435        // FIXME This could grab some other child process out from another436        // waiting thread and then leave a zombie anyway.437        if (wait(&status) != ChildPid)438          MakeErrMsg(ErrMsg, "Child timed out but wouldn't die");439        else440          MakeErrMsg(ErrMsg, "Child timed out", 0);441 442        WaitResult.ReturnCode = -2; // Timeout detected443        return WaitResult;444      } else if (errno != EINTR) {445        MakeErrMsg(ErrMsg, "Error waiting for child process");446        WaitResult.ReturnCode = -1;447        return WaitResult;448      }449    }450  }451 452  // We exited normally without timeout, so turn off the timer.453  if (SecondsToWait && !WaitUntilTerminates) {454    alarm(0);455    sigaction(SIGALRM, &Old, nullptr);456  }457 458#ifndef __Fuchsia__459  if (ProcStat) {460    std::chrono::microseconds UserT = toDuration(Info.ru_utime);461    std::chrono::microseconds KernelT = toDuration(Info.ru_stime);462    uint64_t PeakMemory = 0;463#if !defined(__HAIKU__) && !defined(__MVS__)464    PeakMemory = static_cast<uint64_t>(Info.ru_maxrss);465#endif466    *ProcStat = ProcessStatistics{UserT + KernelT, UserT, PeakMemory};467  }468#endif469 470  // Return the proper exit status. Detect error conditions471  // so we can return -1 for them and set ErrMsg informatively.472  int result = 0;473  if (WIFEXITED(status)) {474    result = WEXITSTATUS(status);475    WaitResult.ReturnCode = result;476 477    if (result == 127) {478      if (ErrMsg)479        *ErrMsg = llvm::sys::StrError(ENOENT);480      WaitResult.ReturnCode = -1;481      return WaitResult;482    }483    if (result == 126) {484      if (ErrMsg)485        *ErrMsg = "Program could not be executed";486      WaitResult.ReturnCode = -1;487      return WaitResult;488    }489  } else if (WIFSIGNALED(status)) {490    if (ErrMsg) {491      *ErrMsg = strsignal(WTERMSIG(status));492#ifdef WCOREDUMP493      if (WCOREDUMP(status))494        *ErrMsg += " (core dumped)";495#endif496    }497    // Return a special value to indicate that the process received an unhandled498    // signal during execution as opposed to failing to execute.499    WaitResult.ReturnCode = -2;500  }501  return WaitResult;502}503 504std::error_code llvm::sys::ChangeStdinMode(fs::OpenFlags Flags) {505  if (!(Flags & fs::OF_Text))506    return ChangeStdinToBinary();507  return std::error_code();508}509 510std::error_code llvm::sys::ChangeStdoutMode(fs::OpenFlags Flags) {511  if (!(Flags & fs::OF_Text))512    return ChangeStdoutToBinary();513  return std::error_code();514}515 516std::error_code llvm::sys::ChangeStdinToBinary() {517#ifdef __MVS__518  return disableAutoConversion(STDIN_FILENO);519#else520  // Do nothing, as Unix doesn't differentiate between text and binary.521  return std::error_code();522#endif523}524 525std::error_code llvm::sys::ChangeStdoutToBinary() {526  // Do nothing, as Unix doesn't differentiate between text and binary.527  return std::error_code();528}529 530std::error_code531llvm::sys::writeFileWithEncoding(StringRef FileName, StringRef Contents,532                                 WindowsEncodingMethod Encoding /*unused*/) {533  std::error_code EC;534  llvm::raw_fd_ostream OS(FileName, EC,535                          llvm::sys::fs::OpenFlags::OF_TextWithCRLF);536 537  if (EC)538    return EC;539 540  OS << Contents;541 542  if (OS.has_error())543    return make_error_code(errc::io_error);544 545  return EC;546}547 548bool llvm::sys::commandLineFitsWithinSystemLimits(StringRef Program,549                                                  ArrayRef<StringRef> Args) {550  static long ArgMax = sysconf(_SC_ARG_MAX);551  // POSIX requires that _POSIX_ARG_MAX is 4096, which is the lowest possible552  // value for ARG_MAX on a POSIX compliant system.553  static long ArgMin = _POSIX_ARG_MAX;554 555  // This the same baseline used by xargs.556  long EffectiveArgMax = 128 * 1024;557 558  if (EffectiveArgMax > ArgMax)559    EffectiveArgMax = ArgMax;560  else if (EffectiveArgMax < ArgMin)561    EffectiveArgMax = ArgMin;562 563  // System says no practical limit.564  if (ArgMax == -1)565    return true;566 567  // Conservatively account for space required by environment variables.568  long HalfArgMax = EffectiveArgMax / 2;569 570  size_t ArgLength = Program.size() + 1;571  for (StringRef Arg : Args) {572    // Ensure that we do not exceed the MAX_ARG_STRLEN constant on Linux, which573    // does not have a constant unlike what the man pages would have you574    // believe. Since this limit is pretty high, perform the check575    // unconditionally rather than trying to be aggressive and limiting it to576    // Linux only.577    if (Arg.size() >= (32 * 4096))578      return false;579 580    ArgLength += Arg.size() + 1;581    if (ArgLength > size_t(HalfArgMax)) {582      return false;583    }584  }585 586  return true;587}588