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