Initial commit v0.0.1
authorSebastien Douheret <sebastien.douheret@iot.bzh>
Mon, 7 Aug 2017 06:49:25 +0000 (08:49 +0200)
committerSebastien Douheret <sebastien.douheret@iot.bzh>
Mon, 7 Aug 2017 16:01:35 +0000 (18:01 +0200)
Signed-off-by: Sebastien Douheret <sebastien.douheret@iot.bzh>
14 files changed:
.gitignore [new file with mode: 0644]
.vscode/launch.json [new file with mode: 0644]
.vscode/settings.json [new file with mode: 0644]
LICENSE [new file with mode: 0644]
Makefile [new file with mode: 0644]
README.md [new file with mode: 0644]
gdb-common.go [new file with mode: 0644]
gdb-common_darwin.go [new file with mode: 0644]
gdb-common_linux.go [new file with mode: 0644]
gdb-common_windows.go [new file with mode: 0644]
gdb-native.go [new file with mode: 0644]
gdb-xds.go [new file with mode: 0644]
glide.yaml [new file with mode: 0644]
main.go [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..07d6bd4
--- /dev/null
@@ -0,0 +1,9 @@
+bin
+tools
+glide.lock
+vendor
+package
+*.zip
+
+debug
+__*
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644 (file)
index 0000000..f047dc0
--- /dev/null
@@ -0,0 +1,57 @@
+{
+    "version": "0.2.0",
+    "configurations": [
+
+    {
+            "name": "xds-gdb help",
+            "type": "go",
+            "request": "launch",
+            "mode": "debug",
+            "program": "${workspaceRoot}",
+            "env": {
+                "GOPATH": "${workspaceRoot}/../../../..:${env:GOPATH}",
+                "XDS_LOGLEVEL": "debug"
+            },
+            "args": ["-tt", "--help", "--version"],
+            "showLog": false
+        },
+        {
+            "name": "xds-gdb",
+            "type": "go",
+            "request": "launch",
+            "mode": "debug",
+            "program": "${workspaceRoot}",
+            "env": {
+                "GOPATH": "${workspaceRoot}/../../../..:${env:GOPATH}"
+            },
+            "args": ["-x", "/tmp/gdbconf.ini", "-nx"],
+            "showLog": false
+        },
+          {
+            "name": "xds-gdb TTY",
+            "type": "go",
+            "request": "launch",
+            "mode": "debug",
+            "program": "${workspaceRoot}",
+            "env": {
+                "GOPATH": "${workspaceRoot}/../../../..:${env:GOPATH}"
+            },
+            "args": ["-tty", "/dev/pts27", "-nx", "-x", "${workspaceRoot}/__config/gdb-on-m3ulcb_debug_pi.ini"],
+            "showLog": false
+        },
+        {
+            "name": "xds-gdb native",
+            "type": "go",
+            "request": "launch",
+            "mode": "debug",
+            "program": "${workspaceRoot}",
+            "env": {
+                "GOPATH": "${workspaceRoot}/../../../..:${env:GOPATH}",
+                "XDS_LOGLEVEL": "debug"
+            },
+            "args": ["-x", "${workspaceRoot}/__config/gdb-on-localhost_debug_pi.ini"],
+            "showLog": false
+        }
+
+    ]
+}
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644 (file)
index 0000000..c9bea18
--- /dev/null
@@ -0,0 +1,22 @@
+// Place your settings in this file to overwrite default and user settings.
+{
+  // Configure glob patterns for excluding files and folders.
+  "files.exclude": {
+    "**/.tmp": true,
+    ".git": true,
+    "glide.lock": true,
+    "vendor/": true,
+    "debug": true,
+    "bin": true,
+    "tools": true
+  },
+  // Words to add to dictionary for a workspace.
+  "cSpell.words": [
+    "apiv",
+    "iosk",
+    "zhouhui",
+    "ldflags",
+    "socketio",
+    "xdsconfig"
+  ]
+}
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..8dada3e
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..8c58555
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,121 @@
+# Makefile used to build xds-gdb commands
+
+# Application Version
+VERSION := 0.0.1
+
+
+# Retrieve git tag/commit to set sub-version string
+ifeq ($(origin SUB_VERSION), undefined)
+       SUB_VERSION := $(shell git describe --exact-match --tags 2>/dev/null | sed 's/^v//')
+       ifneq ($(SUB_VERSION), )
+               VERSION := $(firstword $(subst -, ,$(SUB_VERSION)))
+               SUB_VERSION := $(word 2,$(subst -, ,$(SUB_VERSION)))
+       else
+               SUB_VERSION := $(shell git rev-parse --short HEAD)
+               ifeq ($(SUB_VERSION), )
+                       SUB_VERSION := unknown-dev
+               endif
+       endif
+endif
+
+HOST_GOOS=$(shell go env GOOS)
+HOST_GOARCH=$(shell go env GOARCH)
+ARCH=$(HOST_GOOS)-$(HOST_GOARCH)
+
+EXT=
+ifeq ($(HOST_GOOS), windows)
+       EXT=.exe
+endif
+
+
+mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))
+ROOT_SRCDIR := $(patsubst %/,%,$(dir $(mkfile_path)))
+BINDIR := $(ROOT_SRCDIR)/bin
+ROOT_GOPRJ := $(abspath $(ROOT_SRCDIR)/../../../..)
+PACKAGE_DIR := $(ROOT_SRCDIR)/package
+
+export GOPATH := $(shell go env GOPATH):$(ROOT_GOPRJ)
+export PATH := $(PATH):$(ROOT_SRCDIR)/tools
+
+VERBOSE_1 := -v
+VERBOSE_2 := -v -x
+
+# Release or Debug mode
+ifeq ($(filter 1,$(RELEASE) $(REL)),)
+       GORELEASE=
+       BUILD_MODE="Debug mode"
+else
+       # optimized code without debug info
+       GORELEASE= -s -w
+       BUILD_MODE="Release mode"
+endif
+
+REPOPATH=github.com/iotbzh/xds-gdb
+TARGET := xds-gdb
+
+build: $(TARGET)
+
+xds-gdb: vendor
+       @echo "### Build $@ (version $(VERSION), subversion $(SUB_VERSION)) - $(BUILD_MODE)";
+       @cd $(ROOT_SRCDIR); $(BUILD_ENV_FLAGS) go build $(VERBOSE_$(V)) -i -o $(BINDIR)/$@$(EXT) -ldflags "$(GORELEASE) -X main.AppVersion=$(VERSION) -X main.AppSubVersion=$(SUB_VERSION)" .
+       @([ "$(HOST_GOOS)" = "linux" ] && { cd $(BINDIR) && ln -sf $@ $(subst xds-,,$@); } || { true; } )
+
+test: tools/glide
+       go test --race $(shell ./tools/glide novendor)
+
+vet: tools/glide
+       go vet $(shell ./tools/glide novendor)
+
+fmt: tools/glide
+       go fmt $(shell ./tools/glide novendor)
+
+.PHONY: clean
+clean:
+       rm -rf $(BINDIR)/* debug $(ROOT_GOPRJ)/pkg/*/$(REPOPATH) $(PACKAGE_DIR)
+
+distclean: clean
+       rm -rf $(BINDIR) tools glide.lock vendor $(ROOT_SRCDIR)/*.zip
+
+.PHONY: release
+release:
+       RELEASE=1 make -f $(ROOT_SRCDIR)/Makefile clean build
+
+package: clean build
+       @mkdir -p $(PACKAGE_DIR)/xds-gdb
+       @cp -a $(BINDIR)/*gdb$(EXT) $(PACKAGE_DIR)/xds-gdb
+       @cd $(PACKAGE_DIR) && zip  --symlinks -r $(ROOT_SRCDIR)/xds-gdb_$(ARCH)-v$(VERSION)_$(SUB_VERSION).zip ./xds-gdb
+
+.PHONY: package-all
+package-all:
+       @echo "# Build linux amd64..."
+       GOOS=linux GOARCH=amd64 RELEASE=1 make -f $(ROOT_SRCDIR)/Makefile package
+       @echo "# Build windows amd64..."
+#      GOOS=windows GOARCH=amd64 RELEASE=1 make -f $(ROOT_SRCDIR)/Makefile package
+       @echo " WARNING: build on Windows not supported for now."
+       @echo "# Build darwin amd64..."
+       GOOS=darwin GOARCH=amd64 RELEASE=1 make -f $(ROOT_SRCDIR)/Makefile package
+
+vendor: tools/glide glide.yaml
+       ./tools/glide install --strip-vendor
+
+vendor/debug: vendor
+       (cd vendor/github.com/iotbzh && \
+               rm -rf xds-common && ln -s ../../../../xds-common && \
+               rm -rf xds-server && ln -s ../../../../xds-server )
+
+tools/glide:
+       @echo "Downloading glide"
+       mkdir -p tools
+       curl --silent -L https://glide.sh/get | GOBIN=./tools  sh
+
+help:
+       @echo "Main supported rules:"
+       @echo "  build               (default)"
+       @echo "  release"
+       @echo "  clean"
+       @echo "  package"
+       @echo "  distclean"
+       @echo ""
+       @echo "Influential make variables:"
+       @echo "  V                 - Build verbosity {0,1,2}."
+       @echo "  BUILD_ENV_FLAGS   - Environment added to 'go build'."
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..389c03d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,4 @@
+xds-gdb: wrapper on gdb for XDS
+=================================
+
+`xds-gdb` is a wrapper on gdb debugger for X(cross) Development System.
diff --git a/gdb-common.go b/gdb-common.go
new file mode 100644 (file)
index 0000000..a6984cc
--- /dev/null
@@ -0,0 +1,21 @@
+package main
+
+import "os"
+
+// IGDB is an interface for GDB
+type IGDB interface {
+       Init() (int, error)
+       Close() error
+       SetConfig(name string, value interface{}) error
+       Start(bool) (int, error)
+       Cmd() string
+       Args() []string
+       Env() []string
+       OnError(f func(error))
+       OnDisconnect(f func(error))
+       OnExit(f func(int, error))
+       Read(f func(timestamp, stdout, stderr string))
+       InferiorRead(f func(timestamp, stdout, stderr string))
+       Write(args ...interface{}) error
+       SendSignal(sig os.Signal) error
+}
diff --git a/gdb-common_darwin.go b/gdb-common_darwin.go
new file mode 100644 (file)
index 0000000..d167c5d
--- /dev/null
@@ -0,0 +1,42 @@
+package main
+
+import (
+       "os"
+       "syscall"
+       "unsafe"
+)
+
+const (
+       syscallEBADE = syscall.EBADEXEC
+
+       syscall_TCGETS = 0x402c7413
+       syscall_TCSETS = 0x802c7414
+)
+
+func fcntl(fd uintptr, cmd int, arg int) (val int, err error) {
+       r, _, e := syscall.Syscall(syscall.SYS_FCNTL, fd, uintptr(cmd),
+               uintptr(arg))
+       val = int(r)
+       if e != 0 {
+               err = e
+       }
+       return
+}
+
+func tcsetattr(fd uintptr, termios *syscall.Termios) error {
+       r, _, e := syscall.Syscall(syscall.SYS_IOCTL,
+               fd, uintptr(syscall_TCSETS), uintptr(unsafe.Pointer(termios)))
+       if r != 0 {
+               return os.NewSyscallError("SYS_IOCTL", e)
+       }
+       return nil
+}
+
+func tcgetattr(fd uintptr, termios *syscall.Termios) error {
+       r, _, e := syscall.Syscall(syscall.SYS_IOCTL,
+               fd, uintptr(syscall_TCGETS), uintptr(unsafe.Pointer(termios)))
+       if r != 0 {
+               return os.NewSyscallError("SYS_IOCTL", e)
+       }
+       return nil
+}
diff --git a/gdb-common_linux.go b/gdb-common_linux.go
new file mode 100644 (file)
index 0000000..a2f4bf6
--- /dev/null
@@ -0,0 +1,37 @@
+package main
+
+import (
+       "os"
+       "syscall"
+       "unsafe"
+)
+
+const syscallEBADE = syscall.EBADE
+
+func fcntl(fd uintptr, cmd int, arg int) (val int, err error) {
+       r, _, e := syscall.Syscall(syscall.SYS_FCNTL, fd, uintptr(cmd),
+               uintptr(arg))
+       val = int(r)
+       if e != 0 {
+               err = e
+       }
+       return
+}
+
+func tcsetattr(fd uintptr, termios *syscall.Termios) error {
+       r, _, e := syscall.Syscall(syscall.SYS_IOCTL,
+               fd, uintptr(syscall.TCSETS), uintptr(unsafe.Pointer(termios)))
+       if r != 0 {
+               return os.NewSyscallError("SYS_IOCTL", e)
+       }
+       return nil
+}
+
+func tcgetattr(fd uintptr, termios *syscall.Termios) error {
+       r, _, e := syscall.Syscall(syscall.SYS_IOCTL,
+               fd, uintptr(syscall.TCGETS), uintptr(unsafe.Pointer(termios)))
+       if r != 0 {
+               return os.NewSyscallError("SYS_IOCTL", e)
+       }
+       return nil
+}
diff --git a/gdb-common_windows.go b/gdb-common_windows.go
new file mode 100644 (file)
index 0000000..b233943
--- /dev/null
@@ -0,0 +1,7 @@
+package main
+
+import "syscall"
+
+const (
+       syscallEBADE = syscall.EBADE
+)
diff --git a/gdb-native.go b/gdb-native.go
new file mode 100644 (file)
index 0000000..a4e6189
--- /dev/null
@@ -0,0 +1,164 @@
+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
+}
diff --git a/gdb-xds.go b/gdb-xds.go
new file mode 100644 (file)
index 0000000..f174017
--- /dev/null
@@ -0,0 +1,311 @@
+package main
+
+import (
+       "encoding/json"
+       "fmt"
+       "net/http"
+       "os"
+       "strings"
+       "syscall"
+
+       "github.com/Sirupsen/logrus"
+       common "github.com/iotbzh/xds-common/golib"
+       "github.com/iotbzh/xds-server/lib/apiv1"
+       "github.com/iotbzh/xds-server/lib/crosssdk"
+       "github.com/iotbzh/xds-server/lib/xdsconfig"
+       sio_client "github.com/zhouhui8915/go-socket.io-client"
+)
+
+// GdbXds -
+type GdbXds struct {
+       log     *logrus.Logger
+       ccmd    string
+       aargs   []string
+       eenv    []string
+       uri     string
+       prjID   string
+       sdkID   string
+       rPath   string
+       listPrj bool
+       cmdID   string
+
+       httpCli *common.HTTPClient
+       ioSock  *sio_client.Client
+
+       // callbacks
+       cbOnError      func(error)
+       cbOnDisconnect func(error)
+       cbRead         func(timestamp, stdout, stderr string)
+       cbInferiorRead func(timestamp, stdout, stderr string)
+       cbOnExit       func(code int, err error)
+}
+
+// NewGdbXds creates a new instance of GdbXds
+func NewGdbXds(log *logrus.Logger, args []string, env []string) *GdbXds {
+       return &GdbXds{
+               log:     log,
+               ccmd:    "$GDB", // var set by environment-setup-xxx script
+               aargs:   args,
+               eenv:    env,
+               httpCli: nil,
+               ioSock:  nil,
+       }
+}
+
+// SetConfig set additional config fields
+func (g *GdbXds) SetConfig(name string, value interface{}) error {
+       switch name {
+       case "uri":
+               g.uri = value.(string)
+       case "prjID":
+               g.prjID = value.(string)
+       case "sdkID":
+               g.sdkID = value.(string)
+       case "rPath":
+               g.rPath = value.(string)
+       case "listProject":
+               g.listPrj = value.(bool)
+       default:
+               return fmt.Errorf("Unknown %s field", name)
+       }
+       return nil
+}
+
+// Init initializes gdb XDS
+func (g *GdbXds) Init() (int, error) {
+
+       // Define HTTP and WS url
+       baseURL := g.uri
+       if !strings.HasPrefix(g.uri, "http://") {
+               baseURL = "http://" + g.uri
+       }
+
+       // Create HTTP client
+       g.log.Infoln("Connect HTTP client on ", baseURL)
+       conf := common.HTTPClientConfig{
+               URLPrefix:           "/api/v1",
+               HeaderClientKeyName: "XDS-SID",
+               CsrfDisable:         true,
+       }
+       c, err := common.HTTPNewClient(baseURL, conf)
+       if err != nil {
+               return int(syscallEBADE), err
+       }
+       g.httpCli = c
+
+       // First call to check that xds-server is alive
+       var data []byte
+       if err := c.HTTPGet("/folders", &data); err != nil {
+               return int(syscallEBADE), err
+       }
+       g.log.Infof("Result of /folders: %v", string(data[:]))
+
+       // Check mandatory args
+       if g.prjID == "" || g.listPrj {
+               return getProjectsList(c, g.log, data)
+       }
+
+       // Create io Websocket client
+       g.log.Infoln("Connecting IO.socket client on ", baseURL)
+
+       opts := &sio_client.Options{
+               Transport: "websocket",
+               Header:    make(map[string][]string),
+       }
+       opts.Header["XDS-SID"] = []string{c.GetClientID()}
+
+       iosk, err := sio_client.NewClient(baseURL, opts)
+       if err != nil {
+               e := fmt.Sprintf("IO.socket connection error: " + err.Error())
+               return int(syscall.ECONNABORTED), fmt.Errorf(e)
+       }
+       g.ioSock = iosk
+
+       iosk.On("error", func(err error) {
+               if g.cbOnError != nil {
+                       g.cbOnError(err)
+               }
+       })
+
+       iosk.On("disconnection", func(err error) {
+               if g.cbOnDisconnect != nil {
+                       g.cbOnDisconnect(err)
+               }
+       })
+
+       iosk.On(apiv1.ExecOutEvent, func(ev apiv1.ExecOutMsg) {
+               if g.cbRead != nil {
+                       g.cbRead(ev.Timestamp, ev.Stdout, ev.Stderr)
+               }
+       })
+
+       iosk.On(apiv1.ExecInferiorOutEvent, func(ev apiv1.ExecOutMsg) {
+               if g.cbInferiorRead != nil {
+                       g.cbInferiorRead(ev.Timestamp, ev.Stdout, ev.Stderr)
+               }
+       })
+
+       iosk.On(apiv1.ExecExitEvent, func(ev apiv1.ExecExitMsg) {
+               if g.cbOnExit != nil {
+                       g.cbOnExit(ev.Code, ev.Error)
+               }
+       })
+
+       return 0, nil
+}
+
+func (g *GdbXds) Close() error {
+       g.cbOnDisconnect = nil
+       g.cbOnError = nil
+       g.cbOnExit = nil
+       g.cbRead = nil
+       g.cbInferiorRead = nil
+
+       return nil
+}
+
+// Start sends a request to start remotely gdb within xds-server
+func (g *GdbXds) Start(inferiorTTY bool) (int, error) {
+       var body []byte
+       var err error
+
+       // Enable workaround about inferior output with gdbserver connection
+       // except if XDS_GDBSERVER_OUTPUT_NOFIX is defined
+       _, gdbserverNoFix := os.LookupEnv("XDS_GDBSERVER_OUTPUT_NOFIX")
+
+       args := apiv1.ExecArgs{
+               ID:              g.prjID,
+               SdkID:           g.sdkID,
+               Cmd:             g.ccmd,
+               Args:            g.aargs,
+               Env:             g.eenv,
+               RPath:           g.rPath,
+               TTY:             inferiorTTY,
+               TTYGdbserverFix: !gdbserverNoFix,
+               CmdTimeout:      -1, // no timeout, end when stdin close or command exited normally
+       }
+       body, err = json.Marshal(args)
+       if err != nil {
+               return int(syscallEBADE), err
+       }
+
+       g.log.Infof("POST %s/exec %v", g.uri, string(body))
+       var res *http.Response
+       var found bool
+       res, err = g.httpCli.HTTPPostWithRes("/exec", string(body))
+       if err != nil {
+               return int(syscall.EAGAIN), err
+       }
+       dRes := make(map[string]interface{})
+       json.Unmarshal(g.httpCli.ResponseToBArray(res), &dRes)
+       if _, found = dRes["cmdID"]; !found {
+               return int(syscallEBADE), err
+       }
+       g.cmdID = dRes["cmdID"].(string)
+
+       return 0, nil
+}
+
+// Cmd returns the command name
+func (g *GdbXds) Cmd() string {
+       return g.ccmd
+}
+
+// Args returns the list of arguments
+func (g *GdbXds) Args() []string {
+       return g.aargs
+}
+
+// Env returns the list of environment variables
+func (g *GdbXds) Env() []string {
+       return g.eenv
+}
+
+// OnError is called on a WebSocket error
+func (g *GdbXds) OnError(f func(error)) {
+       g.cbOnError = f
+}
+
+// OnDisconnect is called when WebSocket disconnection
+func (g *GdbXds) OnDisconnect(f func(error)) {
+       g.cbOnDisconnect = f
+}
+
+// OnExit calls when exit event is received
+func (g *GdbXds) 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 *GdbXds) 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 *GdbXds) InferiorRead(f func(timestamp, stdout, stderr string)) {
+       g.cbInferiorRead = f
+}
+
+// Write writes message/string into gdb stdin
+func (g *GdbXds) Write(args ...interface{}) error {
+       return g.ioSock.Emit(apiv1.ExecInEvent, args...)
+}
+
+// SendSignal is used to send a signal to remote process/gdb
+func (g *GdbXds) SendSignal(sig os.Signal) error {
+       var body []byte
+       body, err := json.Marshal(apiv1.ExecSignalArgs{
+               CmdID:  g.cmdID,
+               Signal: sig.String(),
+       })
+       if err != nil {
+               g.log.Errorf(err.Error())
+       }
+       g.log.Debugf("POST /signal %s", string(body))
+       return g.httpCli.HTTPPost("/signal", string(body))
+}
+
+//***** Private functions *****
+
+func getProjectsList(c *common.HTTPClient, log *logrus.Logger, prjData []byte) (int, error) {
+
+       folders := xdsconfig.FoldersConfig{}
+       errMar := json.Unmarshal(prjData, &folders)
+       msg := ""
+       if errMar == nil {
+               msg += "List of existing projects (use: export XDS_PROJECT_ID=<< ID >>): \n"
+               msg += "  ID\t\t\t\t | Label"
+               for _, f := range folders {
+                       msg += fmt.Sprintf("\n  %s\t | %s", f.ID, f.Label)
+                       if f.DefaultSdk != "" {
+                               msg += fmt.Sprintf("\t(default SDK: %s)", f.DefaultSdk)
+                       }
+               }
+               msg += "\n"
+       }
+
+       var data []byte
+       if err := c.HTTPGet("/sdks", &data); err != nil {
+               return int(syscallEBADE), err
+       }
+       log.Infof("Result of /sdks: %v", string(data[:]))
+
+       sdks := []crosssdk.SDK{}
+       errMar = json.Unmarshal(data, &sdks)
+       if errMar == nil {
+               msg += "\nList of installed cross SDKs (use: export XDS_SDK_ID=<< ID >>): \n"
+               msg += "  ID\t\t\t\t\t | NAME\n"
+               for _, s := range sdks {
+                       msg += fmt.Sprintf("  %s\t | %s\n", s.ID, s.Name)
+               }
+       }
+
+       if len(folders) > 0 && len(sdks) > 0 {
+               msg += fmt.Sprintf("\n")
+               msg += fmt.Sprintf("For example: \n")
+               msg += fmt.Sprintf("  XDS_PROJECT_ID=%q XDS_SDK_ID=%q  %s -x myGdbConf.ini\n",
+                       folders[0].ID, sdks[0].ID, AppName)
+       }
+
+       return 0, fmt.Errorf(msg)
+}
diff --git a/glide.yaml b/glide.yaml
new file mode 100644 (file)
index 0000000..f368862
--- /dev/null
@@ -0,0 +1,23 @@
+package: github.com/iotbzh/xds-gdb
+license: Apache-2.0
+owners:
+- name: Sebastien Douheret
+  email: sebastien@iot.bzh
+import:
+- package: github.com/codegangsta/cli
+  version: ^1.19.1
+- package: github.com/Sirupsen/logrus
+  version: ^0.11.5
+- package: github.com/zhouhui8915/go-socket.io-client
+- package: github.com/iotbzh/xds-server
+  subpackages:
+  - apiv1
+  - crosssdk
+  - xdsconfig
+- package: github.com/iotbzh/xds-common
+  subpackages:
+  - golib/common
+- package: github.com/joho/godotenv
+  version: ^1.1.0
+  subpackages:
+  - cmd/godotenv
diff --git a/main.go b/main.go
new file mode 100644 (file)
index 0000000..01b46c9
--- /dev/null
+++ b/main.go
@@ -0,0 +1,578 @@
+// xds-gdb: a wrapper on gdb tool for X(cross) Development System.
+package main
+
+import (
+       "bufio"
+       "fmt"
+       "io/ioutil"
+       "os"
+       "os/signal"
+       "os/user"
+       "syscall"
+       "time"
+
+       "strings"
+
+       "path"
+
+       "github.com/Sirupsen/logrus"
+       "github.com/codegangsta/cli"
+       common "github.com/iotbzh/xds-common/golib"
+       "github.com/joho/godotenv"
+)
+
+var appAuthors = []cli.Author{
+       cli.Author{Name: "Sebastien Douheret", Email: "sebastien@iot.bzh"},
+}
+
+// AppName name of this application
+var AppName = "xds-gdb"
+
+// AppVersion Version of this application
+// (set by Makefile)
+var AppVersion = "?.?.?"
+
+// AppSubVersion is the git tag id added to version string
+// Should be set by compilation -ldflags "-X main.AppSubVersion=xxx"
+// (set by Makefile)
+var AppSubVersion = "unknown-dev"
+
+// Create logger
+var log = logrus.New()
+var earlyLog = []string{}
+
+// Application details
+const (
+       appCopyright    = "Apache-2.0"
+       defaultLogLevel = "warning"
+)
+
+// Exit events
+type exitResult struct {
+       error error
+       code  int
+}
+
+// EnvVar - Environment variables used by application
+type EnvVar struct {
+       Name        string
+       Usage       string
+       Destination *string
+}
+
+// exitError terminates this program with the specified error
+func exitError(code syscall.Errno, f string, a ...interface{}) {
+       err := fmt.Sprintf(f, a...)
+       fmt.Fprintf(os.Stderr, err+"\n")
+       os.Exit(int(code))
+}
+
+// main
+func main() {
+       var uri, prjID, rPath, logLevel, logFile, sdkid, confFile, gdbNative string
+       var listProject bool
+       var err error
+
+       uri = "localhost:8000"
+       logLevel = defaultLogLevel
+
+       // Create a new App instance
+       app := cli.NewApp()
+       app.Name = AppName
+       app.Usage = "wrapper on gdb for X(cross) Development System."
+       app.Version = AppVersion + " (" + AppSubVersion + ")"
+       app.Authors = appAuthors
+       app.Copyright = appCopyright
+       app.Metadata = make(map[string]interface{})
+       app.Metadata["version"] = AppVersion
+       app.Metadata["git-tag"] = AppSubVersion
+       app.Metadata["logger"] = log
+
+       app.Flags = []cli.Flag{
+               cli.BoolFlag{
+                       Name:        "list, ls",
+                       Usage:       "list existing xds projects",
+                       Destination: &listProject,
+               },
+       }
+
+       appEnvVars := []EnvVar{
+               EnvVar{
+                       Name:        "XDS_CONFIG",
+                       Usage:       "env config file to source on startup",
+                       Destination: &confFile,
+               },
+               EnvVar{
+                       Name:        "XDS_LOGLEVEL",
+                       Usage:       "logging level (supported levels: panic, fatal, error, warn, info, debug)",
+                       Destination: &logLevel,
+               },
+               EnvVar{
+                       Name:        "XDS_LOGFILE",
+                       Usage:       "logging file",
+                       Destination: &logFile,
+               },
+               EnvVar{
+                       Name:        "XDS_NATIVE_GDB",
+                       Usage:       "use native gdb instead of remote XDS server",
+                       Destination: &gdbNative,
+               },
+               EnvVar{
+                       Name:        "XDS_PROJECT_ID",
+                       Usage:       "project ID you want to build (mandatory variable)",
+                       Destination: &prjID,
+               },
+               EnvVar{
+                       Name:        "XDS_RPATH",
+                       Usage:       "relative path into project",
+                       Destination: &rPath,
+               },
+               EnvVar{
+                       Name:        "XDS_SDK_ID",
+                       Usage:       "Cross Sdk ID to use to build project",
+                       Destination: &sdkid,
+               },
+               EnvVar{
+                       Name:        "XDS_SERVER_URL",
+                       Usage:       "remote XDS server url",
+                       Destination: &uri,
+               },
+       }
+
+       // Process gdb arguments
+       args := make([]string, len(os.Args))
+       args[0] = os.Args[0]
+       gdbArgs := make([]string, len(os.Args))
+
+       // Split xds-xxx options from gdb options
+       copy(gdbArgs, os.Args[1:])
+       for idx, a := range os.Args[1:] {
+               // Specific case to print help or version of xds-gdb
+               switch a {
+               case "--help", "-h", "--version", "-v":
+                       args[1] = a
+                       goto endloop
+               case "--":
+                       // Detect skip option (IOW '--') to split arguments
+                       copy(args, os.Args[0:idx+1])
+                       copy(gdbArgs, os.Args[idx+2:])
+                       goto endloop
+               }
+       }
+endloop:
+
+       // Parse gdb arguments to detect:
+       //  --tty option: used for inferior/ tty of debugged program
+       //  -x/--command option: XDS env vars may be set within gdb command file
+       clientPty := ""
+       gdbCmdFile := ""
+       for idx, a := range gdbArgs {
+               switch {
+               case strings.HasPrefix(a, "--tty="):
+                       clientPty = a[len("--tty="):]
+                       gdbArgs[idx] = ""
+
+               case a == "--tty":
+               case strings.HasPrefix(a, "-tty"):
+                       clientPty = gdbArgs[idx+1]
+                       gdbArgs[idx] = ""
+                       gdbArgs[idx+1] = ""
+
+               case strings.HasPrefix(a, "--command="):
+                       gdbCmdFile = a[len("--command="):]
+
+               case a == "--command":
+               case strings.HasPrefix(a, "-x"):
+                       gdbCmdFile = gdbArgs[idx+1]
+               }
+       }
+
+       // Source config env file
+       // (we cannot use confFile var because env variables setting is just after)
+       envMap, confFile, err := loadConfigEnvFile(os.Getenv("XDS_CONFIG"), gdbCmdFile)
+       if err != nil {
+               exitError(syscall.ENOENT, err.Error())
+       }
+
+       // Managed env vars and create help
+       dynDesc := "\nENVIRONMENT VARIABLES:"
+       for _, ev := range appEnvVars {
+               dynDesc += fmt.Sprintf("\n %s \t\t %s", ev.Name, ev.Usage)
+               if evVal, evExist := os.LookupEnv(ev.Name); evExist && ev.Destination != nil {
+                       *ev.Destination = evVal
+               }
+       }
+       app.Description = "gdb wrapper for X(cross) Development System\n"
+       app.Description += "\n"
+       app.Description += " Two debugging models are supported:\n"
+       app.Description += "  - xds remote debugging requiring an XDS server and allowing cross debug\n"
+       app.Description += "  - native debugging\n"
+       app.Description += " By default xds remote debug is used and you need to define XDS_NATIVE_GDB to\n"
+       app.Description += " use native gdb debug mode instead.\n"
+       app.Description += "\n"
+       app.Description += " xds-gdb configuration (see variables list below) can be set using:\n"
+       app.Description += "  - a config file (XDS_CONFIG)\n"
+       app.Description += "  - or environment variables\n"
+       app.Description += "  - or by setting variables within gdb ini file (commented line including :XDS-ENV: tag)\n"
+       app.Description += "    Example of gdb ini file where we define project and sdk ID:\n"
+       app.Description += "     # :XDS-ENV: XDS_PROJECT_ID=IW7B4EE-DBY4Z74_myProject\n"
+       app.Description += "     # :XDS-ENV: XDS_SDK_ID=poky-agl_aarch64_3.99.1+snapshot\n"
+       app.Description += "\n"
+       app.Description += dynDesc + "\n"
+
+       // only one action
+       app.Action = func(ctx *cli.Context) error {
+               var err error
+               curDir, _ := os.Getwd()
+
+               // Set logger level, formatter and log file
+               if log.Level, err = logrus.ParseLevel(logLevel); err != nil {
+                       msg := fmt.Sprintf("Invalid log level : \"%v\"\n", logLevel)
+                       return cli.NewExitError(msg, int(syscall.EINVAL))
+               }
+               log.Formatter = &logrus.TextFormatter{}
+
+               // Always log into a file
+               if logFile == "" {
+                       logFile = "/tmp/xds-gdb.log"
+               }
+               fdL, err := os.OpenFile(logFile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
+               if err != nil {
+                       msgErr := fmt.Sprintf("Cannot create log file %s", logFile)
+                       return cli.NewExitError(msgErr, int(syscall.EPERM))
+               }
+               log.Out = fdL
+
+               // Build env variables
+               env := []string{}
+               for k, v := range envMap {
+                       env = append(env, k+"="+v)
+               }
+
+               // Create cross or native gdb interface
+               var gdb IGDB
+               if gdbNative != "" {
+                       gdb = NewGdbNative(log, gdbArgs, env)
+               } else {
+                       gdb = NewGdbXds(log, gdbArgs, env)
+                       gdb.SetConfig("uri", uri)
+                       gdb.SetConfig("prjID", prjID)
+                       gdb.SetConfig("sdkID", sdkid)
+                       gdb.SetConfig("rPath", rPath)
+                       gdb.SetConfig("listProject", listProject)
+               }
+
+               // Log early print
+               for _, msg := range earlyLog {
+                       log.Debugf(msg)
+               }
+
+               // Log useful info
+               log.Infof("Original arguments: %v", os.Args)
+               log.Infof("Current directory : %v", curDir)
+               log.Infof("Use confFile      : '%s'", confFile)
+               log.Infof("Execute           : /exec %v %v", gdb.Cmd(), gdb.Args())
+
+               // Properly report invalid init file error
+               gdbCommandFileError := ""
+               for i, a := range gdbArgs {
+                       if a == "-x" {
+                               gdbCommandFileError = gdbArgs[i+1] + ": No such file or directory."
+                               break
+                       } else if strings.HasPrefix(a, "--command=") {
+                               gdbCommandFileError = strings.TrimLeft(a, "--command=") + ": No such file or directory."
+                               break
+                       }
+               }
+               log.Infof("Add detection of error: <%s>", gdbCommandFileError)
+
+               // Init gdb subprocess management
+               if code, err := gdb.Init(); err != nil {
+                       return cli.NewExitError(err.Error(), code)
+               }
+
+               exitChan := make(chan exitResult, 1)
+
+               gdb.OnError(func(err error) {
+                       fmt.Println("ERROR: ", err.Error())
+               })
+
+               gdb.OnDisconnect(func(err error) {
+                       fmt.Println("Disconnection: ", err.Error())
+                       exitChan <- exitResult{err, int(syscall.ESHUTDOWN)}
+               })
+
+               gdb.Read(func(timestamp, stdout, stderr string) {
+                       if stdout != "" {
+                               fmt.Printf("%s", stdout)
+                               log.Debugf("Recv OUT: <%s>", stdout)
+                       }
+                       if stderr != "" {
+                               fmt.Fprintf(os.Stderr, "%s", stderr)
+                               log.Debugf("Recv ERR: <%s>", stderr)
+                       }
+
+                       // Correctly report error about init file
+                       if gdbCommandFileError != "" && strings.Contains(stdout, gdbCommandFileError) {
+                               fmt.Fprintf(os.Stderr, "ERROR: "+gdbCommandFileError)
+                               log.Errorf("ERROR: " + gdbCommandFileError)
+                               if err := gdb.SendSignal(syscall.SIGTERM); err != nil {
+                                       log.Errorf("Error while sending signal: %s", err.Error())
+                               }
+                               exitChan <- exitResult{err, int(syscall.ENOENT)}
+                       }
+               })
+
+               gdb.OnExit(func(code int, err error) {
+                       exitChan <- exitResult{err, code}
+               })
+
+               // Handle client tty / pts
+               if clientPty != "" {
+                       log.Infoln("Client tty detected: %v\n", clientPty)
+
+                       cpFd, err := os.OpenFile(clientPty, os.O_RDWR, 0)
+                       if err != nil {
+                               return cli.NewExitError(err.Error(), int(syscall.EPERM))
+                       }
+                       defer cpFd.Close()
+
+                       // client tty stdin
+                       /* XXX TODO - implement stdin to send data to debugged program
+                       go func() {
+                               reader := bufio.NewReader(cpFd)
+                               sc := bufio.NewScanner(reader)
+                               for sc.Scan() {
+                                       data := sc.Text()
+                                       iosk.Emit(apiv1.ExecInferiorInEvent, data+"\n")
+                                       log.Debugf("Inferior IN: <%v>", data)
+                               }
+                               if sc.Err() != nil {
+                                       log.Warnf("Inferior Stdin scanner exit, close stdin (err=%v)", sc.Err())
+                               }
+                       }()
+                       */
+
+                       // client tty stdout
+                       gdb.InferiorRead(func(timestamp, stdout, stderr string) {
+                               if stdout != "" {
+                                       fmt.Fprintf(cpFd, "%s", stdout)
+                                       log.Debugf("Inferior OUT: <%s>", stdout)
+                               }
+                               if stderr != "" {
+                                       fmt.Fprintf(cpFd, "%s", stderr)
+                                       log.Debugf("Inferior ERR: <%s>", stderr)
+                               }
+                       })
+               }
+
+               // Allow to overwrite some gdb commands
+               var overwriteMap = make(map[string]string)
+               if overEnv, exist := os.LookupEnv("XDS_OVERWRITE_COMMANDS"); exist {
+                       overEnvS := strings.TrimSpace(overEnv)
+                       if len(overEnvS) > 0 {
+                               // Extract overwrite commands from env variable
+                               for _, def := range strings.Split(overEnvS, ",") {
+                                       if kv := strings.Split(def, ":"); len(kv) == 2 {
+                                               overwriteMap[strings.TrimSpace(kv[0])] = strings.TrimSpace(kv[1])
+                                       } else {
+                                               return cli.NewExitError(
+                                                       fmt.Errorf("Invalid definition in XDS_OVERWRITE_COMMANDS (%s)", def),
+                                                       int(syscall.EINVAL))
+                                       }
+                               }
+                       }
+               } else {
+                       overwriteMap["-exec-run"] = "-exec-continue"
+                       overwriteMap["-file-exec-and-symbols"] = "-file-exec-file"
+               }
+               log.Debugf("overwriteMap = %v", overwriteMap)
+
+               // Send stdin though WS
+               go func() {
+                       paranoia := 600
+                       reader := bufio.NewReader(os.Stdin)
+
+                       for {
+                               sc := bufio.NewScanner(reader)
+                               for sc.Scan() {
+                                       command := sc.Text()
+
+                                       // overwrite some commands
+                                       for key, value := range overwriteMap {
+                                               if strings.Contains(command, key) {
+                                                       command = strings.Replace(command, key, value, 1)
+                                                       log.Debugf("OVERWRITE %s -> %s", key, value)
+                                               }
+                                       }
+                                       gdb.Write(command + "\n")
+                                       log.Debugf("Send: <%v>", command)
+                               }
+                               log.Infof("Stdin scanner exit, close stdin (err=%v)", sc.Err())
+
+                               // CTRL-D exited scanner, so send it explicitly
+                               gdb.Write("\x04")
+                               time.Sleep(time.Millisecond * 100)
+
+                               if paranoia--; paranoia <= 0 {
+                                       msg := "Abnormal loop detected on stdin"
+                                       log.Errorf("Abnormal loop detected on stdin")
+                                       gdb.SendSignal(syscall.SIGTERM)
+                                       exitChan <- exitResult{fmt.Errorf(msg), int(syscall.ELOOP)}
+                               }
+                       }
+               }()
+
+               // Handling all Signals
+               sigs := make(chan os.Signal, 1)
+               signal.Notify(sigs)
+
+               go func() {
+                       for {
+                               sig := <-sigs
+                               if err := gdb.SendSignal(sig); err != nil {
+                                       log.Errorf("Error while sending signal: %s", err.Error())
+                               }
+                       }
+               }()
+
+               // Start gdb
+               if code, err := gdb.Start(clientPty != ""); err != nil {
+                       return cli.NewExitError(err.Error(), code)
+               }
+
+               // Wait exit
+               select {
+               case res := <-exitChan:
+                       errStr := ""
+                       if res.code == 0 {
+                               log.Infoln("Exit successfully")
+                       }
+                       if res.error != nil {
+                               log.Infoln("Exit with ERROR: ", res.error.Error())
+                               errStr = res.error.Error()
+                       }
+                       return cli.NewExitError(errStr, res.code)
+               }
+       }
+
+       app.Run(args)
+}
+
+// loadConfigEnvFile
+func loadConfigEnvFile(confFile, gdbCmdFile string) (map[string]string, string, error) {
+       var err error
+       envMap := make(map[string]string)
+
+       // 1- if no confFile set, use setting from gdb command file is option
+       //    --command/-x is set
+       if confFile == "" && gdbCmdFile != "" {
+               logEarly("Try extract config from gdbCmdFile: %s", gdbCmdFile)
+               confFile, err = extractEnvFromCmdFile(gdbCmdFile)
+               if confFile != "" {
+                       defer os.Remove(confFile)
+               }
+               if err != nil {
+                       return envMap, confFile, fmt.Errorf(err.Error())
+               }
+       }
+       // 2- search xds-gdb.env file in various locations
+       if confFile == "" {
+               curDir, _ := os.Getwd()
+               if u, err := user.Current(); err == nil {
+                       xdsEnvFile := "xds-gdb.env"
+                       for _, d := range []string{
+                               path.Join(curDir),
+                               path.Join(curDir, "..", ".."),
+                               path.Join(curDir, "../../target"),
+                               path.Join(u.HomeDir, ".xds"),
+                       } {
+                               confFile := path.Join(d, xdsEnvFile)
+                               logEarly("Search config in %s", confFile)
+                               if common.Exists(confFile) {
+                                       break
+                               }
+                       }
+               }
+       }
+
+       if confFile == "" {
+               return envMap, "", nil
+       }
+
+       if !common.Exists(confFile) {
+               return envMap, confFile, fmt.Errorf("Error no env config file not found")
+       }
+       if err = godotenv.Load(confFile); err != nil {
+               return envMap, confFile, fmt.Errorf("Error loading env config file " + confFile)
+       }
+       if envMap, err = godotenv.Read(confFile); err != nil {
+               return envMap, confFile, fmt.Errorf("Error reading env config file " + confFile)
+       }
+       return envMap, confFile, nil
+}
+
+/*
+ extractEnvFromCmdFile: extract xds-gdb env variable from gdb command file
+  All commented lines (#) in gdb command file that start with ':XDS-ENV:' prefix
+  will be considered as XDS env commands. For example the 3 syntaxes below
+  are supported:
+  # :XDS-ENV: XDS_PROJECT_ID=IW7B4EE-DBY4Z74_myProject
+  #:XDS-ENV:XDS_SDK_ID=poky-agl_aarch64_3.99.1+snapshot
+  # :XDS-ENV:  export XDS_SERVER_URL=localhost:8800
+*/
+func extractEnvFromCmdFile(cmdFile string) (string, error) {
+       if !common.Exists(cmdFile) {
+               return "", nil
+       }
+       cFd, err := os.Open(cmdFile)
+       if err != nil {
+               return "", fmt.Errorf("Cannot open %s : %s", cmdFile, err.Error())
+       }
+       defer cFd.Close()
+
+       var lines []string
+       scanner := bufio.NewScanner(cFd)
+       for scanner.Scan() {
+               lines = append(lines, scanner.Text())
+       }
+       if err = scanner.Err(); err != nil {
+               return "", fmt.Errorf("Cannot parse %s : %s", cmdFile, err.Error())
+       }
+
+       envFile, err := ioutil.TempFile("", "xds-gdb_env.ini")
+       if err != nil {
+               return "", fmt.Errorf("Error while creating temporary env file: %s", err.Error())
+       }
+       envFileName := envFile.Name()
+       defer envFile.Close()
+
+       envFound := false
+       for _, ln := range lines {
+               ln = strings.TrimSpace(ln)
+               if strings.HasPrefix(ln, "#") && strings.Contains(ln, ":XDS-ENV:") {
+                       env := strings.SplitAfterN(ln, ":XDS-ENV:", 2)
+                       if len(env) == 2 {
+                               envFound = true
+                               if _, err := envFile.WriteString(strings.TrimSpace(env[1]) + "\n"); err != nil {
+                                       return "", fmt.Errorf("Error write into temporary env file: %s", err.Error())
+                               }
+                       } else {
+                               log.Warnf("Error while decoding line %s", ln)
+                       }
+               }
+       }
+
+       if !envFound {
+               ff := envFileName
+               defer os.Remove(ff)
+               envFileName = ""
+
+       }
+
+       return envFileName, nil
+}
+
+func logEarly(format string, a ...interface{}) {
+       earlyLog = append(earlyLog, fmt.Sprintf(format, a...))
+}