Set "exec" command from client side.
[src/xds/xds-gdb.git] / gdb-native.go
1 package main
2
3 import (
4         "bufio"
5         "fmt"
6         "os"
7         "os/exec"
8         "syscall"
9         "time"
10
11         "github.com/Sirupsen/logrus"
12         "github.com/kr/pty"
13 )
14
15 type GdbNative struct {
16         log   *logrus.Logger
17         ccmd  string
18         aargs []string
19         eenv  []string
20
21         exeCmd *exec.Cmd
22         fdPty  *os.File
23
24         // callbacks
25         cbOnDisconnect func(error)
26         cbRead         func(timestamp, stdout, stderr string)
27         cbInferiorRead func(timestamp, stdout, stderr string)
28         cbOnExit       func(code int, err error)
29
30         running bool
31 }
32
33 // NewGdbNative creates a new instance of GdbNative
34 func NewGdbNative(log *logrus.Logger, args []string, env []string) *GdbNative {
35         return &GdbNative{
36                 log:   log,
37                 ccmd:  "/usr/bin/gdb",
38                 aargs: args,
39                 eenv:  env,
40         }
41 }
42
43 // SetConfig set additional config fields
44 func (g *GdbNative) SetConfig(name string, value interface{}) error {
45         return fmt.Errorf("Unknown %s field", name)
46 }
47
48 // Init initializes gdb XDS
49 func (g *GdbNative) Init() (int, error) {
50
51         // Create the exec command
52         g.exeCmd = exec.Command(g.ccmd, g.aargs...)
53
54         return 0, nil
55 }
56
57 // Close
58 func (g *GdbNative) Close() error {
59         g.cbOnDisconnect = nil
60         g.cbOnExit = nil
61         g.cbRead = nil
62         g.cbInferiorRead = nil
63
64         g.running = false
65
66         return nil
67 }
68
69 // Start sends a request to start remotely gdb within xds-server
70 func (g *GdbNative) Start(inferiorTTY bool) (int, error) {
71         var err error
72
73         // Start pty and consequently gdb process
74         if g.fdPty, err = pty.Start(g.exeCmd); err != nil {
75                 return int(syscall.ESPIPE), err
76         }
77
78         g.running = true
79
80         // Monitor gdb process EOF
81         go func() {
82                 // Execute command and wait EOF
83                 err := g.exeCmd.Wait()
84                 g.cbOnDisconnect(err)
85                 g.running = false
86         }()
87
88         // Handle STDOUT
89         go func() {
90                 sc := bufio.NewScanner(g.fdPty)
91                 sc.Split(split)
92                 for sc.Scan() {
93                         if g.cbRead != nil {
94                                 g.cbRead(time.Now().String(), sc.Text(), "")
95                         }
96                         if !g.running {
97                                 return
98                         }
99                 }
100         }()
101
102         return 0, nil
103 }
104
105 // Cmd returns the command name
106 func (g *GdbNative) Cmd() string {
107         return g.ccmd
108 }
109
110 // Args returns the list of arguments
111 func (g *GdbNative) Args() []string {
112         return g.aargs
113 }
114
115 // Env returns the list of environment variables
116 func (g *GdbNative) Env() []string {
117         return g.eenv
118 }
119
120 // OnError doesn't make sens
121 func (g *GdbNative) OnError(f func(error)) {
122         // nothing to do
123 }
124
125 // OnDisconnect is called when stdin is disconnected
126 func (g *GdbNative) OnDisconnect(f func(error)) {
127         g.cbOnDisconnect = f
128 }
129
130 // OnExit calls when exit event is received
131 func (g *GdbNative) OnExit(f func(code int, err error)) {
132         g.cbOnExit = f
133 }
134
135 // Read calls when a message/string event is received on stdout or stderr
136 func (g *GdbNative) Read(f func(timestamp, stdout, stderr string)) {
137         g.cbRead = f
138 }
139
140 // InferiorRead calls when a message/string event is received on stdout or stderr of the debugged program (IOW inferior)
141 func (g *GdbNative) InferiorRead(f func(timestamp, stdout, stderr string)) {
142         g.cbInferiorRead = f
143 }
144
145 // Write writes message/string into gdb stdin
146 func (g *GdbNative) Write(args ...interface{}) error {
147         s := fmt.Sprint(args...)
148         _, err := g.fdPty.Write([]byte(s))
149         return err
150 }
151
152 // SendSignal is used to send a signal to remote process/gdb
153 func (g *GdbNative) SendSignal(sig os.Signal) error {
154         return g.exeCmd.Process.Signal(sig)
155 }
156
157 //***** Private functions *****
158
159 func split(data []byte, atEOF bool) (advance int, token []byte, err error) {
160         if atEOF && len(data) == 0 {
161                 return 0, nil, nil
162         }
163         return len(data), data, nil
164 }