diff --git a/lldb/include/lldb/API/SBTarget.h b/lldb/include/lldb/API/SBTarget.h index bf9d80073519..8532150c9a60 100644 --- a/lldb/include/lldb/API/SBTarget.h +++ b/lldb/include/lldb/API/SBTarget.h @@ -55,16 +55,20 @@ public: GetProcess (); lldb::SBProcess - LaunchProcess (char const **argv, - char const **envp, - const char *tty, - uint32_t launch_flags, // See LaunchFlags - bool stop_at_entry); + Launch (char const **argv, + char const **envp, + const char *tty, + uint32_t launch_flags, // See LaunchFlags + bool stop_at_entry, + lldb::SBError& error); lldb::SBProcess Launch (char const **argv, char const **envp, - const char *tty, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + const char *working_directory, uint32_t launch_flags, // See LaunchFlags bool stop_at_entry, lldb::SBError& error); diff --git a/lldb/include/lldb/Host/Host.h b/lldb/include/lldb/Host/Host.h index 91cfee41b55f..2b8d9abe7568 100644 --- a/lldb/include/lldb/Host/Host.h +++ b/lldb/include/lldb/Host/Host.h @@ -329,8 +329,9 @@ public: static lldb::pid_t LaunchInNewTerminal (const char *tty_name, // Optional partial or full tty name ("/dev/ttys000" or "ttys000") - const char **argv, // argv[0] is executable - const char **envp, + const char **argv, // argv[0] is executable, argv[1] and on are the arguments + const char **envp, + const char *working_dir, const ArchSpec *arch_spec, bool stop_at_entry, bool disable_aslr); diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h index 0076da13076b..adefc33c1dc8 100644 --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -540,6 +540,9 @@ public: /// process. If all stdXX_path arguments are NULL, a pseudo /// terminal will be used. /// + /// @param[in] working_directory + /// The working directory to have the child process run in + /// /// @return /// An error object. Call GetID() to get the process ID if /// the error object is success. @@ -550,7 +553,8 @@ public: uint32_t launch_flags, const char *stdin_path, const char *stdout_path, - const char *stderr_path); + const char *stderr_path, + const char *working_directory); //------------------------------------------------------------------ /// Attach to an existing process using a process ID. @@ -950,6 +954,9 @@ public: /// process. If all stdXX_path arguments are NULL, a pseudo /// terminal will be used. /// + /// @param[in] working_directory + /// The working directory to have the child process run in + /// /// @return /// A new valid process ID, or LLDB_INVALID_PROCESS_ID if /// launching fails. @@ -961,7 +968,8 @@ public: uint32_t launch_flags, const char *stdin_path, const char *stdout_path, - const char *stderr_path) = 0; + const char *stderr_path, + const char *working_directory) = 0; //------------------------------------------------------------------ /// Called after launching a process. diff --git a/lldb/source/API/SBTarget.cpp b/lldb/source/API/SBTarget.cpp index b23262b00051..5247189e3838 100644 --- a/lldb/source/API/SBTarget.cpp +++ b/lldb/source/API/SBTarget.cpp @@ -116,35 +116,6 @@ SBTarget::GetDebugger () const return debugger; } -SBProcess -SBTarget::LaunchProcess -( - char const **argv, - char const **envp, - const char *tty, - uint32_t launch_flags, - bool stop_at_entry -) -{ - LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); - - if (log) - log->Printf ("SBTarget(%p)::LaunchProcess (argv=%p, envp=%p, tty=\"%s\", launch_flags=%d, stop_at_entry=%i)", - m_opaque_sp.get(), argv, envp, tty, launch_flags, stop_at_entry); - - SBError sb_error; - SBProcess sb_process = Launch (argv, envp, tty, launch_flags, stop_at_entry, sb_error); - - log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API); - if (log) - { - log->Printf ("SBTarget(%p)::LaunchProcess (...) => SBProcess(%p)", - m_opaque_sp.get(), sb_process.get()); - } - - return sb_process; -} - SBProcess SBTarget::Launch ( @@ -155,13 +126,39 @@ SBTarget::Launch bool stop_at_entry, SBError &error ) +{ + return Launch (argv, envp, tty, tty, tty, NULL, launch_flags, stop_at_entry, error); +} + +SBProcess +SBTarget::Launch +( + char const **argv, + char const **envp, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + const char *working_directory, + uint32_t launch_flags, // See LaunchFlags + bool stop_at_entry, + lldb::SBError& error +) { LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); if (log) { - log->Printf ("SBTarget(%p)::Launch (argv=%p, envp=%p, tty=\"%s\", launch_flags=%d, stop_at_entry=%i, &error (%p))...", - m_opaque_sp.get(), argv, envp, tty, launch_flags, stop_at_entry, error.get()); + log->Printf ("SBTarget(%p)::Launch (argv=%p, envp=%p, stdin=%s, stdout=%s, stderr=%s, working-dir=%s, launch_flags=0x%x, stop_at_entry=%i, &error (%p))...", + m_opaque_sp.get(), + argv, + envp, + stdin_path ? stdin_path : "NULL", + stdout_path ? stdout_path : "NULL", + stderr_path ? stderr_path : "NULL", + working_directory ? working_directory : "NULL", + launch_flags, + stop_at_entry, + error.get()); } SBProcess sb_process; if (m_opaque_sp) @@ -171,7 +168,7 @@ SBTarget::Launch if (sb_process.IsValid()) { - error.SetError (sb_process->Launch (argv, envp, launch_flags, tty, tty, tty)); + error.SetError (sb_process->Launch (argv, envp, launch_flags, stdin_path, stdout_path, stderr_path, working_directory)); if (error.Success()) { // We we are stopping at the entry point, we can return now! diff --git a/lldb/source/Commands/CommandObjectProcess.cpp b/lldb/source/Commands/CommandObjectProcess.cpp index c07de3ee6aee..5239b7bf2d60 100644 --- a/lldb/source/Commands/CommandObjectProcess.cpp +++ b/lldb/source/Commands/CommandObjectProcess.cpp @@ -57,12 +57,13 @@ public: switch (short_option) { - case 's': stop_at_entry = true; break; - case 'e': stderr_path = option_arg; break; - case 'i': stdin_path = option_arg; break; - case 'o': stdout_path = option_arg; break; - case 'p': plugin_name = option_arg; break; - case 'n': no_stdio = true; break; + case 's': stop_at_entry = true; break; + case 'e': stderr_path.assign (option_arg); break; + case 'i': stdin_path.assign (option_arg); break; + case 'o': stdout_path.assign (option_arg); break; + case 'p': plugin_name.assign (option_arg); break; + case 'n': no_stdio = true; break; + case 'w': working_dir.assign (option_arg); break; case 't': if (option_arg && option_arg[0]) tty_name.assign (option_arg); @@ -87,6 +88,7 @@ public: stdout_path.clear(); stderr_path.clear(); plugin_name.clear(); + working_dir.clear(); no_stdio = false; } @@ -110,6 +112,7 @@ public: std::string stdin_path; std::string stdout_path; std::string plugin_name; + std::string working_dir; }; @@ -249,6 +252,9 @@ public: const char **inferior_envp = environment.GetArgumentCount() ? environment.GetConstArgumentVector() : NULL; Error error; + const char *working_dir = NULL; + if (!m_options.working_dir.empty()) + working_dir = m_options.working_dir.c_str(); if (m_options.in_new_tty) { @@ -256,6 +262,7 @@ public: lldb::pid_t pid = Host::LaunchInNewTerminal (m_options.tty_name.c_str(), inferior_argv, inferior_envp, + working_dir, &exe_module->GetArchitecture(), true, process->GetDisableASLR()); @@ -287,19 +294,13 @@ public: stderr_path = m_options.stderr_path.empty() ? NULL : m_options.stderr_path.c_str(); } - if (stdin_path == NULL) - stdin_path = "/dev/null"; - if (stdout_path == NULL) - stdout_path = "/dev/null"; - if (stderr_path == NULL) - stderr_path = "/dev/null"; - error = process->Launch (inferior_argv, inferior_envp, launch_flags, stdin_path, stdout_path, - stderr_path); + stderr_path, + working_dir); } if (error.Success()) @@ -363,6 +364,7 @@ CommandObjectProcessLaunch::CommandOptions::g_option_table[] = { SET1 | SET2 | SET3, false, "plugin", 'p', required_argument, NULL, 0, eArgTypePlugin, "Name of the process plugin you want to use."}, { SET2 , false, "tty", 't', optional_argument, NULL, 0, eArgTypePath, "Start the process in a terminal. If is specified, look for a terminal whose name contains , else start the process in a new terminal."}, { SET3, false, "no-stdio", 'n', no_argument, NULL, 0, eArgTypeNone, "Do not set up for terminal I/O to go to running process."}, +{ SET1 | SET2 | SET3, false, "working-dir", 'w', required_argument, NULL, 0, eArgTypePath, "Set the current working directory to when running the inferior."}, { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } }; diff --git a/lldb/source/Core/UserSettingsController.cpp b/lldb/source/Core/UserSettingsController.cpp index 6cd1f3ccfd82..ea7d84e94949 100644 --- a/lldb/source/Core/UserSettingsController.cpp +++ b/lldb/source/Core/UserSettingsController.cpp @@ -1946,9 +1946,17 @@ UserSettingsController::UpdateStringVariable (lldb::VarSetOperationType op, Error &err) { if (op == lldb::eVarSetOperationAssign) - string_var = new_value; + { + if (new_value && new_value[0]) + string_var.assign (new_value); + else + string_var.clear(); + } else if (op == lldb::eVarSetOperationAppend) - string_var.append (new_value); + { + if (new_value && new_value[0]) + string_var.append (new_value); + } else if (op == lldb::eVarSetOperationClear) string_var.clear(); else @@ -1964,19 +1972,25 @@ UserSettingsController::UpdateBooleanVariable (lldb::VarSetOperationType op, if (op != lldb::eVarSetOperationAssign) err.SetErrorString ("Invalid operation for Boolean variable. Cannot update value.\n"); + if (new_value && new_value[0]) + { + if ((::strcasecmp(new_value, "true") == 0) || + (::strcasecmp(new_value, "yes") == 0) || + (::strcasecmp(new_value, "on") == 0) || + (::strcasecmp(new_value, "1") == 0)) + bool_var = true; + else + if ((::strcasecmp(new_value, "false") == 0) || + (::strcasecmp(new_value, "no") == 0) || + (::strcasecmp(new_value, "off") == 0) || + (::strcasecmp(new_value, "0") == 0)) + bool_var = false; + else + err.SetErrorStringWithFormat ("Invalid boolean value '%s'\n", new_value); + } + else + err.SetErrorString ("Invalid value. Cannot perform update.\n"); - if ((new_value == NULL) - || (new_value[0] == '\0')) - err.SetErrorString ("Invalid value. Cannot perform update.\n"); - - std::string bool_val_str (new_value); - - std::transform (bool_val_str.begin(), bool_val_str.end(), bool_val_str.begin(), ::tolower); - - if (bool_val_str == "true") - bool_var = true; - else if (bool_val_str == "false") - bool_var = false; } void diff --git a/lldb/source/Host/common/Host.cpp b/lldb/source/Host/common/Host.cpp index 5755aa72c6d6..ce82694ca2bf 100644 --- a/lldb/source/Host/common/Host.cpp +++ b/lldb/source/Host/common/Host.cpp @@ -907,6 +907,7 @@ Host::LaunchInNewTerminal const char *tty_name, const char **argv, const char **envp, + const char *working_dir, const ArchSpec *arch_spec, bool stop_at_entry, bool disable_aslr diff --git a/lldb/source/Host/macosx/Host.mm b/lldb/source/Host/macosx/Host.mm index a76ec805c198..08aec38c3ec6 100644 --- a/lldb/source/Host/macosx/Host.mm +++ b/lldb/source/Host/macosx/Host.mm @@ -226,6 +226,7 @@ LaunchInNewTerminalWithCommandFile ( const char **argv, const char **envp, + const char *working_dir, const ArchSpec *arch_spec, bool stop_at_entry, bool disable_aslr @@ -404,6 +405,7 @@ LaunchInNewTerminalWithAppleScript const char *tty_name, const char **argv, const char **envp, + const char *working_dir, const ArchSpec *arch_spec, bool stop_at_entry, bool disable_aslr @@ -440,6 +442,9 @@ LaunchInNewTerminalWithAppleScript if (arch_spec && arch_spec->IsValid()) command.Printf(" --arch=%s", arch_spec->AsCString()); + if (working_dir) + command.Printf(" --working-dir '%s'", working_dir); + if (disable_aslr) command.PutCString(" --disable-aslr"); @@ -520,15 +525,16 @@ Host::LaunchInNewTerminal const char *tty_name, const char **argv, const char **envp, + const char *working_dir, const ArchSpec *arch_spec, bool stop_at_entry, bool disable_aslr ) { #if defined (LLDB_HOST_USE_APPLESCRIPT) - return LaunchInNewTerminalWithAppleScript (tty_name, argv, envp, arch_spec, stop_at_entry, disable_aslr); + return LaunchInNewTerminalWithAppleScript (tty_name, argv, envp, working_dir, arch_spec, stop_at_entry, disable_aslr); #else - return LaunchInNewTerminalWithCommandFile (argv, envp, arch_spec, stop_at_entry, disable_aslr); + return LaunchInNewTerminalWithCommandFile (argv, envp, working_dir, arch_spec, stop_at_entry, disable_aslr); #endif } diff --git a/lldb/source/Plugins/Process/Linux/ProcessLinux.cpp b/lldb/source/Plugins/Process/Linux/ProcessLinux.cpp index ecf8479ddde6..fa5b5b70ec32 100644 --- a/lldb/source/Plugins/Process/Linux/ProcessLinux.cpp +++ b/lldb/source/Plugins/Process/Linux/ProcessLinux.cpp @@ -122,7 +122,8 @@ ProcessLinux::DoLaunch(Module *module, uint32_t launch_flags, const char *stdin_path, const char *stdout_path, - const char *stderr_path) + const char *stderr_path, + const char *working_directory) { Error error; assert(m_monitor == NULL); diff --git a/lldb/source/Plugins/Process/Linux/ProcessLinux.h b/lldb/source/Plugins/Process/Linux/ProcessLinux.h index fbf14df19885..6fe05526647f 100644 --- a/lldb/source/Plugins/Process/Linux/ProcessLinux.h +++ b/lldb/source/Plugins/Process/Linux/ProcessLinux.h @@ -72,7 +72,8 @@ public: uint32_t launch_flags, const char *stdin_path, const char *stdout_path, - const char *stderr_path); + const char *stderr_path, + const char *working_directory); virtual void DidLaunch(); diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSX.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSX.cpp index c650f5bb6226..9aebc8fdec69 100644 --- a/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSX.cpp +++ b/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSX.cpp @@ -316,7 +316,8 @@ ProcessMacOSX::DoLaunch uint32_t flags, const char *stdin_path, const char *stdout_path, - const char *stderr_path + const char *stderr_path, + const char *working_dir ) { // ::LogSetBitMask (PD_LOG_DEFAULT); diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSX.h b/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSX.h index 4f8bdb930b76..c3df2f3a2dc7 100644 --- a/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSX.h +++ b/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSX.h @@ -95,8 +95,9 @@ public: char const *envp[], // Can be NULL uint32_t launch_flags, const char *stdin_path, // Can be NULL - const char *stdout_path, // Can be NULL - const char *stderr_path); // Can be NULL + const char *stdout_path, // Can be NULL + const char *stderr_path, // Can be NULL + const char *working_dir); // Can be NULL virtual void DidLaunch (); diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp index fe4e06820fca..ffec00e6d6f4 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -375,7 +375,8 @@ ProcessGDBRemote::DoLaunch uint32_t launch_flags, const char *stdin_path, const char *stdout_path, - const char *stderr_path + const char *stderr_path, + const char *working_dir ) { Error error; @@ -400,7 +401,10 @@ ProcessGDBRemote::DoLaunch error = StartDebugserverProcess (host_port, argv, envp, - NULL, //stdin_path, + stdin_path, + stdout_path, + stderr_path, + working_dir, launch_process, LLDB_INVALID_PROCESS_ID, NULL, false, @@ -420,10 +424,14 @@ ProcessGDBRemote::DoLaunch error = StartDebugserverProcess (host_port, NULL, NULL, - NULL, //stdin_path + stdin_path, + stdout_path, + stderr_path, + working_dir, launch_process, LLDB_INVALID_PROCESS_ID, - NULL, false, + NULL, + false, launch_flags, inferior_arch); if (error.Fail()) @@ -644,6 +652,9 @@ ProcessGDBRemote::DoAttachToProcessWithID (lldb::pid_t attach_pid) NULL, // inferior_argv NULL, // inferior_envp NULL, // stdin_path + NULL, // stdout_path + NULL, // stderr_path + NULL, // working_dir false, // launch_process == false (we are attaching) LLDB_INVALID_PROCESS_ID, // Don't send any attach to pid options to debugserver NULL, // Don't send any attach by process name option to debugserver @@ -746,6 +757,9 @@ ProcessGDBRemote::DoAttachToProcessWithName (const char *process_name, bool wait NULL, // inferior_argv NULL, // inferior_envp NULL, // stdin_path + NULL, // stdout_path + NULL, // stderr_path + NULL, // working_dir false, // launch_process == false (we are attaching) LLDB_INVALID_PROCESS_ID, // Don't send any attach to pid options to debugserver NULL, // Don't send any attach by process name option to debugserver @@ -1732,7 +1746,10 @@ ProcessGDBRemote::StartDebugserverProcess const char *debugserver_url, // The connection string to use in the spawned debugserver ("localhost:1234" or "/dev/tty...") char const *inferior_argv[], // Arguments for the inferior program including the path to the inferior itself as the first argument char const *inferior_envp[], // Environment to pass along to the inferior program - char const *stdio_path, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + const char *working_dir, bool launch_process, // Set to true if we are going to be launching a the process lldb::pid_t attach_pid, // If inferior inferior_argv == NULL, and attach_pid != LLDB_INVALID_PROCESS_ID send this pid as an argument to debugserver const char *attach_name, // Wait for the next process to launch whose basename matches "attach_name" @@ -1824,7 +1841,13 @@ ProcessGDBRemote::StartDebugserverProcess char arg_cstr[PATH_MAX]; lldb_utility::PseudoTerminal pty; - if (launch_process && stdio_path == NULL && m_local_debugserver && !no_stdio) + const char *stdio_path = NULL; + if (launch_process && + stdin_path == NULL && + stdout_path == NULL && + stderr_path == NULL && + m_local_debugserver && + no_stdio == false) { if (pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY, NULL, 0)) stdio_path = pty.GetSlaveName (NULL, 0); @@ -1843,14 +1866,60 @@ ProcessGDBRemote::StartDebugserverProcess debugserver_args.AppendArguments("--disable-aslr"); // Only set the inferior - if (launch_process && stdio_path) + if (launch_process) { - debugserver_args.AppendArgument("--stdio-path"); - debugserver_args.AppendArgument(stdio_path); + if (no_stdio) + debugserver_args.AppendArgument("--no-stdio"); + else + { + if (stdin_path && stdout_path && stderr_path && + strcmp(stdin_path, stdout_path) == 0 && + strcmp(stdin_path, stderr_path) == 0) + { + stdio_path = stdin_path; + stdin_path = stdout_path = stderr_path = NULL; + } + + if (stdio_path) + { + // All file handles to stdin, stdout, stderr are the same... + debugserver_args.AppendArgument("--stdio-path"); + debugserver_args.AppendArgument(stdio_path); + } + else + { + if (stdin_path == NULL && (stdout_path || stderr_path)) + stdin_path = "/dev/null"; + + if (stdout_path == NULL && (stdin_path || stderr_path)) + stdout_path = "/dev/null"; + + if (stderr_path == NULL && (stdin_path || stdout_path)) + stderr_path = "/dev/null"; + + if (stdin_path) + { + debugserver_args.AppendArgument("--stdin-path"); + debugserver_args.AppendArgument(stdin_path); + } + if (stdout_path) + { + debugserver_args.AppendArgument("--stdout-path"); + debugserver_args.AppendArgument(stdout_path); + } + if (stderr_path) + { + debugserver_args.AppendArgument("--stderr-path"); + debugserver_args.AppendArgument(stderr_path); + } + } + } } - else if (launch_process && no_stdio) + + if (working_dir) { - debugserver_args.AppendArgument("--no-stdio"); + debugserver_args.AppendArgument("--working-dir"); + debugserver_args.AppendArgument(working_dir); } const char *env_debugserver_log_file = getenv("LLDB_DEBUGSERVER_LOG_FILE"); diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h index d8a2cebbd33e..8c8f1a8a9af6 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -82,8 +82,9 @@ public: char const *envp[], // Can be NULL uint32_t flags, const char *stdin_path, // Can be NULL - const char *stdout_path, // Can be NULL - const char *stderr_path); // Can be NULL + const char *stdout_path, // Can be NULL + const char *stderr_path, // Can be NULL + const char *working_dir); // Can be NULL virtual void DidLaunch (); @@ -295,6 +296,9 @@ protected: char const *inferior_argv[], char const *inferior_envp[], const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + const char *working_dir, bool launch_process, // Set to true if we are going to be launching a the process lldb::pid_t attach_pid, // If inferior inferior_argv == NULL, then attach to this pid const char *attach_pid_name, // Wait for the next process to launch whose basename matches "attach_wait_name" diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index c3b9dfbdd234..f0b79b99ff04 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -1456,7 +1456,8 @@ Process::Launch uint32_t launch_flags, const char *stdin_path, const char *stdout_path, - const char *stderr_path + const char *stderr_path, + const char *working_directory ) { Error error; @@ -1507,7 +1508,8 @@ Process::Launch launch_flags, stdin_path, stdout_path, - stderr_path); + stderr_path, + working_directory); if (error.Fail()) { @@ -3252,10 +3254,10 @@ Process::SettingsController::instance_settings_table[] = { "run-args", eSetVarTypeArray, NULL, NULL, false, false, "A list containing all the arguments to be passed to the executable when it is run." }, { "env-vars", eSetVarTypeDictionary, NULL, NULL, false, false, "A list of all the environment variables to be passed to the executable's environment, and their values." }, { "inherit-env", eSetVarTypeBoolean, "true", NULL, false, false, "Inherit the environment from the process that is running LLDB." }, - { "input-path", eSetVarTypeString, "/dev/stdin", NULL, false, false, "The file/path to be used by the executable program for reading its input." }, - { "output-path", eSetVarTypeString, "/dev/stdout", NULL, false, false, "The file/path to be used by the executable program for writing its output." }, - { "error-path", eSetVarTypeString, "/dev/stderr", NULL, false, false, "The file/path to be used by the executable program for writings its error messages." }, - { "plugin", eSetVarTypeEnum, NULL , g_plugins, false, false, "The plugin to be used to run the process." }, + { "input-path", eSetVarTypeString, NULL, NULL, false, false, "The file/path to be used by the executable program for reading its input." }, + { "output-path", eSetVarTypeString, NULL, NULL, false, false, "The file/path to be used by the executable program for writing its output." }, + { "error-path", eSetVarTypeString, NULL, NULL, false, false, "The file/path to be used by the executable program for writings its error messages." }, + { "plugin", eSetVarTypeEnum, NULL, g_plugins, false, false, "The plugin to be used to run the process." }, { "disable-aslr", eSetVarTypeBoolean, "true", NULL, false, false, "Disable Address Space Layout Randomization (ASLR)" }, { "disable-stdio", eSetVarTypeBoolean, "false", NULL, false, false, "Disable stdin/stdout for process (e.g. for a GUI application)" }, { NULL, eSetVarTypeNone, NULL, NULL, false, false, NULL } diff --git a/lldb/tools/darwin-debug/darwin-debug.cpp b/lldb/tools/darwin-debug/darwin-debug.cpp index de9b270a18fd..689c3864affd 100644 --- a/lldb/tools/darwin-debug/darwin-debug.cpp +++ b/lldb/tools/darwin-debug/darwin-debug.cpp @@ -30,8 +30,9 @@ #include #include #include -#include #include +#include +#include #include #include @@ -50,6 +51,7 @@ static struct option g_long_options[] = { "help", no_argument, NULL, 'h' }, { "setsid", no_argument, NULL, 's' }, { "unix-socket", required_argument, NULL, 'u' }, + { "working-dir", required_argument, NULL, 'w' }, { NULL, 0, NULL, 0 } }; @@ -62,7 +64,7 @@ usage() " for debugging.\n" "\n" "SYNOPSIS\n" -" darwin-debug --unix-socket= [--arch=] [--disable-aslr] [--no-env] [--setsid] [--help] -- [ ....]\n" +" darwin-debug --unix-socket= [--arch=] [--working-dir=] [--disable-aslr] [--no-env] [--setsid] [--help] -- [ ....]\n" "\n" "DESCRIPTION\n" " darwin-debug will exec itself into a child process that is\n" @@ -95,7 +97,13 @@ exit_with_errno (int err, const char *prefix) } pid_t -posix_spawn_for_debug (char *const *argv, char *const *envp, cpu_type_t cpu_type, int disable_aslr) +posix_spawn_for_debug +( + char *const *argv, + char *const *envp, + const char *working_dir, + cpu_type_t cpu_type, + int disable_aslr) { pid_t pid = 0; @@ -126,6 +134,15 @@ posix_spawn_for_debug (char *const *argv, char *const *envp, cpu_type_t cpu_type size_t ocount = 0; exit_with_errno (::posix_spawnattr_setbinpref_np (&attr, 1, &cpu_type, &ocount), "posix_spawnattr_setbinpref_np () error: "); } + + // I wish there was a posix_spawn flag to change the working directory of + // the inferior process we will spawn, but there currently isn't. If there + // ever is a better way to do this, we should use it. I would rather not + // manually fork, chdir in the child process, and then posix_spawn with exec + // as the whole reason for doing posix_spawn is to not hose anything up + // after the fork and prior to the exec... + if (working_dir) + ::chdir (working_dir); exit_with_errno (::posix_spawnp (&pid, path, NULL, &attr, (char * const*)argv, (char * const*)envp), "posix_spawn() error: "); @@ -157,6 +174,7 @@ int main (int argc, char *const *argv, char *const *envp, const char **apple) int disable_aslr = 0; // By default we disable ASLR int pass_env = 1; std::string unix_socket_name; + std::string working_dir; while ((ch = getopt_long(argc, argv, "a:dehsu:?", g_long_options, NULL)) != -1) { switch (ch) @@ -199,6 +217,16 @@ int main (int argc, char *const *argv, char *const *envp, const char **apple) unix_socket_name.assign (optarg); break; + case 'w': + { + struct stat working_dir_stat; + if (stat (optarg, &working_dir_stat) == 0) + working_dir.assign (optarg); + else + ::fprintf(stderr, "warning: working directory doesn't exist: '%s'\n", optarg); + } + break; + case 'h': case '?': default: @@ -254,7 +282,15 @@ int main (int argc, char *const *argv, char *const *envp, const char **apple) close (s); system("clear"); - printf ("Launching '%s' for debug with %u arguments:\n", argv[0], argc); + if (working_dir.empty()) + { + char cwd[PATH_MAX]; + const char *cwd_ptr = getcwd(cwd, sizeof(cwd)); + printf ("Launching '%s' in '%s' for debug with %u arguments:\n", argv[0], cwd_ptr, argc); + } + else + printf ("Launching '%s' in '%s' for debug with %u arguments:\n", argv[0], working_dir.c_str(), argc); + for (int i=0; i 0) err_str[0] = '\0'; struct stat path_stat; diff --git a/lldb/tools/debugserver/source/MacOSX/MachProcess.cpp b/lldb/tools/debugserver/source/MacOSX/MachProcess.cpp index 8b4c61b433b3..12c981bccb46 100644 --- a/lldb/tools/debugserver/source/MacOSX/MachProcess.cpp +++ b/lldb/tools/debugserver/source/MacOSX/MachProcess.cpp @@ -1625,7 +1625,16 @@ MachProcess::PosixSpawnChildForPTraceDebugging { posix_spawnattr_t attr; short flags; - DNBLogThreadedIf(LOG_PROCESS, "%s ( path='%s', argv=%p, envp=%p, process )", __FUNCTION__, path, argv, envp); + DNBLogThreadedIf(LOG_PROCESS, "%s ( path='%s', argv=%p, envp=%p, working_dir=%s, stdin=%s, stdout=%s stderr=%s, no-stdio=%i)", + __FUNCTION__, + path, + argv, + envp, + working_directory, + stdin_path, + stdout_path, + stderr_path, + no_stdio); err.SetError( ::posix_spawnattr_init (&attr), DNBError::POSIX); if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) @@ -1703,9 +1712,9 @@ MachProcess::PosixSpawnChildForPTraceDebugging } else { - int slave_fd_err = open (stderr_path ? stderr_path : "/dev/null", O_RDWR , 0); - int slave_fd_in = open (stdin_path ? stdin_path : "/dev/null", O_RDONLY, 0); - int slave_fd_out = open (stdout_path ? stdout_path : "/dev/null", O_WRONLY, 0); + int slave_fd_err = open (stderr_path ? stderr_path : "/dev/null", O_NOCTTY | O_CREAT | O_RDWR , 0640); + int slave_fd_in = open (stdin_path ? stdin_path : "/dev/null", O_NOCTTY | O_RDONLY); + int slave_fd_out = open (stdout_path ? stdout_path : "/dev/null", O_NOCTTY | O_CREAT | O_WRONLY , 0640); err.SetError( ::posix_spawn_file_actions_adddup2(&file_actions, slave_fd_err, STDERR_FILENO), DNBError::POSIX); if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) @@ -1719,12 +1728,23 @@ MachProcess::PosixSpawnChildForPTraceDebugging if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) err.LogThreaded("::posix_spawn_file_actions_adddup2 ( &file_actions, filedes = %d, newfiledes = STDOUT_FILENO )", slave_fd_out); } + + // TODO: Verify if we can set the working directory back immediately + // after the posix_spawnp call without creating a race condition??? + if (working_directory) + ::chdir (working_directory); + err.SetError( ::posix_spawnp (&pid, path, &file_actions, &attr, (char * const*)argv, (char * const*)envp), DNBError::POSIX); if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) err.LogThreaded("::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", pid, path, &file_actions, &attr, argv, envp); } else { + // TODO: Verify if we can set the working directory back immediately + // after the posix_spawnp call without creating a race condition??? + if (working_directory) + ::chdir (working_directory); + err.SetError( ::posix_spawnp (&pid, path, NULL, &attr, (char * const*)argv, (char * const*)envp), DNBError::POSIX); if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) err.LogThreaded("::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", pid, path, NULL, &attr, argv, envp); diff --git a/lldb/tools/debugserver/source/debugserver.cpp b/lldb/tools/debugserver/source/debugserver.cpp index 49f1f0c9f10d..f3a4b69c5e67 100644 --- a/lldb/tools/debugserver/source/debugserver.cpp +++ b/lldb/tools/debugserver/source/debugserver.cpp @@ -651,14 +651,14 @@ static struct option g_long_options[] = { "waitfor-interval", required_argument, NULL, 'i' }, // Time in usecs to wait between sampling the pid list when waiting for a process by name { "waitfor-duration", required_argument, NULL, 'd' }, // The time in seconds to wait for a process to show up by name { "native-regs", no_argument, NULL, 'r' }, // Specify to use the native registers instead of the gdb defaults for the architecture. - { "stdio-path", required_argument, NULL, 's' }, // Set the STDIO path to be used when launching applications (STDIN, STDOUT and STDERR) - { "stdin-path", required_argument, NULL, 'I' }, // Set the STDIN path to be used when launching applications - { "stdout-path", required_argument, NULL, 'O' }, // Set the STDIN path to be used when launching applications - { "stderr-path", required_argument, NULL, 'E' }, // Set the STDIN path to be used when launching applications - { "no-stdio", no_argument, NULL, 'n' }, // Do not set up any stdio (perhaps the program is a GUI program) - { "setsid", no_argument, NULL, 'S' }, // call setsid() to make debugserver run in its own sessions + { "stdio-path", required_argument, NULL, 's' }, // Set the STDIO path to be used when launching applications (STDIN, STDOUT and STDERR) (only if debugserver launches the process) + { "stdin-path", required_argument, NULL, 'I' }, // Set the STDIN path to be used when launching applications (only if debugserver launches the process) + { "stdout-path", required_argument, NULL, 'O' }, // Set the STDIN path to be used when launching applications (only if debugserver launches the process) + { "stderr-path", required_argument, NULL, 'E' }, // Set the STDIN path to be used when launching applications (only if debugserver launches the process) + { "no-stdio", no_argument, NULL, 'n' }, // Do not set up any stdio (perhaps the program is a GUI program) (only if debugserver launches the process) + { "setsid", no_argument, NULL, 'S' }, // call setsid() to make debugserver run in its own session { "disable-aslr", no_argument, NULL, 'D' }, // Use _POSIX_SPAWN_DISABLE_ASLR to avoid shared library randomization - { "chdir", no_argument, NULL, 'c' }, // Use _POSIX_SPAWN_DISABLE_ASLR to avoid shared library randomization + { "working-dir", required_argument, NULL, 'W' }, // The working directory that the inferior process should have (only if debugserver launches the process) { NULL, 0, NULL, 0 } }; @@ -699,7 +699,7 @@ main (int argc, char *argv[]) std::string stdout_path; std::string stderr_path; std::string arch_name; - std::string working_directory; // The new working directory to use for the inferior + std::string working_dir; // The new working directory to use for the inferior useconds_t waitfor_interval = 1000; // Time in usecs between process lists polls when waiting for a process by name, default 1 msec. useconds_t waitfor_duration = 0; // Time in seconds to wait for a process by name, 0 means wait forever. bool no_stdio = false; @@ -785,9 +785,9 @@ main (int argc, char *argv[]) } break; - case 'c': + case 'W': if (optarg && optarg[0]) - working_directory.assign(optarg); + working_dir.assign(optarg); break; case 'x': @@ -826,7 +826,7 @@ main (int argc, char *argv[]) else if (strcasecmp(optarg, "stderr") == 0) log_file = stderr; else - log_file = fopen(optarg, "w+"); + log_file = fopen(optarg, "w"); if (log_file == NULL) { @@ -938,11 +938,11 @@ main (int argc, char *argv[]) return -1; } - if (!working_directory.empty()) + if (!working_dir.empty()) { - if (remote->Context().SetWorkingDirectory (working_directory.c_str()) == false) + if (remote->Context().SetWorkingDirectory (working_dir.c_str()) == false) { - RNBLogSTDERR ("error: working directory doesn't exist '%s'.\n", working_directory.c_str()); + RNBLogSTDERR ("error: working directory doesn't exist '%s'.\n", working_dir.c_str()); exit (8); } }