1 //===-- ProcessLaunchInfo.cpp -----------------------------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 // C Includes 11 // C++ Includes 12 #include <climits> 13 14 // Other libraries and framework includes 15 // Project includes 16 #include "lldb/Core/Debugger.h" 17 #include "lldb/Host/Config.h" 18 #include "lldb/Host/FileSystem.h" 19 #include "lldb/Host/HostInfo.h" 20 #include "lldb/Target/FileAction.h" 21 #include "lldb/Target/ProcessLaunchInfo.h" 22 #include "lldb/Target/Target.h" 23 #include "lldb/Utility/Log.h" 24 #include "lldb/Utility/StreamString.h" 25 26 #include "llvm/Support/ConvertUTF.h" 27 #include "llvm/Support/FileSystem.h" 28 29 #if !defined(_WIN32) 30 #include <limits.h> 31 #endif 32 33 using namespace lldb; 34 using namespace lldb_private; 35 36 //---------------------------------------------------------------------------- 37 // ProcessLaunchInfo member functions 38 //---------------------------------------------------------------------------- 39 40 ProcessLaunchInfo::ProcessLaunchInfo() 41 : ProcessInfo(), m_working_dir(), m_plugin_name(), m_flags(0), 42 m_file_actions(), m_pty(new lldb_utility::PseudoTerminal), 43 m_resume_count(0), m_monitor_callback(nullptr), 44 m_monitor_callback_baton(nullptr), m_monitor_signals(false), 45 m_listener_sp(), m_hijack_listener_sp() {} 46 47 ProcessLaunchInfo::ProcessLaunchInfo(const FileSpec &stdin_file_spec, 48 const FileSpec &stdout_file_spec, 49 const FileSpec &stderr_file_spec, 50 const FileSpec &working_directory, 51 uint32_t launch_flags) 52 : ProcessInfo(), m_working_dir(), m_plugin_name(), m_flags(launch_flags), 53 m_file_actions(), m_pty(new lldb_utility::PseudoTerminal), 54 m_resume_count(0), m_monitor_callback(nullptr), 55 m_monitor_callback_baton(nullptr), m_monitor_signals(false), 56 m_listener_sp(), m_hijack_listener_sp() { 57 if (stdin_file_spec) { 58 FileAction file_action; 59 const bool read = true; 60 const bool write = false; 61 if (file_action.Open(STDIN_FILENO, stdin_file_spec, read, write)) 62 AppendFileAction(file_action); 63 } 64 if (stdout_file_spec) { 65 FileAction file_action; 66 const bool read = false; 67 const bool write = true; 68 if (file_action.Open(STDOUT_FILENO, stdout_file_spec, read, write)) 69 AppendFileAction(file_action); 70 } 71 if (stderr_file_spec) { 72 FileAction file_action; 73 const bool read = false; 74 const bool write = true; 75 if (file_action.Open(STDERR_FILENO, stderr_file_spec, read, write)) 76 AppendFileAction(file_action); 77 } 78 if (working_directory) 79 SetWorkingDirectory(working_directory); 80 } 81 82 bool ProcessLaunchInfo::AppendCloseFileAction(int fd) { 83 FileAction file_action; 84 if (file_action.Close(fd)) { 85 AppendFileAction(file_action); 86 return true; 87 } 88 return false; 89 } 90 91 bool ProcessLaunchInfo::AppendDuplicateFileAction(int fd, int dup_fd) { 92 FileAction file_action; 93 if (file_action.Duplicate(fd, dup_fd)) { 94 AppendFileAction(file_action); 95 return true; 96 } 97 return false; 98 } 99 100 bool ProcessLaunchInfo::AppendOpenFileAction(int fd, const FileSpec &file_spec, 101 bool read, bool write) { 102 FileAction file_action; 103 if (file_action.Open(fd, file_spec, read, write)) { 104 AppendFileAction(file_action); 105 return true; 106 } 107 return false; 108 } 109 110 bool ProcessLaunchInfo::AppendSuppressFileAction(int fd, bool read, 111 bool write) { 112 FileAction file_action; 113 if (file_action.Open(fd, FileSpec{FileSystem::DEV_NULL, false}, read, 114 write)) { 115 AppendFileAction(file_action); 116 return true; 117 } 118 return false; 119 } 120 121 const FileAction *ProcessLaunchInfo::GetFileActionAtIndex(size_t idx) const { 122 if (idx < m_file_actions.size()) 123 return &m_file_actions[idx]; 124 return nullptr; 125 } 126 127 const FileAction *ProcessLaunchInfo::GetFileActionForFD(int fd) const { 128 for (size_t idx = 0, count = m_file_actions.size(); idx < count; ++idx) { 129 if (m_file_actions[idx].GetFD() == fd) 130 return &m_file_actions[idx]; 131 } 132 return nullptr; 133 } 134 135 const FileSpec &ProcessLaunchInfo::GetWorkingDirectory() const { 136 return m_working_dir; 137 } 138 139 void ProcessLaunchInfo::SetWorkingDirectory(const FileSpec &working_dir) { 140 m_working_dir = working_dir; 141 } 142 143 const char *ProcessLaunchInfo::GetProcessPluginName() const { 144 return (m_plugin_name.empty() ? nullptr : m_plugin_name.c_str()); 145 } 146 147 void ProcessLaunchInfo::SetProcessPluginName(llvm::StringRef plugin) { 148 m_plugin_name = plugin; 149 } 150 151 const FileSpec &ProcessLaunchInfo::GetShell() const { return m_shell; } 152 153 void ProcessLaunchInfo::SetShell(const FileSpec &shell) { 154 m_shell = shell; 155 if (m_shell) { 156 m_shell.ResolveExecutableLocation(); 157 m_flags.Set(lldb::eLaunchFlagLaunchInShell); 158 } else 159 m_flags.Clear(lldb::eLaunchFlagLaunchInShell); 160 } 161 162 void ProcessLaunchInfo::SetLaunchInSeparateProcessGroup(bool separate) { 163 if (separate) 164 m_flags.Set(lldb::eLaunchFlagLaunchInSeparateProcessGroup); 165 else 166 m_flags.Clear(lldb::eLaunchFlagLaunchInSeparateProcessGroup); 167 } 168 169 void ProcessLaunchInfo::SetShellExpandArguments(bool expand) { 170 if (expand) 171 m_flags.Set(lldb::eLaunchFlagShellExpandArguments); 172 else 173 m_flags.Clear(lldb::eLaunchFlagShellExpandArguments); 174 } 175 176 void ProcessLaunchInfo::Clear() { 177 ProcessInfo::Clear(); 178 m_working_dir.Clear(); 179 m_plugin_name.clear(); 180 m_shell.Clear(); 181 m_flags.Clear(); 182 m_file_actions.clear(); 183 m_resume_count = 0; 184 m_listener_sp.reset(); 185 m_hijack_listener_sp.reset(); 186 } 187 188 void ProcessLaunchInfo::SetMonitorProcessCallback( 189 const Host::MonitorChildProcessCallback &callback, bool monitor_signals) { 190 m_monitor_callback = callback; 191 m_monitor_signals = monitor_signals; 192 } 193 194 bool ProcessLaunchInfo::MonitorProcess() const { 195 if (m_monitor_callback && ProcessIDIsValid()) { 196 Host::StartMonitoringChildProcess(m_monitor_callback, GetProcessID(), 197 m_monitor_signals); 198 return true; 199 } 200 return false; 201 } 202 203 void ProcessLaunchInfo::SetDetachOnError(bool enable) { 204 if (enable) 205 m_flags.Set(lldb::eLaunchFlagDetachOnError); 206 else 207 m_flags.Clear(lldb::eLaunchFlagDetachOnError); 208 } 209 210 void ProcessLaunchInfo::FinalizeFileActions(Target *target, 211 bool default_to_use_pty) { 212 Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); 213 214 // If nothing for stdin or stdout or stderr was specified, then check the 215 // process for any default 216 // settings that were set with "settings set" 217 if (GetFileActionForFD(STDIN_FILENO) == nullptr || 218 GetFileActionForFD(STDOUT_FILENO) == nullptr || 219 GetFileActionForFD(STDERR_FILENO) == nullptr) { 220 if (log) 221 log->Printf("ProcessLaunchInfo::%s at least one of stdin/stdout/stderr " 222 "was not set, evaluating default handling", 223 __FUNCTION__); 224 225 if (m_flags.Test(eLaunchFlagLaunchInTTY)) { 226 // Do nothing, if we are launching in a remote terminal 227 // no file actions should be done at all. 228 return; 229 } 230 231 if (m_flags.Test(eLaunchFlagDisableSTDIO)) { 232 if (log) 233 log->Printf("ProcessLaunchInfo::%s eLaunchFlagDisableSTDIO set, adding " 234 "suppression action for stdin, stdout and stderr", 235 __FUNCTION__); 236 AppendSuppressFileAction(STDIN_FILENO, true, false); 237 AppendSuppressFileAction(STDOUT_FILENO, false, true); 238 AppendSuppressFileAction(STDERR_FILENO, false, true); 239 } else { 240 // Check for any values that might have gotten set with any of: 241 // (lldb) settings set target.input-path 242 // (lldb) settings set target.output-path 243 // (lldb) settings set target.error-path 244 FileSpec in_file_spec; 245 FileSpec out_file_spec; 246 FileSpec err_file_spec; 247 if (target) { 248 // Only override with the target settings if we don't already have 249 // an action for in, out or error 250 if (GetFileActionForFD(STDIN_FILENO) == nullptr) 251 in_file_spec = target->GetStandardInputPath(); 252 if (GetFileActionForFD(STDOUT_FILENO) == nullptr) 253 out_file_spec = target->GetStandardOutputPath(); 254 if (GetFileActionForFD(STDERR_FILENO) == nullptr) 255 err_file_spec = target->GetStandardErrorPath(); 256 } 257 258 if (log) 259 log->Printf("ProcessLaunchInfo::%s target stdin='%s', target " 260 "stdout='%s', stderr='%s'", 261 __FUNCTION__, 262 in_file_spec ? in_file_spec.GetCString() : "<null>", 263 out_file_spec ? out_file_spec.GetCString() : "<null>", 264 err_file_spec ? err_file_spec.GetCString() : "<null>"); 265 266 if (in_file_spec) { 267 AppendOpenFileAction(STDIN_FILENO, in_file_spec, true, false); 268 if (log) 269 log->Printf( 270 "ProcessLaunchInfo::%s appended stdin open file action for %s", 271 __FUNCTION__, in_file_spec.GetCString()); 272 } 273 274 if (out_file_spec) { 275 AppendOpenFileAction(STDOUT_FILENO, out_file_spec, false, true); 276 if (log) 277 log->Printf( 278 "ProcessLaunchInfo::%s appended stdout open file action for %s", 279 __FUNCTION__, out_file_spec.GetCString()); 280 } 281 282 if (err_file_spec) { 283 AppendOpenFileAction(STDERR_FILENO, err_file_spec, false, true); 284 if (log) 285 log->Printf( 286 "ProcessLaunchInfo::%s appended stderr open file action for %s", 287 __FUNCTION__, err_file_spec.GetCString()); 288 } 289 290 if (default_to_use_pty && 291 (!in_file_spec || !out_file_spec || !err_file_spec)) { 292 if (log) 293 log->Printf("ProcessLaunchInfo::%s default_to_use_pty is set, and at " 294 "least one stdin/stderr/stdout is unset, so generating a " 295 "pty to use for it", 296 __FUNCTION__); 297 298 int open_flags = O_RDWR | O_NOCTTY; 299 #if !defined(_WIN32) 300 // We really shouldn't be specifying platform specific flags 301 // that are intended for a system call in generic code. But 302 // this will have to do for now. 303 open_flags |= O_CLOEXEC; 304 #endif 305 if (m_pty->OpenFirstAvailableMaster(open_flags, nullptr, 0)) { 306 const FileSpec slave_file_spec{m_pty->GetSlaveName(nullptr, 0), 307 false}; 308 309 // Only use the slave tty if we don't have anything specified for 310 // input and don't have an action for stdin 311 if (!in_file_spec && GetFileActionForFD(STDIN_FILENO) == nullptr) { 312 AppendOpenFileAction(STDIN_FILENO, slave_file_spec, true, false); 313 } 314 315 // Only use the slave tty if we don't have anything specified for 316 // output and don't have an action for stdout 317 if (!out_file_spec && GetFileActionForFD(STDOUT_FILENO) == nullptr) { 318 AppendOpenFileAction(STDOUT_FILENO, slave_file_spec, false, true); 319 } 320 321 // Only use the slave tty if we don't have anything specified for 322 // error and don't have an action for stderr 323 if (!err_file_spec && GetFileActionForFD(STDERR_FILENO) == nullptr) { 324 AppendOpenFileAction(STDERR_FILENO, slave_file_spec, false, true); 325 } 326 } 327 } 328 } 329 } 330 } 331 332 bool ProcessLaunchInfo::ConvertArgumentsForLaunchingInShell( 333 Status &error, bool localhost, bool will_debug, 334 bool first_arg_is_full_shell_command, int32_t num_resumes) { 335 error.Clear(); 336 337 if (GetFlags().Test(eLaunchFlagLaunchInShell)) { 338 if (m_shell) { 339 std::string shell_executable = m_shell.GetPath(); 340 341 const char **argv = GetArguments().GetConstArgumentVector(); 342 if (argv == nullptr || argv[0] == nullptr) 343 return false; 344 Args shell_arguments; 345 std::string safe_arg; 346 shell_arguments.AppendArgument(shell_executable); 347 const llvm::Triple &triple = GetArchitecture().GetTriple(); 348 if (triple.getOS() == llvm::Triple::Win32 && 349 !triple.isWindowsCygwinEnvironment()) 350 shell_arguments.AppendArgument(llvm::StringRef("/C")); 351 else 352 shell_arguments.AppendArgument(llvm::StringRef("-c")); 353 354 StreamString shell_command; 355 if (will_debug) { 356 // Add a modified PATH environment variable in case argv[0] 357 // is a relative path. 358 const char *argv0 = argv[0]; 359 FileSpec arg_spec(argv0, false); 360 if (arg_spec.IsRelative()) { 361 // We have a relative path to our executable which may not work if 362 // we just try to run "a.out" (without it being converted to 363 // "./a.out") 364 FileSpec working_dir = GetWorkingDirectory(); 365 // Be sure to put quotes around PATH's value in case any paths have 366 // spaces... 367 std::string new_path("PATH=\""); 368 const size_t empty_path_len = new_path.size(); 369 370 if (working_dir) { 371 new_path += working_dir.GetPath(); 372 } else { 373 llvm::SmallString<64> cwd; 374 if (! llvm::sys::fs::current_path(cwd)) 375 new_path += cwd; 376 } 377 std::string curr_path; 378 if (HostInfo::GetEnvironmentVar("PATH", curr_path)) { 379 if (new_path.size() > empty_path_len) 380 new_path += ':'; 381 new_path += curr_path; 382 } 383 new_path += "\" "; 384 shell_command.PutCString(new_path); 385 } 386 387 if (triple.getOS() != llvm::Triple::Win32 || 388 triple.isWindowsCygwinEnvironment()) 389 shell_command.PutCString("exec"); 390 391 // Only Apple supports /usr/bin/arch being able to specify the 392 // architecture 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 398 { 399 shell_command.Printf(" /usr/bin/arch -arch %s", 400 GetArchitecture().GetArchitectureName()); 401 // Set the resume count to 2: 402 // 1 - stop in shell 403 // 2 - stop in /usr/bin/arch 404 // 3 - then we will stop in our program 405 SetResumeCount(num_resumes + 1); 406 } else { 407 // Set the resume count to 1: 408 // 1 - stop in shell 409 // 2 - then we will stop in our program 410 SetResumeCount(num_resumes); 411 } 412 } 413 414 if (first_arg_is_full_shell_command) { 415 // There should only be one argument that is the shell command itself to 416 // be used as is 417 if (argv[0] && !argv[1]) 418 shell_command.Printf("%s", argv[0]); 419 else 420 return false; 421 } else { 422 for (size_t i = 0; argv[i] != nullptr; ++i) { 423 const char *arg = 424 Args::GetShellSafeArgument(m_shell, argv[i], safe_arg); 425 shell_command.Printf(" %s", arg); 426 } 427 } 428 shell_arguments.AppendArgument(shell_command.GetString()); 429 m_executable = m_shell; 430 m_arguments = shell_arguments; 431 return true; 432 } else { 433 error.SetErrorString("invalid shell path"); 434 } 435 } else { 436 error.SetErrorString("not launching in shell"); 437 } 438 return false; 439 } 440 441 ListenerSP ProcessLaunchInfo::GetListenerForProcess(Debugger &debugger) { 442 if (m_listener_sp) 443 return m_listener_sp; 444 else 445 return debugger.GetListener(); 446 } 447