private Task <Results> CmdLinuxBreak(int debugeePid, ResultClass expectedResultClass) { // Send sigint to the debuggee process. This is the equivalent of hitting ctrl-c on the console. // This will cause gdb to async-break. This is necessary because gdb does not support async break // when attached. const int sigint = 2; LinuxNativeMethods.Kill(debugeePid, sigint); return(Task.FromResult <Results>(new Results(ResultClass.done))); }
private void MakeGdbFifo(string path) { // Mod is normally in octal, but C# has no octal values. This is 384 (rw owner, no rights anyone else) const int rw_owner = 384; int result = LinuxNativeMethods.MkFifo(path, rw_owner); if (result != 0) { // Failed to create the fifo. Bail. Logger.WriteLine("Failed to create gdb fifo"); throw new ArgumentException("MakeGdbFifo failed to create fifo at path {0}", path); } }
public override void InitStreams(LaunchOptions options, out StreamReader reader, out StreamWriter writer) { LocalLaunchOptions localOptions = (LocalLaunchOptions)options; string debuggeeDir = System.IO.Path.GetDirectoryName(options.ExePath); string gdbStdInName = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); string gdbStdOutName = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); MakeGdbFifo(gdbStdInName); MakeGdbFifo(gdbStdOutName); // Setup the streams on the fifos as soon as possible. System.IO.FileStream gdbStdInStream = new FileStream(gdbStdInName, FileMode.Open); System.IO.FileStream gdbStdOutStream = new FileStream(gdbStdOutName, FileMode.Open); // If running as root, make sure the new console is also root. bool isRoot = LinuxNativeMethods.GetEUid() == 0; // Spin up a new bash shell, cd to the working dir, execute a tty command to get the shell tty and store it // start the debugger in mi mode setting the tty to the terminal defined earlier and redirect stdin/stdout // to the correct pipes. After gdb exits, cleanup the FIFOs. This is done using the trap command to add a // signal handler for SIGHUP on the console (executing the two rm commands) // // NOTE: sudo launch requires sudo or the terminal will fail to launch. The first argument must then be the terminal path // TODO: this should be configurable in launch options to allow for other terminals with a default of gnome-terminal so the user can change the terminal // command. Note that this is trickier than it sounds since each terminal has its own set of parameters. For now, rely on remote for those scenarios string terminalPath = "/usr/bin/gnome-terminal"; string sudoPath = "/usr/bin/sudo"; Process terminalProcess = new Process(); terminalProcess.StartInfo.CreateNoWindow = false; terminalProcess.StartInfo.UseShellExecute = false; terminalProcess.StartInfo.WorkingDirectory = debuggeeDir; terminalProcess.StartInfo.FileName = !isRoot ? terminalPath : sudoPath; string argumentString = string.Format(System.Globalization.CultureInfo.InvariantCulture, "--title DebuggerTerminal -x bash -c \"cd {0}; DbgTerm=`tty`; trap 'rm {2}; rm {3}' EXIT; {1} --interpreter=mi --tty=$DbgTerm < {2} > {3};", debuggeeDir, localOptions.MIDebuggerPath, gdbStdInName, gdbStdOutName ); terminalProcess.StartInfo.Arguments = !isRoot ? argumentString : String.Concat(terminalPath, " ", argumentString); Logger.WriteLine("LocalLinuxTransport command: " + terminalProcess.StartInfo.FileName + " " + terminalProcess.StartInfo.Arguments); if (localOptions.Environment != null) { foreach (EnvironmentEntry entry in localOptions.Environment) { terminalProcess.StartInfo.Environment.Add(entry.Name, entry.Value); } } terminalProcess.Start(); // The in/out names are confusing in this case as they are relative to gdb. // What that means is the names are backwards wrt miengine hence the reader // being the writer and vice-versa writer = new StreamWriter(gdbStdInStream); reader = new StreamReader(gdbStdOutStream); }
public override void InitStreams(LaunchOptions options, out StreamReader reader, out StreamWriter writer) { LocalLaunchOptions localOptions = (LocalLaunchOptions)options; if (!this.IsValidMiDebuggerPath(localOptions.MIDebuggerPath)) { throw new Exception(MICoreResources.Error_InvalidMiDebuggerPath); } // Default working directory is next to the app string debuggeeDir; if (Path.IsPathRooted(options.ExePath) && File.Exists(options.ExePath)) { debuggeeDir = System.IO.Path.GetDirectoryName(options.ExePath); } else { // If we don't know where the app is, default to HOME, and if we somehow can't get that, go with the root directory. debuggeeDir = Environment.GetEnvironmentVariable("HOME"); if (string.IsNullOrEmpty(debuggeeDir)) { debuggeeDir = "/"; } } _gdbStdInName = Path.Combine(Path.GetTempPath(), FifoPrefix + Path.GetRandomFileName()); _gdbStdOutName = Path.Combine(Path.GetTempPath(), FifoPrefix + Path.GetRandomFileName()); MakeGdbFifo(_gdbStdInName); MakeGdbFifo(_gdbStdOutName); _fifoWatcher = new FileSystemWatcher(Path.GetTempPath(), FifoPrefix + "*"); _fifoWatcher.Deleted += FifoWatcher_Deleted; _fifoWatcher.EnableRaisingEvents = true; // Setup the streams on the fifos as soon as possible. System.IO.FileStream gdbStdInStream = new FileStream(_gdbStdInName, FileMode.Open); System.IO.FileStream gdbStdOutStream = new FileStream(_gdbStdOutName, FileMode.Open); // If running as root, make sure the new console is also root. bool isRoot = LinuxNativeMethods.GetEUid() == 0; // Check and see if gnome-terminal exists. If not, fall back to xterm string terminalCmd, bashCommandPrefix; if (File.Exists(GnomeTerminalPath)) { terminalCmd = GnomeTerminalPath; bashCommandPrefix = "--title DebuggerTerminal -x"; } else { terminalCmd = XTermPath; bashCommandPrefix = "-title DebuggerTerminal -e"; } // Spin up a new bash shell, cd to the working dir, execute a tty command to get the shell tty and store it // start the debugger in mi mode setting the tty to the terminal defined earlier and redirect stdin/stdout // to the correct pipes. After gdb exits, cleanup the FIFOs. This is done using the trap command to add a // signal handler for SIGHUP on the console (executing the two rm commands) // // NOTE: sudo launch requires sudo or the terminal will fail to launch. The first argument must then be the terminal path // TODO: this should be configurable in launch options to allow for other terminals with a default of gnome-terminal so the user can change the terminal // command. Note that this is trickier than it sounds since each terminal has its own set of parameters. For now, rely on remote for those scenarios Process terminalProcess = new Process(); terminalProcess.StartInfo.CreateNoWindow = false; terminalProcess.StartInfo.UseShellExecute = false; terminalProcess.StartInfo.WorkingDirectory = debuggeeDir; terminalProcess.StartInfo.FileName = !isRoot ? terminalCmd : SudoPath; string debuggerCmd = localOptions.MIDebuggerPath; // If the system doesn't allow a non-root process to attach to another process, try to run GDB as root if (localOptions.ProcessId != 0 && !isRoot && GetRequiresRootAttach(localOptions)) { // Prefer pkexec for a nice graphical prompt, but fall back to sudo if it's not available if (File.Exists(LocalLinuxTransport.PKExecPath)) { debuggerCmd = String.Concat(LocalLinuxTransport.PKExecPath, " ", debuggerCmd); } else if (File.Exists(LocalLinuxTransport.SudoPath)) { debuggerCmd = String.Concat(LocalLinuxTransport.SudoPath, " ", debuggerCmd); } else { Debug.Fail("Root required to attach, but no means of elevating available!"); } } string argumentString = string.Format(CultureInfo.InvariantCulture, "{4} bash -c \"cd {0}; DbgTerm=`tty`; trap 'rm {2}; rm {3}' EXIT; {1} --interpreter=mi --tty=$DbgTerm < {2} > {3};\"", debuggeeDir, debuggerCmd, _gdbStdInName, _gdbStdOutName, bashCommandPrefix ); terminalProcess.StartInfo.Arguments = !isRoot ? argumentString : String.Concat(terminalCmd, " ", argumentString); Logger?.WriteLine("LocalLinuxTransport command: " + terminalProcess.StartInfo.FileName + " " + terminalProcess.StartInfo.Arguments); if (localOptions.Environment != null) { foreach (EnvironmentEntry entry in localOptions.Environment) { terminalProcess.StartInfo.Environment.Add(entry.Name, entry.Value); } } terminalProcess.Start(); // The in/out names are confusing in this case as they are relative to gdb. // What that means is the names are backwards wrt miengine hence the reader // being the writer and vice-versa writer = new StreamWriter(gdbStdInStream); reader = new StreamReader(gdbStdOutStream); }