in lib/LLVMSupport/Support/Unix/Program.inc [176:327]
static bool Execute(ProcessInfo &PI, StringRef Program,
ArrayRef<StringRef> Args, Optional<ArrayRef<StringRef>> Env,
ArrayRef<Optional<StringRef>> Redirects,
unsigned MemoryLimit, std::string *ErrMsg) {
if (!llvm::sys::fs::exists(Program)) {
if (ErrMsg)
*ErrMsg = std::string("Executable \"") + Program.str() +
std::string("\" doesn't exist!");
return false;
}
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] = *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
// Explicitly initialized to prevent what appears to be a valgrind false
// positive.
pid_t PID = 0;
int Err = posix_spawn(&PID, Program.str().c_str(), FileActions,
/*attrp*/ nullptr, const_cast<char **>(Argv),
const_cast<char **>(Envp));
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 = 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;
}