aboutsummaryrefslogtreecommitdiffstats
path: root/cmd-target.go
diff options
context:
space:
mode:
authorSebastien Douheret <sebastien.douheret@iot.bzh>2018-03-09 17:33:18 +0100
committerSebastien Douheret <sebastien.douheret@iot.bzh>2018-03-09 17:33:18 +0100
commit00b5b83dcff4904aeb18760caa193fa3393241e0 (patch)
tree447d4d5249e659de8c0a062559bb58f739220322 /cmd-target.go
parent04040c928142db92d2ef2d4b43ad4701392e5ceb (diff)
Fixed terminal output (support escape and control characters)
Signed-off-by: Sebastien Douheret <sebastien.douheret@iot.bzh>
Diffstat (limited to 'cmd-target.go')
-rw-r--r--cmd-target.go120
1 files changed, 80 insertions, 40 deletions
diff --git a/cmd-target.go b/cmd-target.go
index db97e91..cd96ed4 100644
--- a/cmd-target.go
+++ b/cmd-target.go
@@ -19,18 +19,17 @@
package main
import (
- "bufio"
"encoding/json"
"fmt"
+ "io"
"os"
"sort"
"strings"
- "syscall"
"time"
- "github.com/golang/crypto/ssh/terminal"
-
"gerrit.automotivelinux.org/gerrit/src/xds/xds-agent.git/lib/xaapiv1"
+ "github.com/creack/goselect"
+ "github.com/golang/crypto/ssh/terminal"
"github.com/urfave/cli"
)
@@ -198,6 +197,7 @@ func _displayTargets(tgts []xaapiv1.TargetConfig, verbose bool) {
for _, tt := range tgt.Terms {
tmNfo += "\t ID:\t" + tt.ID + "\n"
tmNfo += "\t Name:\t" + tt.Name + "\n"
+ tmNfo += "\t Type:\t" + string(tt.Type) + "\n"
tmNfo += "\t Status:\t" + tt.Status + "\n"
tmNfo += "\t User:\t" + tt.User + "\n"
tmNfo += "\t Options:\t" + strings.Join(tt.Options, " ") + "\n"
@@ -324,60 +324,88 @@ func terminalOpen(ctx *cli.Context) error {
}
exitChan := make(chan exitResult, 1)
- IOsk.On("disconnection", func(err error) {
+ IOSkClient.On("disconnection", func(err error) {
Log.Debugf("WS disconnection event with err: %v\n", err)
exitChan <- exitResult{err, 2}
})
- IOsk.On(xaapiv1.TerminalOutEvent, func(ev xaapiv1.TerminalOutMsg) {
- if ev.Stdout != "" {
- fmt.Printf(ev.Stdout)
+ IOSkClient.On(xaapiv1.TerminalOutEvent, func(ev xaapiv1.TerminalOutMsg) {
+ if len(ev.Stdout) > 0 {
+ os.Stdout.Write(ev.Stdout)
}
- if ev.Stderr != "" {
- fmt.Fprintf(os.Stderr, ev.Stderr)
+ if len(ev.Stderr) > 0 {
+ os.Stderr.Write(ev.Stdout)
}
})
- IOsk.On(xaapiv1.TerminalExitEvent, func(ev xaapiv1.TerminalExitMsg) {
+ IOSkClient.On(xaapiv1.TerminalExitEvent, func(ev xaapiv1.TerminalExitMsg) {
exitChan <- exitResult{ev.Error, ev.Code}
})
- /* FIXME - use raw mode to support escape keys, arrows keys, control char...
- // import "github.com/golang/crypto/ssh/terminal"
-
+ // Setup terminal (raw mode to handle escape and control keys)
+ if !terminal.IsTerminal(0) || !terminal.IsTerminal(1) {
+ return cli.NewExitError("stdin/stdout should be terminal", 1)
+ }
oldState, err := terminal.MakeRaw(int(os.Stdin.Fd()))
- if err == nil {
- defer terminal.Restore(int(os.Stdin.Fd()), oldState)
+ if err != nil {
+ return cli.NewExitError(err.Error(), 1)
}
- */
+ defer terminal.Restore(int(os.Stdin.Fd()), oldState)
// Send stdin though WS
go func() {
- paranoia := 600
- reader := bufio.NewReader(os.Stdin)
+ type exposeFd interface {
+ Fd() uintptr
+ }
+ buff := make([]byte, 128)
+ rdfs := &goselect.FDSet{}
+ reader := io.ReadCloser(os.Stdin)
+ defer reader.Close()
+
for {
- sc := bufio.NewScanner(reader)
- for sc.Scan() {
- command := sc.Text()
- Log.Debugf("Terminal Send command <%v>", command)
- IOsk.Emit(xaapiv1.TerminalInEvent, command+"\n")
- }
- if sc.Err() != nil {
- exitChan <- exitResult{sc.Err(), 3}
+ rdfs.Zero()
+ rdfs.Set(reader.(exposeFd).Fd())
+ err := goselect.Select(1, rdfs, nil, nil, 50*time.Millisecond)
+ if err != nil {
+ terminal.Restore(int(os.Stdin.Fd()), oldState)
+ exitChan <- exitResult{err, 3}
+ return
}
+ if rdfs.IsSet(reader.(exposeFd).Fd()) {
+ size, err := reader.Read(buff)
+
+ if err != nil {
+ Log.Debugf("Read error %v; err %v", size, err)
+ if err == io.EOF {
+ // CTRL-D exited scanner, so send it explicitly
+ err := IOSkClient.Emit(xaapiv1.TerminalInEvent, "\x04\n")
+
+ if err != nil {
+ terminal.Restore(int(os.Stdin.Fd()), oldState)
+ exitChan <- exitResult{err, 4}
+ return
+ }
+ time.Sleep(time.Millisecond * 100)
+ continue
+ } else {
+ terminal.Restore(int(os.Stdin.Fd()), oldState)
+ exitChan <- exitResult{err, 5}
+ return
+ }
+ }
- // CTRL-D exited scanner, so send it explicitly
- IOsk.Emit(xaapiv1.TerminalInEvent, "\x04\n")
- time.Sleep(time.Millisecond * 100)
-
- if paranoia--; paranoia <= 0 {
- msg := "Abnormal loop detected on stdin"
- Log.Errorf("Abnormal loop detected on stdin")
-
- // Send signal to gently exit terminal session
- TerminalSendSignal(tgt, term, syscall.SIGTERM)
+ if size <= 0 {
+ continue
+ }
- exitChan <- exitResult{fmt.Errorf(msg), int(syscall.ELOOP)}
+ data := buff[:size]
+ LogSillyf("Terminal Send data <%v> (%s)", data, data)
+ err = IOSkClient.Emit(xaapiv1.TerminalInEvent, data)
+ if err != nil {
+ terminal.Restore(int(os.Stdin.Fd()), oldState)
+ exitChan <- exitResult{err, 6}
+ return
+ }
}
}
}()
@@ -388,7 +416,7 @@ func terminalOpen(ctx *cli.Context) error {
if IsWinResizeSignal(sig) {
TerminalResize(tgt, term)
} else if IsInterruptSignal(sig) {
- IOsk.Emit(xaapiv1.TerminalInEvent, "\x03\n")
+ IOSkClient.Emit(xaapiv1.TerminalInEvent, "\x03\n")
} else {
TerminalSendSignal(tgt, term, sig)
}
@@ -404,6 +432,9 @@ func terminalOpen(ctx *cli.Context) error {
return cli.NewExitError(err.Error(), 1)
}
+ // Send init size
+ TerminalResize(tgt, term)
+
// Wait exit - blocking
select {
case res := <-exitChan:
@@ -452,7 +483,8 @@ func TerminalResize(tgt *xaapiv1.TargetConfig, term *xaapiv1.TerminalConfig) {
if err != nil {
Log.Errorf("Error cannot get terminal size: %v", err)
}
- Log.Debugf("Terminal resizing rows %v, cols %v", row, col)
+
+ LogSillyf("Terminal resizing rows %v, cols %v", row, col)
sz := xaapiv1.TerminalResizeArgs{Rows: uint16(row), Cols: uint16(col)}
url := XdsServerComputeURL("/targets/" + tgt.ID + "/terminals/" + term.ID + "/resize")
if err := HTTPCli.Post(url, &sz, nil); err != nil {
@@ -514,6 +546,14 @@ func GetTargetAndTerminalIDs(ctx *cli.Context, useFirstFree bool) (*xaapiv1.Targ
for _, tt := range tgts {
if compareID(tt.ID, idArg) {
+ if useFirstFree {
+ for _, ttm := range tt.Terms {
+ if ttm.Type == xaapiv1.TypeTermSSH &&
+ (ttm.Status == xaapiv1.StatusTermEnable || ttm.Status == xaapiv1.StatusTermClose) {
+ return &tt, &ttm, nil
+ }
+ }
+ }
return &tt, nil, nil
}
}