11 "github.com/Sirupsen/logrus"
12 common "github.com/iotbzh/xds-common/golib"
13 "github.com/iotbzh/xds-server/lib/apiv1"
14 "github.com/iotbzh/xds-server/lib/crosssdk"
15 "github.com/iotbzh/xds-server/lib/xdsconfig"
16 sio_client "github.com/zhouhui8915/go-socket.io-client"
32 httpCli *common.HTTPClient
33 ioSock *sio_client.Client
37 cbOnDisconnect func(error)
38 cbRead func(timestamp, stdout, stderr string)
39 cbInferiorRead func(timestamp, stdout, stderr string)
40 cbOnExit func(code int, err error)
43 // NewGdbXds creates a new instance of GdbXds
44 func NewGdbXds(log *logrus.Logger, args []string, env []string) *GdbXds {
47 ccmd: "exec $GDB", // var set by environment-setup-xxx script
55 // SetConfig set additional config fields
56 func (g *GdbXds) SetConfig(name string, value interface{}) error {
59 g.uri = value.(string)
61 g.prjID = value.(string)
63 g.sdkID = value.(string)
65 g.rPath = value.(string)
67 g.listPrj = value.(bool)
69 return fmt.Errorf("Unknown %s field", name)
74 // Init initializes gdb XDS
75 func (g *GdbXds) Init() (int, error) {
77 // Define HTTP and WS url
79 if !strings.HasPrefix(g.uri, "http://") {
80 baseURL = "http://" + g.uri
84 g.log.Infoln("Connect HTTP client on ", baseURL)
85 conf := common.HTTPClientConfig{
87 HeaderClientKeyName: "XDS-SID",
90 c, err := common.HTTPNewClient(baseURL, conf)
92 return int(syscallEBADE), err
96 // First call to check that xds-server is alive
98 if err := c.HTTPGet("/folders", &data); err != nil {
99 return int(syscallEBADE), err
101 g.log.Infof("Result of /folders: %v", string(data[:]))
103 // Check mandatory args
104 if g.prjID == "" || g.listPrj {
105 return getProjectsList(c, g.log, data)
108 // Create io Websocket client
109 g.log.Infoln("Connecting IO.socket client on ", baseURL)
111 opts := &sio_client.Options{
112 Transport: "websocket",
113 Header: make(map[string][]string),
115 opts.Header["XDS-SID"] = []string{c.GetClientID()}
117 iosk, err := sio_client.NewClient(baseURL, opts)
119 e := fmt.Sprintf("IO.socket connection error: " + err.Error())
120 return int(syscall.ECONNABORTED), fmt.Errorf(e)
124 iosk.On("error", func(err error) {
125 if g.cbOnError != nil {
130 iosk.On("disconnection", func(err error) {
131 if g.cbOnDisconnect != nil {
132 g.cbOnDisconnect(err)
136 iosk.On(apiv1.ExecOutEvent, func(ev apiv1.ExecOutMsg) {
138 g.cbRead(ev.Timestamp, ev.Stdout, ev.Stderr)
142 iosk.On(apiv1.ExecInferiorOutEvent, func(ev apiv1.ExecOutMsg) {
143 if g.cbInferiorRead != nil {
144 g.cbInferiorRead(ev.Timestamp, ev.Stdout, ev.Stderr)
148 iosk.On(apiv1.ExecExitEvent, func(ev apiv1.ExecExitMsg) {
149 if g.cbOnExit != nil {
150 g.cbOnExit(ev.Code, ev.Error)
157 func (g *GdbXds) Close() error {
158 g.cbOnDisconnect = nil
162 g.cbInferiorRead = nil
167 // Start sends a request to start remotely gdb within xds-server
168 func (g *GdbXds) Start(inferiorTTY bool) (int, error) {
172 // Enable workaround about inferior output with gdbserver connection
173 // except if XDS_GDBSERVER_OUTPUT_NOFIX is defined
174 _, gdbserverNoFix := os.LookupEnv("XDS_GDBSERVER_OUTPUT_NOFIX")
176 args := apiv1.ExecArgs{
184 TTYGdbserverFix: !gdbserverNoFix,
185 CmdTimeout: -1, // no timeout, end when stdin close or command exited normally
187 body, err = json.Marshal(args)
189 return int(syscallEBADE), err
192 g.log.Infof("POST %s/exec %v", g.uri, string(body))
193 var res *http.Response
195 res, err = g.httpCli.HTTPPostWithRes("/exec", string(body))
197 return int(syscall.EAGAIN), err
199 dRes := make(map[string]interface{})
200 json.Unmarshal(g.httpCli.ResponseToBArray(res), &dRes)
201 if _, found = dRes["cmdID"]; !found {
202 return int(syscallEBADE), err
204 g.cmdID = dRes["cmdID"].(string)
209 // Cmd returns the command name
210 func (g *GdbXds) Cmd() string {
214 // Args returns the list of arguments
215 func (g *GdbXds) Args() []string {
219 // Env returns the list of environment variables
220 func (g *GdbXds) Env() []string {
224 // OnError is called on a WebSocket error
225 func (g *GdbXds) OnError(f func(error)) {
229 // OnDisconnect is called when WebSocket disconnection
230 func (g *GdbXds) OnDisconnect(f func(error)) {
234 // OnExit calls when exit event is received
235 func (g *GdbXds) OnExit(f func(code int, err error)) {
239 // Read calls when a message/string event is received on stdout or stderr
240 func (g *GdbXds) Read(f func(timestamp, stdout, stderr string)) {
244 // InferiorRead calls when a message/string event is received on stdout or stderr of the debugged program (IOW inferior)
245 func (g *GdbXds) InferiorRead(f func(timestamp, stdout, stderr string)) {
249 // Write writes message/string into gdb stdin
250 func (g *GdbXds) Write(args ...interface{}) error {
251 return g.ioSock.Emit(apiv1.ExecInEvent, args...)
254 // SendSignal is used to send a signal to remote process/gdb
255 func (g *GdbXds) SendSignal(sig os.Signal) error {
257 body, err := json.Marshal(apiv1.ExecSignalArgs{
259 Signal: sig.String(),
262 g.log.Errorf(err.Error())
264 g.log.Debugf("POST /signal %s", string(body))
265 return g.httpCli.HTTPPost("/signal", string(body))
268 //***** Private functions *****
270 func getProjectsList(c *common.HTTPClient, log *logrus.Logger, prjData []byte) (int, error) {
272 folders := xdsconfig.FoldersConfig{}
273 errMar := json.Unmarshal(prjData, &folders)
276 msg += "List of existing projects (use: export XDS_PROJECT_ID=<< ID >>): \n"
277 msg += " ID\t\t\t\t | Label"
278 for _, f := range folders {
279 msg += fmt.Sprintf("\n %s\t | %s", f.ID, f.Label)
280 if f.DefaultSdk != "" {
281 msg += fmt.Sprintf("\t(default SDK: %s)", f.DefaultSdk)
288 if err := c.HTTPGet("/sdks", &data); err != nil {
289 return int(syscallEBADE), err
291 log.Infof("Result of /sdks: %v", string(data[:]))
293 sdks := []crosssdk.SDK{}
294 errMar = json.Unmarshal(data, &sdks)
296 msg += "\nList of installed cross SDKs (use: export XDS_SDK_ID=<< ID >>): \n"
297 msg += " ID\t\t\t\t\t | NAME\n"
298 for _, s := range sdks {
299 msg += fmt.Sprintf(" %s\t | %s\n", s.ID, s.Name)
303 if len(folders) > 0 && len(sdks) > 0 {
304 msg += fmt.Sprintf("\n")
305 msg += fmt.Sprintf("For example: \n")
306 msg += fmt.Sprintf(" XDS_PROJECT_ID=%q XDS_SDK_ID=%q %s -x myGdbConf.ini\n",
307 folders[0].ID, sdks[0].ID, AppName)
310 return 0, fmt.Errorf(msg)