1dda28197Spatrick //===-- ProcessLaunchInfo.cpp ---------------------------------------------===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick
9061da546Spatrick #include <climits>
10061da546Spatrick
11061da546Spatrick #include "lldb/Host/Config.h"
12061da546Spatrick #include "lldb/Host/FileAction.h"
13061da546Spatrick #include "lldb/Host/FileSystem.h"
14061da546Spatrick #include "lldb/Host/HostInfo.h"
15061da546Spatrick #include "lldb/Host/ProcessLaunchInfo.h"
16*f6aab3d8Srobert #include "lldb/Utility/LLDBLog.h"
17061da546Spatrick #include "lldb/Utility/Log.h"
18061da546Spatrick #include "lldb/Utility/StreamString.h"
19061da546Spatrick
20061da546Spatrick #include "llvm/Support/ConvertUTF.h"
21061da546Spatrick #include "llvm/Support/FileSystem.h"
22061da546Spatrick
23061da546Spatrick #if !defined(_WIN32)
24be691f3bSpatrick #include <climits>
25061da546Spatrick #endif
26061da546Spatrick
27061da546Spatrick using namespace lldb;
28061da546Spatrick using namespace lldb_private;
29061da546Spatrick
30061da546Spatrick // ProcessLaunchInfo member functions
31061da546Spatrick
ProcessLaunchInfo()32061da546Spatrick ProcessLaunchInfo::ProcessLaunchInfo()
33061da546Spatrick : ProcessInfo(), m_working_dir(), m_plugin_name(), m_flags(0),
34be691f3bSpatrick m_file_actions(), m_pty(new PseudoTerminal), m_monitor_callback(nullptr),
35be691f3bSpatrick m_listener_sp(), m_hijack_listener_sp(), m_scripted_process_class_name(),
36be691f3bSpatrick m_scripted_process_dictionary_sp() {}
37061da546Spatrick
ProcessLaunchInfo(const FileSpec & stdin_file_spec,const FileSpec & stdout_file_spec,const FileSpec & stderr_file_spec,const FileSpec & working_directory,uint32_t launch_flags)38061da546Spatrick ProcessLaunchInfo::ProcessLaunchInfo(const FileSpec &stdin_file_spec,
39061da546Spatrick const FileSpec &stdout_file_spec,
40061da546Spatrick const FileSpec &stderr_file_spec,
41061da546Spatrick const FileSpec &working_directory,
42061da546Spatrick uint32_t launch_flags)
43061da546Spatrick : ProcessInfo(), m_working_dir(), m_plugin_name(), m_flags(launch_flags),
44*f6aab3d8Srobert m_file_actions(), m_pty(new PseudoTerminal),
45be691f3bSpatrick m_scripted_process_class_name(), m_scripted_process_dictionary_sp() {
46061da546Spatrick if (stdin_file_spec) {
47061da546Spatrick FileAction file_action;
48061da546Spatrick const bool read = true;
49061da546Spatrick const bool write = false;
50061da546Spatrick if (file_action.Open(STDIN_FILENO, stdin_file_spec, read, write))
51061da546Spatrick AppendFileAction(file_action);
52061da546Spatrick }
53061da546Spatrick if (stdout_file_spec) {
54061da546Spatrick FileAction file_action;
55061da546Spatrick const bool read = false;
56061da546Spatrick const bool write = true;
57061da546Spatrick if (file_action.Open(STDOUT_FILENO, stdout_file_spec, read, write))
58061da546Spatrick AppendFileAction(file_action);
59061da546Spatrick }
60061da546Spatrick if (stderr_file_spec) {
61061da546Spatrick FileAction file_action;
62061da546Spatrick const bool read = false;
63061da546Spatrick const bool write = true;
64061da546Spatrick if (file_action.Open(STDERR_FILENO, stderr_file_spec, read, write))
65061da546Spatrick AppendFileAction(file_action);
66061da546Spatrick }
67061da546Spatrick if (working_directory)
68061da546Spatrick SetWorkingDirectory(working_directory);
69061da546Spatrick }
70061da546Spatrick
AppendCloseFileAction(int fd)71061da546Spatrick bool ProcessLaunchInfo::AppendCloseFileAction(int fd) {
72061da546Spatrick FileAction file_action;
73061da546Spatrick if (file_action.Close(fd)) {
74061da546Spatrick AppendFileAction(file_action);
75061da546Spatrick return true;
76061da546Spatrick }
77061da546Spatrick return false;
78061da546Spatrick }
79061da546Spatrick
AppendDuplicateFileAction(int fd,int dup_fd)80061da546Spatrick bool ProcessLaunchInfo::AppendDuplicateFileAction(int fd, int dup_fd) {
81061da546Spatrick FileAction file_action;
82061da546Spatrick if (file_action.Duplicate(fd, dup_fd)) {
83061da546Spatrick AppendFileAction(file_action);
84061da546Spatrick return true;
85061da546Spatrick }
86061da546Spatrick return false;
87061da546Spatrick }
88061da546Spatrick
AppendOpenFileAction(int fd,const FileSpec & file_spec,bool read,bool write)89061da546Spatrick bool ProcessLaunchInfo::AppendOpenFileAction(int fd, const FileSpec &file_spec,
90061da546Spatrick bool read, bool write) {
91061da546Spatrick FileAction file_action;
92061da546Spatrick if (file_action.Open(fd, file_spec, read, write)) {
93061da546Spatrick AppendFileAction(file_action);
94061da546Spatrick return true;
95061da546Spatrick }
96061da546Spatrick return false;
97061da546Spatrick }
98061da546Spatrick
AppendSuppressFileAction(int fd,bool read,bool write)99061da546Spatrick bool ProcessLaunchInfo::AppendSuppressFileAction(int fd, bool read,
100061da546Spatrick bool write) {
101061da546Spatrick FileAction file_action;
102061da546Spatrick if (file_action.Open(fd, FileSpec(FileSystem::DEV_NULL), read, write)) {
103061da546Spatrick AppendFileAction(file_action);
104061da546Spatrick return true;
105061da546Spatrick }
106061da546Spatrick return false;
107061da546Spatrick }
108061da546Spatrick
GetFileActionAtIndex(size_t idx) const109061da546Spatrick const FileAction *ProcessLaunchInfo::GetFileActionAtIndex(size_t idx) const {
110061da546Spatrick if (idx < m_file_actions.size())
111061da546Spatrick return &m_file_actions[idx];
112061da546Spatrick return nullptr;
113061da546Spatrick }
114061da546Spatrick
GetFileActionForFD(int fd) const115061da546Spatrick const FileAction *ProcessLaunchInfo::GetFileActionForFD(int fd) const {
116061da546Spatrick for (size_t idx = 0, count = m_file_actions.size(); idx < count; ++idx) {
117061da546Spatrick if (m_file_actions[idx].GetFD() == fd)
118061da546Spatrick return &m_file_actions[idx];
119061da546Spatrick }
120061da546Spatrick return nullptr;
121061da546Spatrick }
122061da546Spatrick
GetWorkingDirectory() const123061da546Spatrick const FileSpec &ProcessLaunchInfo::GetWorkingDirectory() const {
124061da546Spatrick return m_working_dir;
125061da546Spatrick }
126061da546Spatrick
SetWorkingDirectory(const FileSpec & working_dir)127061da546Spatrick void ProcessLaunchInfo::SetWorkingDirectory(const FileSpec &working_dir) {
128061da546Spatrick m_working_dir = working_dir;
129061da546Spatrick }
130061da546Spatrick
GetProcessPluginName() const131061da546Spatrick const char *ProcessLaunchInfo::GetProcessPluginName() const {
132061da546Spatrick return (m_plugin_name.empty() ? nullptr : m_plugin_name.c_str());
133061da546Spatrick }
134061da546Spatrick
SetProcessPluginName(llvm::StringRef plugin)135061da546Spatrick void ProcessLaunchInfo::SetProcessPluginName(llvm::StringRef plugin) {
136dda28197Spatrick m_plugin_name = std::string(plugin);
137061da546Spatrick }
138061da546Spatrick
GetShell() const139061da546Spatrick const FileSpec &ProcessLaunchInfo::GetShell() const { return m_shell; }
140061da546Spatrick
SetShell(const FileSpec & shell)141061da546Spatrick void ProcessLaunchInfo::SetShell(const FileSpec &shell) {
142061da546Spatrick m_shell = shell;
143061da546Spatrick if (m_shell) {
144061da546Spatrick FileSystem::Instance().ResolveExecutableLocation(m_shell);
145061da546Spatrick m_flags.Set(lldb::eLaunchFlagLaunchInShell);
146061da546Spatrick } else
147061da546Spatrick m_flags.Clear(lldb::eLaunchFlagLaunchInShell);
148061da546Spatrick }
149061da546Spatrick
SetLaunchInSeparateProcessGroup(bool separate)150061da546Spatrick void ProcessLaunchInfo::SetLaunchInSeparateProcessGroup(bool separate) {
151061da546Spatrick if (separate)
152061da546Spatrick m_flags.Set(lldb::eLaunchFlagLaunchInSeparateProcessGroup);
153061da546Spatrick else
154061da546Spatrick m_flags.Clear(lldb::eLaunchFlagLaunchInSeparateProcessGroup);
155061da546Spatrick }
156061da546Spatrick
SetShellExpandArguments(bool expand)157061da546Spatrick void ProcessLaunchInfo::SetShellExpandArguments(bool expand) {
158061da546Spatrick if (expand)
159061da546Spatrick m_flags.Set(lldb::eLaunchFlagShellExpandArguments);
160061da546Spatrick else
161061da546Spatrick m_flags.Clear(lldb::eLaunchFlagShellExpandArguments);
162061da546Spatrick }
163061da546Spatrick
Clear()164061da546Spatrick void ProcessLaunchInfo::Clear() {
165061da546Spatrick ProcessInfo::Clear();
166061da546Spatrick m_working_dir.Clear();
167061da546Spatrick m_plugin_name.clear();
168061da546Spatrick m_shell.Clear();
169061da546Spatrick m_flags.Clear();
170061da546Spatrick m_file_actions.clear();
171061da546Spatrick m_resume_count = 0;
172061da546Spatrick m_listener_sp.reset();
173061da546Spatrick m_hijack_listener_sp.reset();
174be691f3bSpatrick m_scripted_process_class_name.clear();
175be691f3bSpatrick m_scripted_process_dictionary_sp.reset();
176061da546Spatrick }
177061da546Spatrick
NoOpMonitorCallback(lldb::pid_t pid,int signal,int status)178*f6aab3d8Srobert void ProcessLaunchInfo::NoOpMonitorCallback(lldb::pid_t pid, int signal,
179*f6aab3d8Srobert int status) {
180*f6aab3d8Srobert Log *log = GetLog(LLDBLog::Process);
181*f6aab3d8Srobert LLDB_LOG(log, "pid = {0}, signal = {1}, status = {2}", pid, signal, status);
182061da546Spatrick }
183061da546Spatrick
MonitorProcess() const184061da546Spatrick bool ProcessLaunchInfo::MonitorProcess() const {
185061da546Spatrick if (m_monitor_callback && ProcessIDIsValid()) {
186061da546Spatrick llvm::Expected<HostThread> maybe_thread =
187*f6aab3d8Srobert Host::StartMonitoringChildProcess(m_monitor_callback, GetProcessID());
188061da546Spatrick if (!maybe_thread)
189*f6aab3d8Srobert LLDB_LOG(GetLog(LLDBLog::Host), "failed to launch host thread: {}",
190061da546Spatrick llvm::toString(maybe_thread.takeError()));
191061da546Spatrick return true;
192061da546Spatrick }
193061da546Spatrick return false;
194061da546Spatrick }
195061da546Spatrick
SetDetachOnError(bool enable)196061da546Spatrick void ProcessLaunchInfo::SetDetachOnError(bool enable) {
197061da546Spatrick if (enable)
198061da546Spatrick m_flags.Set(lldb::eLaunchFlagDetachOnError);
199061da546Spatrick else
200061da546Spatrick m_flags.Clear(lldb::eLaunchFlagDetachOnError);
201061da546Spatrick }
202061da546Spatrick
SetUpPtyRedirection()203061da546Spatrick llvm::Error ProcessLaunchInfo::SetUpPtyRedirection() {
204*f6aab3d8Srobert Log *log = GetLog(LLDBLog::Process);
205*f6aab3d8Srobert
206*f6aab3d8Srobert bool stdin_free = GetFileActionForFD(STDIN_FILENO) == nullptr;
207*f6aab3d8Srobert bool stdout_free = GetFileActionForFD(STDOUT_FILENO) == nullptr;
208*f6aab3d8Srobert bool stderr_free = GetFileActionForFD(STDERR_FILENO) == nullptr;
209*f6aab3d8Srobert bool any_free = stdin_free || stdout_free || stderr_free;
210*f6aab3d8Srobert if (!any_free)
211*f6aab3d8Srobert return llvm::Error::success();
212*f6aab3d8Srobert
213061da546Spatrick LLDB_LOG(log, "Generating a pty to use for stdin/out/err");
214061da546Spatrick
215061da546Spatrick int open_flags = O_RDWR | O_NOCTTY;
216061da546Spatrick #if !defined(_WIN32)
217061da546Spatrick // We really shouldn't be specifying platform specific flags that are
218061da546Spatrick // intended for a system call in generic code. But this will have to
219061da546Spatrick // do for now.
220061da546Spatrick open_flags |= O_CLOEXEC;
221061da546Spatrick #endif
222be691f3bSpatrick if (llvm::Error Err = m_pty->OpenFirstAvailablePrimary(open_flags))
223be691f3bSpatrick return Err;
224be691f3bSpatrick
225be691f3bSpatrick const FileSpec secondary_file_spec(m_pty->GetSecondaryName());
226061da546Spatrick
227*f6aab3d8Srobert if (stdin_free)
228dda28197Spatrick AppendOpenFileAction(STDIN_FILENO, secondary_file_spec, true, false);
229061da546Spatrick
230*f6aab3d8Srobert if (stdout_free)
231dda28197Spatrick AppendOpenFileAction(STDOUT_FILENO, secondary_file_spec, false, true);
232061da546Spatrick
233*f6aab3d8Srobert if (stderr_free)
234dda28197Spatrick AppendOpenFileAction(STDERR_FILENO, secondary_file_spec, false, true);
235061da546Spatrick return llvm::Error::success();
236061da546Spatrick }
237061da546Spatrick
ConvertArgumentsForLaunchingInShell(Status & error,bool will_debug,bool first_arg_is_full_shell_command,uint32_t num_resumes)238061da546Spatrick bool ProcessLaunchInfo::ConvertArgumentsForLaunchingInShell(
239be691f3bSpatrick Status &error, bool will_debug, bool first_arg_is_full_shell_command,
240be691f3bSpatrick uint32_t num_resumes) {
241061da546Spatrick error.Clear();
242061da546Spatrick
243061da546Spatrick if (GetFlags().Test(eLaunchFlagLaunchInShell)) {
244061da546Spatrick if (m_shell) {
245061da546Spatrick std::string shell_executable = m_shell.GetPath();
246061da546Spatrick
247061da546Spatrick const char **argv = GetArguments().GetConstArgumentVector();
248061da546Spatrick if (argv == nullptr || argv[0] == nullptr)
249061da546Spatrick return false;
250061da546Spatrick Args shell_arguments;
251061da546Spatrick shell_arguments.AppendArgument(shell_executable);
252061da546Spatrick const llvm::Triple &triple = GetArchitecture().GetTriple();
253061da546Spatrick if (triple.getOS() == llvm::Triple::Win32 &&
254061da546Spatrick !triple.isWindowsCygwinEnvironment())
255061da546Spatrick shell_arguments.AppendArgument(llvm::StringRef("/C"));
256061da546Spatrick else
257061da546Spatrick shell_arguments.AppendArgument(llvm::StringRef("-c"));
258061da546Spatrick
259061da546Spatrick StreamString shell_command;
260061da546Spatrick if (will_debug) {
261061da546Spatrick // Add a modified PATH environment variable in case argv[0] is a
262061da546Spatrick // relative path.
263061da546Spatrick const char *argv0 = argv[0];
264061da546Spatrick FileSpec arg_spec(argv0);
265061da546Spatrick if (arg_spec.IsRelative()) {
266061da546Spatrick // We have a relative path to our executable which may not work if we
267061da546Spatrick // just try to run "a.out" (without it being converted to "./a.out")
268061da546Spatrick FileSpec working_dir = GetWorkingDirectory();
269061da546Spatrick // Be sure to put quotes around PATH's value in case any paths have
270061da546Spatrick // spaces...
271061da546Spatrick std::string new_path("PATH=\"");
272061da546Spatrick const size_t empty_path_len = new_path.size();
273061da546Spatrick
274061da546Spatrick if (working_dir) {
275061da546Spatrick new_path += working_dir.GetPath();
276061da546Spatrick } else {
277061da546Spatrick llvm::SmallString<64> cwd;
278061da546Spatrick if (! llvm::sys::fs::current_path(cwd))
279061da546Spatrick new_path += cwd;
280061da546Spatrick }
281061da546Spatrick std::string curr_path;
282061da546Spatrick if (HostInfo::GetEnvironmentVar("PATH", curr_path)) {
283061da546Spatrick if (new_path.size() > empty_path_len)
284061da546Spatrick new_path += ':';
285061da546Spatrick new_path += curr_path;
286061da546Spatrick }
287061da546Spatrick new_path += "\" ";
288061da546Spatrick shell_command.PutCString(new_path);
289061da546Spatrick }
290061da546Spatrick
291061da546Spatrick if (triple.getOS() != llvm::Triple::Win32 ||
292061da546Spatrick triple.isWindowsCygwinEnvironment())
293061da546Spatrick shell_command.PutCString("exec");
294061da546Spatrick
295061da546Spatrick // Only Apple supports /usr/bin/arch being able to specify the
296061da546Spatrick // architecture
297061da546Spatrick if (GetArchitecture().IsValid() && // Valid architecture
298061da546Spatrick GetArchitecture().GetTriple().getVendor() ==
299061da546Spatrick llvm::Triple::Apple && // Apple only
300061da546Spatrick GetArchitecture().GetCore() !=
301061da546Spatrick ArchSpec::eCore_x86_64_x86_64h) // Don't do this for x86_64h
302061da546Spatrick {
303061da546Spatrick shell_command.Printf(" /usr/bin/arch -arch %s",
304061da546Spatrick GetArchitecture().GetArchitectureName());
305061da546Spatrick // Set the resume count to 2:
306061da546Spatrick // 1 - stop in shell
307061da546Spatrick // 2 - stop in /usr/bin/arch
308061da546Spatrick // 3 - then we will stop in our program
309061da546Spatrick SetResumeCount(num_resumes + 1);
310061da546Spatrick } else {
311061da546Spatrick // Set the resume count to 1:
312061da546Spatrick // 1 - stop in shell
313061da546Spatrick // 2 - then we will stop in our program
314061da546Spatrick SetResumeCount(num_resumes);
315061da546Spatrick }
316061da546Spatrick }
317061da546Spatrick
318061da546Spatrick if (first_arg_is_full_shell_command) {
319061da546Spatrick // There should only be one argument that is the shell command itself
320061da546Spatrick // to be used as is
321061da546Spatrick if (argv[0] && !argv[1])
322061da546Spatrick shell_command.Printf("%s", argv[0]);
323061da546Spatrick else
324061da546Spatrick return false;
325061da546Spatrick } else {
326061da546Spatrick for (size_t i = 0; argv[i] != nullptr; ++i) {
327be691f3bSpatrick std::string safe_arg = Args::GetShellSafeArgument(m_shell, argv[i]);
328be691f3bSpatrick // Add a space to separate this arg from the previous one.
329be691f3bSpatrick shell_command.PutCString(" ");
330be691f3bSpatrick shell_command.PutCString(safe_arg);
331061da546Spatrick }
332061da546Spatrick }
333061da546Spatrick shell_arguments.AppendArgument(shell_command.GetString());
334061da546Spatrick m_executable = m_shell;
335061da546Spatrick m_arguments = shell_arguments;
336061da546Spatrick return true;
337061da546Spatrick } else {
338061da546Spatrick error.SetErrorString("invalid shell path");
339061da546Spatrick }
340061da546Spatrick } else {
341061da546Spatrick error.SetErrorString("not launching in shell");
342061da546Spatrick }
343061da546Spatrick return false;
344061da546Spatrick }
345