//===-- ProcessLaunchInfo.cpp -----------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Host/Config.h" #include "lldb/Target/ProcessLaunchInfo.h" #ifndef LLDB_DISABLE_POSIX #include #endif #include "lldb/Target/Target.h" using namespace lldb; using namespace lldb_private; //---------------------------------------------------------------------------- // ProcessLaunchInfo::FileAction member functions //---------------------------------------------------------------------------- ProcessLaunchInfo::FileAction::FileAction () : m_action (eFileActionNone), m_fd (-1), m_arg (-1), m_path () { } void ProcessLaunchInfo::FileAction::Clear() { m_action = eFileActionNone; m_fd = -1; m_arg = -1; m_path.clear(); } const char * ProcessLaunchInfo::FileAction::GetPath () const { if (m_path.empty()) return NULL; return m_path.c_str(); } //---------------------------------------------------------------------------- // ProcessLaunchInfo member functions //---------------------------------------------------------------------------- ProcessLaunchInfo::ProcessLaunchInfo () : ProcessInfo(), m_working_dir (), m_plugin_name (), m_shell (), m_flags (0), m_file_actions (), m_pty (), m_resume_count (0), m_monitor_callback (NULL), m_monitor_callback_baton (NULL), m_monitor_signals (false), m_hijack_listener_sp () { } ProcessLaunchInfo::ProcessLaunchInfo ( const char *stdin_path, const char *stdout_path, const char *stderr_path, const char *working_directory, uint32_t launch_flags) : ProcessInfo(), m_working_dir (), m_plugin_name (), m_shell (), m_flags (launch_flags), m_file_actions (), m_pty (), m_resume_count (0), m_monitor_callback (NULL), m_monitor_callback_baton (NULL), m_monitor_signals (false), m_hijack_listener_sp () { if (stdin_path) { ProcessLaunchInfo::FileAction file_action; const bool read = true; const bool write = false; if (file_action.Open(STDIN_FILENO, stdin_path, read, write)) AppendFileAction (file_action); } if (stdout_path) { ProcessLaunchInfo::FileAction file_action; const bool read = false; const bool write = true; if (file_action.Open(STDOUT_FILENO, stdout_path, read, write)) AppendFileAction (file_action); } if (stderr_path) { ProcessLaunchInfo::FileAction file_action; const bool read = false; const bool write = true; if (file_action.Open(STDERR_FILENO, stderr_path, read, write)) AppendFileAction (file_action); } if (working_directory) SetWorkingDirectory(working_directory); } bool ProcessLaunchInfo::AppendCloseFileAction (int fd) { FileAction file_action; if (file_action.Close (fd)) { AppendFileAction (file_action); return true; } return false; } bool ProcessLaunchInfo::AppendDuplicateFileAction (int fd, int dup_fd) { FileAction file_action; if (file_action.Duplicate (fd, dup_fd)) { AppendFileAction (file_action); return true; } return false; } bool ProcessLaunchInfo::AppendOpenFileAction (int fd, const char *path, bool read, bool write) { FileAction file_action; if (file_action.Open (fd, path, read, write)) { AppendFileAction (file_action); return true; } return false; } bool ProcessLaunchInfo::AppendSuppressFileAction (int fd, bool read, bool write) { FileAction file_action; if (file_action.Open (fd, "/dev/null", read, write)) { AppendFileAction (file_action); return true; } return false; } const ProcessLaunchInfo::FileAction * ProcessLaunchInfo::GetFileActionAtIndex (size_t idx) const { if (idx < m_file_actions.size()) return &m_file_actions[idx]; return NULL; } const ProcessLaunchInfo::FileAction * ProcessLaunchInfo::GetFileActionForFD (int fd) const { for (size_t idx=0, count=m_file_actions.size(); idx < count; ++idx) { if (m_file_actions[idx].GetFD () == fd) return &m_file_actions[idx]; } return NULL; } const char * ProcessLaunchInfo::GetWorkingDirectory () const { if (m_working_dir.empty()) return NULL; return m_working_dir.c_str(); } void ProcessLaunchInfo::SetWorkingDirectory (const char *working_dir) { if (working_dir && working_dir[0]) m_working_dir.assign (working_dir); else m_working_dir.clear(); } const char * ProcessLaunchInfo::GetProcessPluginName () const { if (m_plugin_name.empty()) return NULL; return m_plugin_name.c_str(); } void ProcessLaunchInfo::SetProcessPluginName (const char *plugin) { if (plugin && plugin[0]) m_plugin_name.assign (plugin); else m_plugin_name.clear(); } const char * ProcessLaunchInfo::GetShell () const { if (m_shell.empty()) return NULL; return m_shell.c_str(); } void ProcessLaunchInfo::SetShell (const char * path) { if (path && path[0]) { m_shell.assign (path); m_flags.Set (lldb::eLaunchFlagLaunchInShell); } else { m_shell.clear(); m_flags.Clear (lldb::eLaunchFlagLaunchInShell); } } void ProcessLaunchInfo::SetLaunchInSeparateProcessGroup (bool separate) { if (separate) m_flags.Set(lldb::eLaunchFlagLaunchInSeparateProcessGroup); else m_flags.Clear (lldb::eLaunchFlagLaunchInSeparateProcessGroup); } void ProcessLaunchInfo::Clear () { ProcessInfo::Clear(); m_working_dir.clear(); m_plugin_name.clear(); m_shell.clear(); m_flags.Clear(); m_file_actions.clear(); m_resume_count = 0; m_hijack_listener_sp.reset(); } void ProcessLaunchInfo::SetMonitorProcessCallback (Host::MonitorChildProcessCallback callback, void *baton, bool monitor_signals) { m_monitor_callback = callback; m_monitor_callback_baton = baton; m_monitor_signals = monitor_signals; } bool ProcessLaunchInfo::MonitorProcess () const { if (m_monitor_callback && ProcessIDIsValid()) { Host::StartMonitoringChildProcess (m_monitor_callback, m_monitor_callback_baton, GetProcessID(), m_monitor_signals); return true; } return false; } void ProcessLaunchInfo::SetDetachOnError (bool enable) { if (enable) m_flags.Set(lldb::eLaunchFlagDetachOnError); else m_flags.Clear(lldb::eLaunchFlagDetachOnError); } void ProcessLaunchInfo::FinalizeFileActions (Target *target, bool default_to_use_pty) { // If nothing for stdin or stdout or stderr was specified, then check the process for any default // settings that were set with "settings set" if (GetFileActionForFD(STDIN_FILENO) == NULL || GetFileActionForFD(STDOUT_FILENO) == NULL || GetFileActionForFD(STDERR_FILENO) == NULL) { if (m_flags.Test(eLaunchFlagDisableSTDIO)) { AppendSuppressFileAction (STDIN_FILENO , true, false); AppendSuppressFileAction (STDOUT_FILENO, false, true); AppendSuppressFileAction (STDERR_FILENO, false, true); } else { // Check for any values that might have gotten set with any of: // (lldb) settings set target.input-path // (lldb) settings set target.output-path // (lldb) settings set target.error-path FileSpec in_path; FileSpec out_path; FileSpec err_path; if (target) { in_path = target->GetStandardInputPath(); out_path = target->GetStandardOutputPath(); err_path = target->GetStandardErrorPath(); } char path[PATH_MAX]; if (in_path && in_path.GetPath(path, sizeof(path))) AppendOpenFileAction(STDIN_FILENO, path, true, false); if (out_path && out_path.GetPath(path, sizeof(path))) AppendOpenFileAction(STDOUT_FILENO, path, false, true); if (err_path && err_path.GetPath(path, sizeof(path))) AppendOpenFileAction(STDERR_FILENO, path, false, true); if (default_to_use_pty && (!in_path || !out_path || !err_path)) { if (m_pty.OpenFirstAvailableMaster(O_RDWR| O_NOCTTY, NULL, 0)) { const char *slave_path = m_pty.GetSlaveName(NULL, 0); if (!in_path) { AppendOpenFileAction(STDIN_FILENO, slave_path, true, false); } if (!out_path) { AppendOpenFileAction(STDOUT_FILENO, slave_path, false, true); } if (!err_path) { AppendOpenFileAction(STDERR_FILENO, slave_path, false, true); } } } } } } bool ProcessLaunchInfo::ConvertArgumentsForLaunchingInShell (Error &error, bool localhost, bool will_debug, bool first_arg_is_full_shell_command, int32_t num_resumes) { error.Clear(); if (GetFlags().Test (eLaunchFlagLaunchInShell)) { const char *shell_executable = GetShell(); if (shell_executable) { char shell_resolved_path[PATH_MAX]; if (localhost) { FileSpec shell_filespec (shell_executable, true); if (!shell_filespec.Exists()) { // Resolve the path in case we just got "bash", "sh" or "tcsh" if (!shell_filespec.ResolveExecutableLocation ()) { error.SetErrorStringWithFormat("invalid shell path '%s'", shell_executable); return false; } } shell_filespec.GetPath (shell_resolved_path, sizeof(shell_resolved_path)); shell_executable = shell_resolved_path; } const char **argv = GetArguments().GetConstArgumentVector (); if (argv == NULL || argv[0] == NULL) return false; Args shell_arguments; std::string safe_arg; shell_arguments.AppendArgument (shell_executable); shell_arguments.AppendArgument ("-c"); StreamString shell_command; if (will_debug) { // Add a modified PATH environment variable in case argv[0] // is a relative path const char *argv0 = argv[0]; if (argv0 && (argv0[0] != '/' && argv0[0] != '~')) { // We have a relative path to our executable which may not work if // we just try to run "a.out" (without it being converted to "./a.out") const char *working_dir = GetWorkingDirectory(); // Be sure to put quotes around PATH's value in case any paths have spaces... std::string new_path("PATH=\""); const size_t empty_path_len = new_path.size(); if (working_dir && working_dir[0]) { new_path += working_dir; } else { char current_working_dir[PATH_MAX]; const char *cwd = getcwd(current_working_dir, sizeof(current_working_dir)); if (cwd && cwd[0]) new_path += cwd; } const char *curr_path = getenv("PATH"); if (curr_path) { if (new_path.size() > empty_path_len) new_path += ':'; new_path += curr_path; } new_path += "\" "; shell_command.PutCString(new_path.c_str()); } shell_command.PutCString ("exec"); // Only Apple supports /usr/bin/arch being able to specify the architecture if (GetArchitecture().IsValid()) { shell_command.Printf(" /usr/bin/arch -arch %s", GetArchitecture().GetArchitectureName()); // Set the resume count to 2: // 1 - stop in shell // 2 - stop in /usr/bin/arch // 3 - then we will stop in our program SetResumeCount(num_resumes + 1); } else { // Set the resume count to 1: // 1 - stop in shell // 2 - then we will stop in our program SetResumeCount(num_resumes); } } if (first_arg_is_full_shell_command) { // There should only be one argument that is the shell command itself to be used as is if (argv[0] && !argv[1]) shell_command.Printf("%s", argv[0]); else return false; } else { for (size_t i=0; argv[i] != NULL; ++i) { const char *arg = Args::GetShellSafeArgument (argv[i], safe_arg); shell_command.Printf(" %s", arg); } } shell_arguments.AppendArgument (shell_command.GetString().c_str()); m_executable.SetFile(shell_executable, false); m_arguments = shell_arguments; return true; } else { error.SetErrorString ("invalid shell path"); } } else { error.SetErrorString ("not launching in shell"); } return false; } bool ProcessLaunchInfo::FileAction::Open (int fd, const char *path, bool read, bool write) { if ((read || write) && fd >= 0 && path && path[0]) { m_action = eFileActionOpen; m_fd = fd; if (read && write) m_arg = O_NOCTTY | O_CREAT | O_RDWR; else if (read) m_arg = O_NOCTTY | O_RDONLY; else m_arg = O_NOCTTY | O_CREAT | O_WRONLY; m_path.assign (path); return true; } else { Clear(); } return false; } bool ProcessLaunchInfo::FileAction::Close (int fd) { Clear(); if (fd >= 0) { m_action = eFileActionClose; m_fd = fd; } return m_fd >= 0; } bool ProcessLaunchInfo::FileAction::Duplicate (int fd, int dup_fd) { Clear(); if (fd >= 0 && dup_fd >= 0) { m_action = eFileActionDuplicate; m_fd = fd; m_arg = dup_fd; } return m_fd >= 0; } #ifndef LLDB_DISABLE_POSIX bool ProcessLaunchInfo::FileAction::AddPosixSpawnFileAction (void *_file_actions, const FileAction *info, Log *log, Error& error) { if (info == NULL) return false; posix_spawn_file_actions_t *file_actions = reinterpret_cast(_file_actions); switch (info->m_action) { case eFileActionNone: error.Clear(); break; case eFileActionClose: if (info->m_fd == -1) error.SetErrorString ("invalid fd for posix_spawn_file_actions_addclose(...)"); else { error.SetError (::posix_spawn_file_actions_addclose (file_actions, info->m_fd), eErrorTypePOSIX); if (log && (error.Fail() || log)) error.PutToLog(log, "posix_spawn_file_actions_addclose (action=%p, fd=%i)", static_cast(file_actions), info->m_fd); } break; case eFileActionDuplicate: if (info->m_fd == -1) error.SetErrorString ("invalid fd for posix_spawn_file_actions_adddup2(...)"); else if (info->m_arg == -1) error.SetErrorString ("invalid duplicate fd for posix_spawn_file_actions_adddup2(...)"); else { error.SetError (::posix_spawn_file_actions_adddup2 (file_actions, info->m_fd, info->m_arg), eErrorTypePOSIX); if (log && (error.Fail() || log)) error.PutToLog(log, "posix_spawn_file_actions_adddup2 (action=%p, fd=%i, dup_fd=%i)", static_cast(file_actions), info->m_fd, info->m_arg); } break; case eFileActionOpen: if (info->m_fd == -1) error.SetErrorString ("invalid fd in posix_spawn_file_actions_addopen(...)"); else { int oflag = info->m_arg; mode_t mode = 0; if (oflag & O_CREAT) mode = 0640; error.SetError (::posix_spawn_file_actions_addopen (file_actions, info->m_fd, info->m_path.c_str(), oflag, mode), eErrorTypePOSIX); if (error.Fail() || log) error.PutToLog(log, "posix_spawn_file_actions_addopen (action=%p, fd=%i, path='%s', oflag=%i, mode=%i)", static_cast(file_actions), info->m_fd, info->m_path.c_str(), oflag, mode); } break; } return error.Success(); } #endif