Path: blob/main/contrib/llvm-project/lldb/source/Host/common/ProcessLaunchInfo.cpp
39606 views
//===-- ProcessLaunchInfo.cpp ---------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include <climits>910#include "lldb/Host/Config.h"11#include "lldb/Host/FileAction.h"12#include "lldb/Host/FileSystem.h"13#include "lldb/Host/HostInfo.h"14#include "lldb/Host/ProcessLaunchInfo.h"15#include "lldb/Utility/LLDBLog.h"16#include "lldb/Utility/Log.h"17#include "lldb/Utility/StreamString.h"1819#include "llvm/Support/ConvertUTF.h"20#include "llvm/Support/FileSystem.h"2122#if !defined(_WIN32)23#include <climits>24#endif2526using namespace lldb;27using namespace lldb_private;2829// ProcessLaunchInfo member functions3031ProcessLaunchInfo::ProcessLaunchInfo()32: ProcessInfo(), m_working_dir(), m_plugin_name(), m_flags(0),33m_file_actions(), m_pty(new PseudoTerminal), m_monitor_callback(nullptr) {34}3536ProcessLaunchInfo::ProcessLaunchInfo(const FileSpec &stdin_file_spec,37const FileSpec &stdout_file_spec,38const FileSpec &stderr_file_spec,39const FileSpec &working_directory,40uint32_t launch_flags)41: ProcessInfo(), m_working_dir(), m_plugin_name(), m_flags(launch_flags),42m_file_actions(), m_pty(new PseudoTerminal) {43if (stdin_file_spec) {44FileAction file_action;45const bool read = true;46const bool write = false;47if (file_action.Open(STDIN_FILENO, stdin_file_spec, read, write))48AppendFileAction(file_action);49}50if (stdout_file_spec) {51FileAction file_action;52const bool read = false;53const bool write = true;54if (file_action.Open(STDOUT_FILENO, stdout_file_spec, read, write))55AppendFileAction(file_action);56}57if (stderr_file_spec) {58FileAction file_action;59const bool read = false;60const bool write = true;61if (file_action.Open(STDERR_FILENO, stderr_file_spec, read, write))62AppendFileAction(file_action);63}64if (working_directory)65SetWorkingDirectory(working_directory);66}6768bool ProcessLaunchInfo::AppendCloseFileAction(int fd) {69FileAction file_action;70if (file_action.Close(fd)) {71AppendFileAction(file_action);72return true;73}74return false;75}7677bool ProcessLaunchInfo::AppendDuplicateFileAction(int fd, int dup_fd) {78FileAction file_action;79if (file_action.Duplicate(fd, dup_fd)) {80AppendFileAction(file_action);81return true;82}83return false;84}8586bool ProcessLaunchInfo::AppendOpenFileAction(int fd, const FileSpec &file_spec,87bool read, bool write) {88FileAction file_action;89if (file_action.Open(fd, file_spec, read, write)) {90AppendFileAction(file_action);91return true;92}93return false;94}9596bool ProcessLaunchInfo::AppendSuppressFileAction(int fd, bool read,97bool write) {98FileAction file_action;99if (file_action.Open(fd, FileSpec(FileSystem::DEV_NULL), read, write)) {100AppendFileAction(file_action);101return true;102}103return false;104}105106const FileAction *ProcessLaunchInfo::GetFileActionAtIndex(size_t idx) const {107if (idx < m_file_actions.size())108return &m_file_actions[idx];109return nullptr;110}111112const FileAction *ProcessLaunchInfo::GetFileActionForFD(int fd) const {113for (size_t idx = 0, count = m_file_actions.size(); idx < count; ++idx) {114if (m_file_actions[idx].GetFD() == fd)115return &m_file_actions[idx];116}117return nullptr;118}119120const FileSpec &ProcessLaunchInfo::GetWorkingDirectory() const {121return m_working_dir;122}123124void ProcessLaunchInfo::SetWorkingDirectory(const FileSpec &working_dir) {125m_working_dir = working_dir;126}127128llvm::StringRef ProcessLaunchInfo::GetProcessPluginName() const {129return llvm::StringRef(m_plugin_name);130}131132void ProcessLaunchInfo::SetProcessPluginName(llvm::StringRef plugin) {133m_plugin_name = std::string(plugin);134}135136const FileSpec &ProcessLaunchInfo::GetShell() const { return m_shell; }137138void ProcessLaunchInfo::SetShell(const FileSpec &shell) {139m_shell = shell;140if (m_shell) {141FileSystem::Instance().ResolveExecutableLocation(m_shell);142m_flags.Set(lldb::eLaunchFlagLaunchInShell);143} else144m_flags.Clear(lldb::eLaunchFlagLaunchInShell);145}146147void ProcessLaunchInfo::SetLaunchInSeparateProcessGroup(bool separate) {148if (separate)149m_flags.Set(lldb::eLaunchFlagLaunchInSeparateProcessGroup);150else151m_flags.Clear(lldb::eLaunchFlagLaunchInSeparateProcessGroup);152}153154void ProcessLaunchInfo::SetShellExpandArguments(bool expand) {155if (expand)156m_flags.Set(lldb::eLaunchFlagShellExpandArguments);157else158m_flags.Clear(lldb::eLaunchFlagShellExpandArguments);159}160161void ProcessLaunchInfo::Clear() {162ProcessInfo::Clear();163m_working_dir.Clear();164m_plugin_name.clear();165m_shell.Clear();166m_flags.Clear();167m_file_actions.clear();168m_resume_count = 0;169m_listener_sp.reset();170m_hijack_listener_sp.reset();171}172173void ProcessLaunchInfo::NoOpMonitorCallback(lldb::pid_t pid, int signal,174int status) {175Log *log = GetLog(LLDBLog::Process);176LLDB_LOG(log, "pid = {0}, signal = {1}, status = {2}", pid, signal, status);177}178179bool ProcessLaunchInfo::MonitorProcess() const {180if (m_monitor_callback && ProcessIDIsValid()) {181llvm::Expected<HostThread> maybe_thread =182Host::StartMonitoringChildProcess(m_monitor_callback, GetProcessID());183if (!maybe_thread)184LLDB_LOG_ERROR(GetLog(LLDBLog::Host), maybe_thread.takeError(),185"failed to launch host thread: {0}");186return true;187}188return false;189}190191void ProcessLaunchInfo::SetDetachOnError(bool enable) {192if (enable)193m_flags.Set(lldb::eLaunchFlagDetachOnError);194else195m_flags.Clear(lldb::eLaunchFlagDetachOnError);196}197198llvm::Error ProcessLaunchInfo::SetUpPtyRedirection() {199Log *log = GetLog(LLDBLog::Process);200201bool stdin_free = GetFileActionForFD(STDIN_FILENO) == nullptr;202bool stdout_free = GetFileActionForFD(STDOUT_FILENO) == nullptr;203bool stderr_free = GetFileActionForFD(STDERR_FILENO) == nullptr;204bool any_free = stdin_free || stdout_free || stderr_free;205if (!any_free)206return llvm::Error::success();207208LLDB_LOG(log, "Generating a pty to use for stdin/out/err");209210int open_flags = O_RDWR | O_NOCTTY;211#if !defined(_WIN32)212// We really shouldn't be specifying platform specific flags that are213// intended for a system call in generic code. But this will have to214// do for now.215open_flags |= O_CLOEXEC;216#endif217if (llvm::Error Err = m_pty->OpenFirstAvailablePrimary(open_flags))218return Err;219220const FileSpec secondary_file_spec(m_pty->GetSecondaryName());221222if (stdin_free)223AppendOpenFileAction(STDIN_FILENO, secondary_file_spec, true, false);224225if (stdout_free)226AppendOpenFileAction(STDOUT_FILENO, secondary_file_spec, false, true);227228if (stderr_free)229AppendOpenFileAction(STDERR_FILENO, secondary_file_spec, false, true);230return llvm::Error::success();231}232233bool ProcessLaunchInfo::ConvertArgumentsForLaunchingInShell(234Status &error, bool will_debug, bool first_arg_is_full_shell_command,235uint32_t num_resumes) {236error.Clear();237238if (GetFlags().Test(eLaunchFlagLaunchInShell)) {239if (m_shell) {240std::string shell_executable = m_shell.GetPath();241242const char **argv = GetArguments().GetConstArgumentVector();243if (argv == nullptr || argv[0] == nullptr)244return false;245Args shell_arguments;246shell_arguments.AppendArgument(shell_executable);247const llvm::Triple &triple = GetArchitecture().GetTriple();248if (triple.getOS() == llvm::Triple::Win32 &&249!triple.isWindowsCygwinEnvironment())250shell_arguments.AppendArgument(llvm::StringRef("/C"));251else252shell_arguments.AppendArgument(llvm::StringRef("-c"));253254StreamString shell_command;255if (will_debug) {256// Add a modified PATH environment variable in case argv[0] is a257// relative path.258const char *argv0 = argv[0];259FileSpec arg_spec(argv0);260if (arg_spec.IsRelative()) {261// We have a relative path to our executable which may not work if we262// just try to run "a.out" (without it being converted to "./a.out")263FileSpec working_dir = GetWorkingDirectory();264// Be sure to put quotes around PATH's value in case any paths have265// spaces...266std::string new_path("PATH=\"");267const size_t empty_path_len = new_path.size();268269if (working_dir) {270new_path += working_dir.GetPath();271} else {272llvm::SmallString<64> cwd;273if (! llvm::sys::fs::current_path(cwd))274new_path += cwd;275}276std::string curr_path;277if (HostInfo::GetEnvironmentVar("PATH", curr_path)) {278if (new_path.size() > empty_path_len)279new_path += ':';280new_path += curr_path;281}282new_path += "\" ";283shell_command.PutCString(new_path);284}285286if (triple.getOS() != llvm::Triple::Win32 ||287triple.isWindowsCygwinEnvironment())288shell_command.PutCString("exec");289290// Only Apple supports /usr/bin/arch being able to specify the291// architecture292if (GetArchitecture().IsValid() && // Valid architecture293GetArchitecture().GetTriple().getVendor() ==294llvm::Triple::Apple && // Apple only295GetArchitecture().GetCore() !=296ArchSpec::eCore_x86_64_x86_64h) // Don't do this for x86_64h297{298shell_command.Printf(" /usr/bin/arch -arch %s",299GetArchitecture().GetArchitectureName());300// Set the resume count to 2:301// 1 - stop in shell302// 2 - stop in /usr/bin/arch303// 3 - then we will stop in our program304SetResumeCount(num_resumes + 1);305} else {306// Set the resume count to 1:307// 1 - stop in shell308// 2 - then we will stop in our program309SetResumeCount(num_resumes);310}311}312313if (first_arg_is_full_shell_command) {314// There should only be one argument that is the shell command itself315// to be used as is316if (argv[0] && !argv[1])317shell_command.Printf("%s", argv[0]);318else319return false;320} else {321for (size_t i = 0; argv[i] != nullptr; ++i) {322std::string safe_arg = Args::GetShellSafeArgument(m_shell, argv[i]);323if (safe_arg.empty())324safe_arg = "\"\"";325// Add a space to separate this arg from the previous one.326shell_command.PutCString(" ");327shell_command.PutCString(safe_arg);328}329}330shell_arguments.AppendArgument(shell_command.GetString());331m_executable = m_shell;332m_arguments = shell_arguments;333return true;334} else {335error.SetErrorString("invalid shell path");336}337} else {338error.SetErrorString("not launching in shell");339}340return false;341}342343344