1 //===-- ProcessLaunchInfo.cpp -----------------------------------*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
14 // Other libraries and framework includes
16 #include "lldb/Core/Debugger.h"
17 #include "lldb/Core/Log.h"
18 #include "lldb/Host/Config.h"
19 #include "lldb/Host/FileSystem.h"
20 #include "lldb/Host/HostInfo.h"
21 #include "lldb/Target/FileAction.h"
22 #include "lldb/Target/ProcessLaunchInfo.h"
23 #include "lldb/Target/Target.h"
25 #include "llvm/Support/ConvertUTF.h"
32 using namespace lldb_private;
34 //----------------------------------------------------------------------------
35 // ProcessLaunchInfo member functions
36 //----------------------------------------------------------------------------
38 ProcessLaunchInfo::ProcessLaunchInfo()
39 : ProcessInfo(), m_working_dir(), m_plugin_name(), m_flags(0),
40 m_file_actions(), m_pty(new lldb_utility::PseudoTerminal),
41 m_resume_count(0), m_monitor_callback(nullptr),
42 m_monitor_callback_baton(nullptr), m_monitor_signals(false),
43 m_listener_sp(), m_hijack_listener_sp() {}
45 ProcessLaunchInfo::ProcessLaunchInfo(const FileSpec &stdin_file_spec,
46 const FileSpec &stdout_file_spec,
47 const FileSpec &stderr_file_spec,
48 const FileSpec &working_directory,
49 uint32_t launch_flags)
50 : ProcessInfo(), m_working_dir(), m_plugin_name(), m_flags(launch_flags),
51 m_file_actions(), m_pty(new lldb_utility::PseudoTerminal),
52 m_resume_count(0), m_monitor_callback(nullptr),
53 m_monitor_callback_baton(nullptr), m_monitor_signals(false),
54 m_listener_sp(), m_hijack_listener_sp() {
55 if (stdin_file_spec) {
56 FileAction file_action;
57 const bool read = true;
58 const bool write = false;
59 if (file_action.Open(STDIN_FILENO, stdin_file_spec, read, write))
60 AppendFileAction(file_action);
62 if (stdout_file_spec) {
63 FileAction file_action;
64 const bool read = false;
65 const bool write = true;
66 if (file_action.Open(STDOUT_FILENO, stdout_file_spec, read, write))
67 AppendFileAction(file_action);
69 if (stderr_file_spec) {
70 FileAction file_action;
71 const bool read = false;
72 const bool write = true;
73 if (file_action.Open(STDERR_FILENO, stderr_file_spec, read, write))
74 AppendFileAction(file_action);
76 if (working_directory)
77 SetWorkingDirectory(working_directory);
80 bool ProcessLaunchInfo::AppendCloseFileAction(int fd) {
81 FileAction file_action;
82 if (file_action.Close(fd)) {
83 AppendFileAction(file_action);
89 bool ProcessLaunchInfo::AppendDuplicateFileAction(int fd, int dup_fd) {
90 FileAction file_action;
91 if (file_action.Duplicate(fd, dup_fd)) {
92 AppendFileAction(file_action);
98 bool ProcessLaunchInfo::AppendOpenFileAction(int fd, const FileSpec &file_spec,
99 bool read, bool write) {
100 FileAction file_action;
101 if (file_action.Open(fd, file_spec, read, write)) {
102 AppendFileAction(file_action);
108 bool ProcessLaunchInfo::AppendSuppressFileAction(int fd, bool read,
110 FileAction file_action;
111 if (file_action.Open(fd, FileSpec{FileSystem::DEV_NULL, false}, read,
113 AppendFileAction(file_action);
119 const FileAction *ProcessLaunchInfo::GetFileActionAtIndex(size_t idx) const {
120 if (idx < m_file_actions.size())
121 return &m_file_actions[idx];
125 const FileAction *ProcessLaunchInfo::GetFileActionForFD(int fd) const {
126 for (size_t idx = 0, count = m_file_actions.size(); idx < count; ++idx) {
127 if (m_file_actions[idx].GetFD() == fd)
128 return &m_file_actions[idx];
133 const FileSpec &ProcessLaunchInfo::GetWorkingDirectory() const {
134 return m_working_dir;
137 void ProcessLaunchInfo::SetWorkingDirectory(const FileSpec &working_dir) {
138 m_working_dir = working_dir;
141 const char *ProcessLaunchInfo::GetProcessPluginName() const {
142 return (m_plugin_name.empty() ? nullptr : m_plugin_name.c_str());
145 void ProcessLaunchInfo::SetProcessPluginName(llvm::StringRef plugin) {
146 m_plugin_name = plugin;
149 const FileSpec &ProcessLaunchInfo::GetShell() const { return m_shell; }
151 void ProcessLaunchInfo::SetShell(const FileSpec &shell) {
154 m_shell.ResolveExecutableLocation();
155 m_flags.Set(lldb::eLaunchFlagLaunchInShell);
157 m_flags.Clear(lldb::eLaunchFlagLaunchInShell);
160 void ProcessLaunchInfo::SetLaunchInSeparateProcessGroup(bool separate) {
162 m_flags.Set(lldb::eLaunchFlagLaunchInSeparateProcessGroup);
164 m_flags.Clear(lldb::eLaunchFlagLaunchInSeparateProcessGroup);
167 void ProcessLaunchInfo::SetShellExpandArguments(bool expand) {
169 m_flags.Set(lldb::eLaunchFlagShellExpandArguments);
171 m_flags.Clear(lldb::eLaunchFlagShellExpandArguments);
174 void ProcessLaunchInfo::Clear() {
175 ProcessInfo::Clear();
176 m_working_dir.Clear();
177 m_plugin_name.clear();
180 m_file_actions.clear();
182 m_listener_sp.reset();
183 m_hijack_listener_sp.reset();
186 void ProcessLaunchInfo::SetMonitorProcessCallback(
187 const Host::MonitorChildProcessCallback &callback, bool monitor_signals) {
188 m_monitor_callback = callback;
189 m_monitor_signals = monitor_signals;
192 bool ProcessLaunchInfo::MonitorProcess() const {
193 if (m_monitor_callback && ProcessIDIsValid()) {
194 Host::StartMonitoringChildProcess(m_monitor_callback, GetProcessID(),
201 void ProcessLaunchInfo::SetDetachOnError(bool enable) {
203 m_flags.Set(lldb::eLaunchFlagDetachOnError);
205 m_flags.Clear(lldb::eLaunchFlagDetachOnError);
208 void ProcessLaunchInfo::FinalizeFileActions(Target *target,
209 bool default_to_use_pty) {
210 Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
212 // If nothing for stdin or stdout or stderr was specified, then check the
213 // process for any default
214 // settings that were set with "settings set"
215 if (GetFileActionForFD(STDIN_FILENO) == nullptr ||
216 GetFileActionForFD(STDOUT_FILENO) == nullptr ||
217 GetFileActionForFD(STDERR_FILENO) == nullptr) {
219 log->Printf("ProcessLaunchInfo::%s at least one of stdin/stdout/stderr "
220 "was not set, evaluating default handling",
223 if (m_flags.Test(eLaunchFlagLaunchInTTY)) {
224 // Do nothing, if we are launching in a remote terminal
225 // no file actions should be done at all.
229 if (m_flags.Test(eLaunchFlagDisableSTDIO)) {
231 log->Printf("ProcessLaunchInfo::%s eLaunchFlagDisableSTDIO set, adding "
232 "suppression action for stdin, stdout and stderr",
234 AppendSuppressFileAction(STDIN_FILENO, true, false);
235 AppendSuppressFileAction(STDOUT_FILENO, false, true);
236 AppendSuppressFileAction(STDERR_FILENO, false, true);
238 // Check for any values that might have gotten set with any of:
239 // (lldb) settings set target.input-path
240 // (lldb) settings set target.output-path
241 // (lldb) settings set target.error-path
242 FileSpec in_file_spec;
243 FileSpec out_file_spec;
244 FileSpec err_file_spec;
246 // Only override with the target settings if we don't already have
247 // an action for in, out or error
248 if (GetFileActionForFD(STDIN_FILENO) == nullptr)
249 in_file_spec = target->GetStandardInputPath();
250 if (GetFileActionForFD(STDOUT_FILENO) == nullptr)
251 out_file_spec = target->GetStandardOutputPath();
252 if (GetFileActionForFD(STDERR_FILENO) == nullptr)
253 err_file_spec = target->GetStandardErrorPath();
257 log->Printf("ProcessLaunchInfo::%s target stdin='%s', target "
258 "stdout='%s', stderr='%s'",
260 in_file_spec ? in_file_spec.GetCString() : "<null>",
261 out_file_spec ? out_file_spec.GetCString() : "<null>",
262 err_file_spec ? err_file_spec.GetCString() : "<null>");
265 AppendOpenFileAction(STDIN_FILENO, in_file_spec, true, false);
268 "ProcessLaunchInfo::%s appended stdin open file action for %s",
269 __FUNCTION__, in_file_spec.GetCString());
273 AppendOpenFileAction(STDOUT_FILENO, out_file_spec, false, true);
276 "ProcessLaunchInfo::%s appended stdout open file action for %s",
277 __FUNCTION__, out_file_spec.GetCString());
281 AppendOpenFileAction(STDERR_FILENO, err_file_spec, false, true);
284 "ProcessLaunchInfo::%s appended stderr open file action for %s",
285 __FUNCTION__, err_file_spec.GetCString());
288 if (default_to_use_pty &&
289 (!in_file_spec || !out_file_spec || !err_file_spec)) {
291 log->Printf("ProcessLaunchInfo::%s default_to_use_pty is set, and at "
292 "least one stdin/stderr/stdout is unset, so generating a "
296 int open_flags = O_RDWR | O_NOCTTY;
298 // We really shouldn't be specifying platform specific flags
299 // that are intended for a system call in generic code. But
300 // this will have to do for now.
301 open_flags |= O_CLOEXEC;
303 if (m_pty->OpenFirstAvailableMaster(open_flags, nullptr, 0)) {
304 const FileSpec slave_file_spec{m_pty->GetSlaveName(nullptr, 0),
307 // Only use the slave tty if we don't have anything specified for
308 // input and don't have an action for stdin
309 if (!in_file_spec && GetFileActionForFD(STDIN_FILENO) == nullptr) {
310 AppendOpenFileAction(STDIN_FILENO, slave_file_spec, true, false);
313 // Only use the slave tty if we don't have anything specified for
314 // output and don't have an action for stdout
315 if (!out_file_spec && GetFileActionForFD(STDOUT_FILENO) == nullptr) {
316 AppendOpenFileAction(STDOUT_FILENO, slave_file_spec, false, true);
319 // Only use the slave tty if we don't have anything specified for
320 // error and don't have an action for stderr
321 if (!err_file_spec && GetFileActionForFD(STDERR_FILENO) == nullptr) {
322 AppendOpenFileAction(STDERR_FILENO, slave_file_spec, false, true);
330 bool ProcessLaunchInfo::ConvertArgumentsForLaunchingInShell(
331 Error &error, bool localhost, bool will_debug,
332 bool first_arg_is_full_shell_command, int32_t num_resumes) {
335 if (GetFlags().Test(eLaunchFlagLaunchInShell)) {
337 std::string shell_executable = m_shell.GetPath();
339 const char **argv = GetArguments().GetConstArgumentVector();
340 if (argv == nullptr || argv[0] == nullptr)
342 Args shell_arguments;
343 std::string safe_arg;
344 shell_arguments.AppendArgument(shell_executable);
345 const llvm::Triple &triple = GetArchitecture().GetTriple();
346 if (triple.getOS() == llvm::Triple::Win32 &&
347 !triple.isWindowsCygwinEnvironment())
348 shell_arguments.AppendArgument(llvm::StringRef("/C"));
350 shell_arguments.AppendArgument(llvm::StringRef("-c"));
352 StreamString shell_command;
354 // Add a modified PATH environment variable in case argv[0]
355 // is a relative path.
356 const char *argv0 = argv[0];
357 FileSpec arg_spec(argv0, false);
358 if (arg_spec.IsRelative()) {
359 // We have a relative path to our executable which may not work if
360 // we just try to run "a.out" (without it being converted to
362 FileSpec working_dir = GetWorkingDirectory();
363 // Be sure to put quotes around PATH's value in case any paths have
365 std::string new_path("PATH=\"");
366 const size_t empty_path_len = new_path.size();
369 new_path += working_dir.GetPath();
371 char current_working_dir[PATH_MAX];
373 getcwd(current_working_dir, sizeof(current_working_dir));
377 std::string curr_path;
378 if (HostInfo::GetEnvironmentVar("PATH", curr_path)) {
379 if (new_path.size() > empty_path_len)
381 new_path += curr_path;
384 shell_command.PutCString(new_path);
387 if (triple.getOS() != llvm::Triple::Win32 ||
388 triple.isWindowsCygwinEnvironment())
389 shell_command.PutCString("exec");
391 // Only Apple supports /usr/bin/arch being able to specify the
393 if (GetArchitecture().IsValid() && // Valid architecture
394 GetArchitecture().GetTriple().getVendor() ==
395 llvm::Triple::Apple && // Apple only
396 GetArchitecture().GetCore() !=
397 ArchSpec::eCore_x86_64_x86_64h) // Don't do this for x86_64h
399 shell_command.Printf(" /usr/bin/arch -arch %s",
400 GetArchitecture().GetArchitectureName());
401 // Set the resume count to 2:
403 // 2 - stop in /usr/bin/arch
404 // 3 - then we will stop in our program
405 SetResumeCount(num_resumes + 1);
407 // Set the resume count to 1:
409 // 2 - then we will stop in our program
410 SetResumeCount(num_resumes);
414 if (first_arg_is_full_shell_command) {
415 // There should only be one argument that is the shell command itself to
417 if (argv[0] && !argv[1])
418 shell_command.Printf("%s", argv[0]);
422 for (size_t i = 0; argv[i] != nullptr; ++i) {
424 Args::GetShellSafeArgument(m_shell, argv[i], safe_arg);
425 shell_command.Printf(" %s", arg);
428 shell_arguments.AppendArgument(shell_command.GetString());
429 m_executable = m_shell;
430 m_arguments = shell_arguments;
433 error.SetErrorString("invalid shell path");
436 error.SetErrorString("not launching in shell");
441 ListenerSP ProcessLaunchInfo::GetListenerForProcess(Debugger &debugger) {
443 return m_listener_sp;
445 return debugger.GetListener();