package main import ( "bufio" "fmt" "os" "os/exec" "syscall" "time" "github.com/Sirupsen/logrus" "github.com/kr/pty" ) type GdbNative struct { log *logrus.Logger ccmd string aargs []string eenv []string exeCmd *exec.Cmd fdPty *os.File // callbacks cbOnDisconnect func(error) cbRead func(timestamp, stdout, stderr string) cbInferiorRead func(timestamp, stdout, stderr string) cbOnExit func(code int, err error) running bool } // NewGdbNative creates a new instance of GdbNative func NewGdbNative(log *logrus.Logger, args []string, env []string) *GdbNative { return &GdbNative{ log: log, ccmd: "/usr/bin/gdb", aargs: args, eenv: env, } } // SetConfig set additional config fields func (g *GdbNative) SetConfig(name string, value interface{}) error { return fmt.Errorf("Unknown %s field", name) } // Init initializes gdb XDS func (g *GdbNative) Init() (int, error) { // Create the exec command g.exeCmd = exec.Command(g.ccmd, g.aargs...) return 0, nil } // Close func (g *GdbNative) Close() error { g.cbOnDisconnect = nil g.cbOnExit = nil g.cbRead = nil g.cbInferiorRead = nil g.running = false return nil } // Start sends a request to start remotely gdb within xds-server func (g *GdbNative) Start(inferiorTTY bool) (int, error) { var err error // Start pty and consequently gdb process if g.fdPty, err = pty.Start(g.exeCmd); err != nil { return int(syscall.ESPIPE), err } g.running = true // Monitor gdb process EOF go func() { // Execute command and wait EOF err := g.exeCmd.Wait() g.cbOnDisconnect(err) g.running = false }() // Handle STDOUT go func() { sc := bufio.NewScanner(g.fdPty) sc.Split(split) for sc.Scan() { if g.cbRead != nil { g.cbRead(time.Now().String(), sc.Text(), "") } if !g.running { return } } }() return 0, nil } // Cmd returns the command name func (g *GdbNative) Cmd() string { return g.ccmd } // Args returns the list of arguments func (g *GdbNative) Args() []string { return g.aargs } // Env returns the list of environment variables func (g *GdbNative) Env() []string { return g.eenv } // OnError doesn't make sens func (g *GdbNative) OnError(f func(error)) { // nothing to do } // OnDisconnect is called when stdin is disconnected func (g *GdbNative) OnDisconnect(f func(error)) { g.cbOnDisconnect = f } // OnExit calls when exit event is received func (g *GdbNative) OnExit(f func(code int, err error)) { g.cbOnExit = f } // Read calls when a message/string event is received on stdout or stderr func (g *GdbNative) Read(f func(timestamp, stdout, stderr string)) { g.cbRead = f } // InferiorRead calls when a message/string event is received on stdout or stderr of the debugged program (IOW inferior) func (g *GdbNative) InferiorRead(f func(timestamp, stdout, stderr string)) { g.cbInferiorRead = f } // Write writes message/string into gdb stdin func (g *GdbNative) Write(args ...interface{}) error { s := fmt.Sprint(args...) _, err := g.fdPty.Write([]byte(s)) return err } // SendSignal is used to send a signal to remote process/gdb func (g *GdbNative) SendSignal(sig os.Signal) error { return g.exeCmd.Process.Signal(sig) } //***** Private functions ***** func split(data []byte, atEOF bool) (advance int, token []byte, err error) { if atEOF && len(data) == 0 { return 0, nil, nil } return len(data), data, nil }