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