Example #1
0
        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)));
        }
Example #2
0
        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);
            }
        }
Example #3
0
        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);
        }