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