From 2231e3eae86c5e3ae05e30f667d331f5875c7884 Mon Sep 17 00:00:00 2001 From: Sebastien Douheret Date: Fri, 23 Feb 2018 22:13:22 +0100 Subject: [PATCH] Added target and terminal support Signed-off-by: Sebastien Douheret --- .gitignore | 1 + .vscode/launch.json | 16 ++ .vscode/settings.json | 4 +- cmd-projects.go | 10 +- cmd-target.go | 546 ++++++++++++++++++++++++++++++++++++++++++++++++++ glide.yaml | 5 +- main.go | 5 + signals.go | 70 +++++++ signals_other_arch.go | 45 +++++ signals_windows.go | 43 ++++ utils.go | 15 +- 11 files changed, 751 insertions(+), 9 deletions(-) create mode 100644 cmd-target.go create mode 100644 signals.go create mode 100644 signals_other_arch.go create mode 100644 signals_windows.go diff --git a/.gitignore b/.gitignore index 2f4d998..27f9d12 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,6 @@ glide.lock bin/** tools/** vendor/** +package/** *.zip __* diff --git a/.vscode/launch.json b/.vscode/launch.json index f5ca868..1fc4ed1 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -81,6 +81,22 @@ }, "args": ["-c", "xds-config-sample.env", "sdks", "ls"], "showLog": false + }, + { + "name": "xds-cli (terminal)", + "type": "go", + "request": "launch", + "mode": "debug", + "program": "${workspaceRoot}", + "env": { + "GOPATH": "${workspaceRoot}/../../../../../..:${env:GOPATH}", + "XDS_APPNAME": "xds-cli", + "XDS_AGENT_URL": "localhost:8800", + "XDS_LOGLEVEL": "debug" + }, + "args": ["targets", "term", "-tid", "10bd", "-u", "root" ], + //"args": ["targets", "term-rm", "e31ad288-18ab-11e8-8afa-3c970e49ad9b" ], + "showLog": false } ] diff --git a/.vscode/settings.json b/.vscode/settings.json index d78342b..fc15f8b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -37,6 +37,8 @@ "reflectme", "franciscocpg", "gerrit", - "EVTSDK" + "EVTSDK", + "tgts", + "sigs" ] } diff --git a/cmd-projects.go b/cmd-projects.go index 1aa0dfc..e056563 100644 --- a/cmd-projects.go +++ b/cmd-projects.go @@ -63,7 +63,7 @@ func initCmdProjects(cmdDef *[]cli.Command) { }, { Name: "get", - Usage: "Get a property of a project", + Usage: "Get properties of a project", Action: projectsGet, Flags: []cli.Flag{ cli.StringFlag{ @@ -180,12 +180,12 @@ func _displayProjects(prjs []xaapiv1.ProjectConfig, verbose bool) { func projectsAdd(ctx *cli.Context) error { // Decode project type - var ptype xaapiv1.ProjectType + var pType xaapiv1.ProjectType switch strings.ToLower(ctx.String("type")) { case "pathmap", "pm": - ptype = xaapiv1.TypePathMap + pType = xaapiv1.TypePathMap case "cloudsync", "cs": - ptype = xaapiv1.TypeCloudSync + pType = xaapiv1.TypeCloudSync default: return cli.NewExitError("Unknown project type", 1) } @@ -193,7 +193,7 @@ func projectsAdd(ctx *cli.Context) error { prj := xaapiv1.ProjectConfig{ ServerID: XdsServerIDGet(), Label: ctx.String("label"), - Type: ptype, + Type: pType, ClientPath: ctx.String("path"), ServerPath: ctx.String("server-path"), } diff --git a/cmd-target.go b/cmd-target.go new file mode 100644 index 0000000..688aef6 --- /dev/null +++ b/cmd-target.go @@ -0,0 +1,546 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author Sebastien Douheret + * + * 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. + * + */ + +package main + +import ( + "bufio" + "encoding/json" + "fmt" + "os" + "sort" + "strings" + "syscall" + "time" + + "github.com/golang/crypto/ssh/terminal" + + "gerrit.automotivelinux.org/gerrit/src/xds/xds-agent.git/lib/xaapiv1" + "github.com/urfave/cli" +) + +func initCmdTargets(cmdDef *[]cli.Command) { + *cmdDef = append(*cmdDef, cli.Command{ + Name: "targets", + Aliases: []string{"tgt"}, + HideHelp: true, + Usage: "targets commands group", + Subcommands: []cli.Command{ + { + Name: "add", + Aliases: []string{"a"}, + Usage: "Add a new target", + Action: targetsAdd, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "name, n", + Usage: "target name (free form string)", + }, + cli.StringFlag{ + Name: "ip", + Usage: "IP address", + }, + cli.BoolFlag{ + Name: "short, s", + Usage: "short output, only print create target id (useful from scripting)", + }, + cli.StringFlag{ + Name: "type, t", + Usage: "target type (standard|std)", + }, + }, + }, + { + Name: "get", + Usage: "Get properties of a target", + Action: targetsGet, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "id", + Usage: "target id", + EnvVar: "XDS_TARGET_ID", + }, + }, + }, + { + Name: "list", + Aliases: []string{"ls"}, + Usage: "List existing targets", + Action: targetsList, + Flags: []cli.Flag{ + cli.BoolFlag{ + Name: "verbose, v", + Usage: "display verbose output", + }, + }, + }, + { + Name: "remove", + Aliases: []string{"rm"}, + Usage: "Remove an existing target", + Action: targetsRemove, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "id", + Usage: "target id", + EnvVar: "XDS_TARGET_ID", + }, + cli.BoolFlag{ + Name: "force, f", + Usage: "remove confirmation prompt before removal", + }, + }, + }, + { + Name: "terminal", + Aliases: []string{"term"}, + Usage: "Open a target terminal", + Action: terminalOpen, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "id", + Usage: "target id", + EnvVar: "XDS_TARGET_ID", + }, + cli.StringSliceFlag{ + Name: "options, o", + Usage: "passthrough options set to command line used to start terminal", + }, + cli.StringFlag{ + Name: "termId, tid", + Usage: "terminal id", + EnvVar: "XDS_TERMINAL_ID", + }, + cli.StringFlag{ + Name: "user, u", + Usage: "user name used to connect terminal", + EnvVar: "XDS_TERMINAL_USER", + }, + }, + }, + { + Name: "terminal-remove", + Aliases: []string{"term-rm"}, + Usage: "Remove a target terminal", + Action: terminalRemove, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "id", + Usage: "target id", + EnvVar: "XDS_TARGET_ID", + }, + cli.StringFlag{ + Name: "termId, tid", + Usage: "terminal id", + EnvVar: "XDS_TERMINAL_ID", + }, + }, + }, + }, + }) +} + +func targetsList(ctx *cli.Context) error { + // Get targets list + tgts := []xaapiv1.TargetConfig{} + if err := TargetsListGet(&tgts); err != nil { + return cli.NewExitError(err.Error(), 1) + } + _displayTargets(tgts, ctx.Bool("verbose")) + return nil +} + +func targetsGet(ctx *cli.Context) error { + id := GetID(ctx) + if id == "" { + return cli.NewExitError("id parameter or option must be set", 1) + } + tgts := make([]xaapiv1.TargetConfig, 1) + url := XdsServerComputeURL("/targets/" + id) + if err := HTTPCli.Get(url, &tgts[0]); err != nil { + return cli.NewExitError(err, 1) + } + _displayTargets(tgts, true) + return nil +} + +func _displayTargets(tgts []xaapiv1.TargetConfig, verbose bool) { + // Display result + first := true + writer := NewTableWriter() + for _, tgt := range tgts { + if verbose { + if !first { + fmt.Fprintln(writer) + } + fmt.Fprintln(writer, "ID:\t", tgt.ID) + fmt.Fprintln(writer, "Name:\t", tgt.Name) + fmt.Fprintln(writer, "Type:\t", tgt.Type) + fmt.Fprintln(writer, "IP:\t", tgt.IP) + fmt.Fprintln(writer, "Status:\t", tgt.Status) + if len(tgt.Terms) > 0 { + tmNfo := "\t\n" + for _, tt := range tgt.Terms { + tmNfo += "\t ID:\t" + tt.ID + "\n" + tmNfo += "\t Name:\t" + tt.Name + "\n" + tmNfo += "\t Status:\t" + tt.Status + "\n" + tmNfo += "\t User:\t" + tt.User + "\n" + tmNfo += "\t Options:\t" + strings.Join(tt.Options, " ") + "\n" + tmNfo += fmt.Sprintf("\t Size:\t%v x %v\n", tt.Cols, tt.Rows) + } + fmt.Fprintln(writer, "Terminals:", tmNfo) + } else { + fmt.Fprintln(writer, "Terminals:\t None") + } + + } else { + if first { + fmt.Fprintln(writer, "ID\t Name\t IP\t Terminals #") + } + fmt.Fprintln(writer, tgt.ID[0:8], "\t", tgt.Name, "\t", tgt.IP, "\t", len(tgt.Terms)) + } + first = false + } + writer.Flush() +} + +func targetsAdd(ctx *cli.Context) error { + + // Decode target type + var tType xaapiv1.TargetType + switch strings.ToLower(ctx.String("type")) { + case "standard", "std": + tType = xaapiv1.TypeTgtStandard + default: + tType = xaapiv1.TypeTgtStandard + } + + tgt := xaapiv1.TargetConfig{ + Name: ctx.String("name"), + Type: tType, + IP: ctx.String("ip"), + } + + Log.Infof("POST /target %v", tgt) + newTgt := xaapiv1.TargetConfig{} + err := HTTPCli.Post(XdsServerComputeURL("/targets"), tgt, &newTgt) + if err != nil { + return cli.NewExitError(err, 1) + } + + if ctx.Bool("short") { + fmt.Println(newTgt.ID) + } else { + fmt.Printf("New target '%s' (id %v) successfully created.\n", newTgt.Name, newTgt.ID) + } + + return nil +} + +func targetsRemove(ctx *cli.Context) error { + var res xaapiv1.TargetConfig + id := GetID(ctx) + if id == "" { + return cli.NewExitError("id parameter or option must be set", 1) + } + + if !ctx.Bool("force") { + if !Confirm("Do you permanently remove target id '" + id + "' [yes/No] ? ") { + return nil + } + } + + if err := HTTPCli.Delete(XdsServerComputeURL("/targets/"+id), &res); err != nil { + return cli.NewExitError(err, 1) + } + + fmt.Println("Target ID " + res.ID + " successfully deleted.") + return nil +} + +func terminalOpen(ctx *cli.Context) error { + + tgt, term, err := GetTargetAndTerminalIDs(ctx, true) + if err != nil { + return cli.NewExitError(err.Error(), 1) + } + if tgt == nil { + return cli.NewExitError("cannot identify target", 1) + } + + if term == nil { + // Create a new terminal when needed + newTerm := xaapiv1.TerminalConfig{ + Name: "ssh session from xds-cli", + Type: xaapiv1.TypeTermSSH, + User: ctx.String("user"), + Options: ctx.StringSlice("options"), + } + term = &newTerm + url := XdsServerComputeURL("/targets/" + tgt.ID + "/terminals") + if err := HTTPCli.Post(url, &newTerm, term); err != nil { + return cli.NewExitError(err.Error(), 1) + } + Log.Debugf("New terminal created: %v", term) + } else { + // Update terminal config when needed + needUp := false + if ctx.String("user") != "" { + term.User = ctx.String("user") + needUp = true + } + if len(ctx.StringSlice("options")) > 0 { + term.Options = ctx.StringSlice("options") + needUp = true + } + if needUp { + url := XdsServerComputeURL("/targets/" + tgt.ID + "/terminals/" + term.ID) + if err := HTTPCli.Put(url, &term, term); err != nil { + return cli.NewExitError(err.Error(), 1) + } + Log.Debugf("Update terminal config: %v", term) + } + } + + // Process Socket IO events + type exitResult struct { + error error + code int + } + exitChan := make(chan exitResult, 1) + + IOsk.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) + } + if ev.Stderr != "" { + fmt.Fprintf(os.Stderr, ev.Stderr) + } + }) + + IOsk.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" + + oldState, err := terminal.MakeRaw(int(os.Stdin.Fd())) + if err == nil { + defer terminal.Restore(int(os.Stdin.Fd()), oldState) + } + */ + + // Send stdin though WS + go func() { + paranoia := 600 + reader := bufio.NewReader(os.Stdin) + 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} + } + + // 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) + + exitChan <- exitResult{fmt.Errorf(msg), int(syscall.ELOOP)} + } + } + }() + + // Handle signals + err = OnSignals(func(sig os.Signal) { + Log.Debugf("Send signal %v", sig) + if IsWinResizeSignal(sig) { + TerminalResize(tgt, term) + } else if IsInterruptSignal(sig) { + IOsk.Emit(xaapiv1.TerminalInEvent, "\x03\n") + } else { + TerminalSendSignal(tgt, term, sig) + } + }) + if err != nil { + return cli.NewExitError(err.Error(), 1) + } + + // Send open command + url := XdsServerComputeURL("/targets/" + tgt.ID + "/terminals/" + term.ID + "/open") + LogPost("POST %v", url) + if err := HTTPCli.Post(url, nil, term); err != nil { + return cli.NewExitError(err.Error(), 1) + } + + // Wait exit - blocking + select { + case res := <-exitChan: + errStr := "" + if res.code == 0 { + Log.Debugln("Exit Target Terminal successfully") + } + if res.error != nil { + Log.Debugln("Exit Target Terminal with ERROR: ", res.error.Error()) + errStr = res.error.Error() + } + return cli.NewExitError(errStr, res.code) + } +} + +func terminalRemove(ctx *cli.Context) error { + + tgt, term, err := GetTargetAndTerminalIDs(ctx, false) + if err != nil { + return cli.NewExitError(err.Error(), 1) + } + if tgt == nil || tgt.ID == "" { + return cli.NewExitError("cannot identify target id", 1) + } + if term == nil || term.ID == "" { + return cli.NewExitError("cannot identify terminal id", 1) + } + + // Send delete command + url := XdsServerComputeURL("/targets/" + tgt.ID + "/terminals/" + term.ID) + LogPost("DELETE %v", url) + if err := HTTPCli.Delete(url, term); err != nil { + return cli.NewExitError(err.Error(), 1) + } + + return nil +} + +/** + * utils functions + */ + +// TerminalResize Send command to resize target terminal +func TerminalResize(tgt *xaapiv1.TargetConfig, term *xaapiv1.TerminalConfig) { + col, row, err := terminal.GetSize(int(os.Stdin.Fd())) + if err != nil { + Log.Errorf("Error cannot get terminal size: %v", err) + } + Log.Debugf("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 { + Log.Errorf("Error while resizing terminal (term %v): %v", sz, err) + } +} + +// TerminalSendSignal Send a signal to a target terminal +func TerminalSendSignal(tgt *xaapiv1.TargetConfig, term *xaapiv1.TerminalConfig, sig os.Signal) { + url := XdsServerComputeURL("/targets/" + tgt.ID + "/terminals/" + term.ID + "/signal/" + sig.String()) + if err := HTTPCli.Post(url, nil, nil); err != nil { + Log.Errorf("Error to send signal %v: %v", sig, err) + } +} + +// GetTargetAndTerminalIDs Retrieve Target and Terminal definition from IDs +func GetTargetAndTerminalIDs(ctx *cli.Context, useFirstFree bool) (*xaapiv1.TargetConfig, *xaapiv1.TerminalConfig, error) { + + idArg := ctx.String("id") + tidArg := GetIDName(ctx, "termId") + if tidArg == "" { + tidArg = GetIDName(ctx, "tid") + } + if idArg == "" && tidArg == "" { + return nil, nil, fmt.Errorf("id or termId argument must be set") + } + + tgts := []xaapiv1.TargetConfig{} + if err := TargetsListGet(&tgts); err != nil { + return nil, nil, err + } + + matching := 0 + ti := 0 + tj := 0 + for ii, tt := range tgts { + for jj, ttm := range tt.Terms { + if idArg == "" && compareID(ttm.ID, tidArg) { + ti = ii + tj = jj + matching++ + } + if idArg != "" && compareID(tt.ID, idArg) && compareID(ttm.ID, tidArg) { + ti = ii + tj = jj + matching++ + } + } + } + if matching > 1 { + return nil, nil, fmt.Errorf("Multiple IDs found, please set -id and -tid with full ID notation") + } else if matching == 1 { + return &tgts[ti], &tgts[ti].Terms[tj], nil + } + + // Allow to create a new terminal when only target id is set + idArg = GetIDName(ctx, "id") + if idArg != "" { + for _, tt := range tgts { + if compareID(tt.ID, idArg) { + return &tt, nil, nil + } + } + } + + return nil, nil, fmt.Errorf("No matching id found") +} + +// Sort targets by Name +type _TgtByName []xaapiv1.TargetConfig + +func (s _TgtByName) Len() int { return len(s) } +func (s _TgtByName) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s _TgtByName) Less(i, j int) bool { return s[i].Name < s[j].Name } + +// TargetsListGet Get the list of existing targets +func TargetsListGet(tgts *[]xaapiv1.TargetConfig) error { + var data []byte + if err := HTTPCli.HTTPGet(XdsServerComputeURL("/targets"), &data); err != nil { + return err + } + Log.Debugf("Result of /targets: %v", string(data[:])) + + if err := json.Unmarshal(data, &tgts); err != nil { + return err + } + + sort.Sort(_TgtByName(*tgts)) + + return nil +} diff --git a/glide.yaml b/glide.yaml index cf23f36..a0e7826 100644 --- a/glide.yaml +++ b/glide.yaml @@ -12,11 +12,11 @@ import: - package: github.com/sebd71/go-socket.io-client version: 46defcb47f - package: gerrit.automotivelinux.org/gerrit/src/xds/xds-agent.git - version: ~1.0.0 + version: ~1.1.0 subpackages: - lib/xaapiv1 - package: gerrit.automotivelinux.org/gerrit/src/xds/xds-common.git - version: ^0.1.0 + version: ~0.2.0 subpackages: - golib/common - package: github.com/joho/godotenv @@ -25,3 +25,4 @@ import: - cmd/godotenv - package: github.com/franciscocpg/reflectme version: ^0.1.9 +- package: github.com/golang/crypto/ssh/terminal diff --git a/main.go b/main.go index f9b32f3..7d1f5e1 100644 --- a/main.go +++ b/main.go @@ -208,6 +208,7 @@ func main() { initCmdProjects(&app.Commands) initCmdSdks(&app.Commands) initCmdExec(&app.Commands) + initCmdTargets(&app.Commands) initCmdMisc(&app.Commands) // Add --config option to all commands to support --config option either before or after command verb @@ -312,6 +313,10 @@ func main() { XdsConnClose() }() + // Start signals monitoring routine + MonitorSignals() + + // Run the cli app app.Run(os.Args) } diff --git a/signals.go b/signals.go new file mode 100644 index 0000000..042257e --- /dev/null +++ b/signals.go @@ -0,0 +1,70 @@ +package main + +import ( + "fmt" + "os" + "os/signal" +) + +// OnSignalCB callback type for signal +type OnSignalCB func(sig os.Signal) + +// MonSignals . +type MonSignals struct { + callback map[string][]OnSignalCB + registeredSignals []os.Signal +} + +var monSig MonSignals + +// MonitorSignals Routine used to monitor signals (eg. SIGINT, SIGTERM, ...) +func MonitorSignals() { + + monSig.callback = make(map[string][]OnSignalCB) + monSig.registeredSignals = GetRegisteredSignals() + + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, monSig.registeredSignals...) + + go func() { + for { + sig := <-sigs + Log.Debugf("Detect signal %v", sig) + if cbArr, exist := monSig.callback[sig.String()]; exist { + for _, cb := range cbArr { + cb(sig) + } + } + } + }() +} + +// isSupportSignal Check if a signal is supported or not +func isSupportSignal(sig os.Signal) bool { + for _, s := range monSig.registeredSignals { + if s == sig { + return true + } + } + return false +} + +// OnSignal Register a callback for a specified signal +func OnSignal(sig os.Signal, fCB OnSignalCB) error { + if !isSupportSignal(sig) { + return fmt.Errorf("unsupported signal %v", sig) + } + sigStr := sig.String() + monSig.callback[sigStr] = append(monSig.callback[sigStr], fCB) + return nil +} + +// OnSignals Register a callback for any signals +func OnSignals(fCB OnSignalCB) error { + for _, s := range monSig.registeredSignals { + if err := OnSignal(s, fCB); err != nil { + return err + } + } + return nil +} diff --git a/signals_other_arch.go b/signals_other_arch.go new file mode 100644 index 0000000..dd5a658 --- /dev/null +++ b/signals_other_arch.go @@ -0,0 +1,45 @@ +// +build !windows + +/* + * Copyright (C) 2017-2018 "IoT.bzh" + * Author Sebastien Douheret + * + * 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. + * + */ + +package main + +import ( + "os" + "syscall" +) + +// GetRegisteredSignals Return the list of registrable signals +func GetRegisteredSignals() []os.Signal { + return []os.Signal{ + syscall.SIGTERM, + syscall.SIGINT, + syscall.SIGWINCH, + } +} + +// IsWinResizeSignal Return true if SIGWINCH signal +func IsWinResizeSignal(sig os.Signal) bool { + return sig == syscall.SIGWINCH +} + +// IsInterruptSignal Return true if SIGINT signal +func IsInterruptSignal(sig os.Signal) bool { + return sig == syscall.SIGINT +} diff --git a/signals_windows.go b/signals_windows.go new file mode 100644 index 0000000..517220a --- /dev/null +++ b/signals_windows.go @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017-2018 "IoT.bzh" + * Author Sebastien Douheret + * + * 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. + * + */ + +package main + +import ( + "os" + "syscall" +) + +// GetRegisteredSignals Return the list of registrable signals +func GetRegisteredSignals() []os.Signal { + return []os.Signal{ + syscall.SIGTERM, + syscall.SIGINT, + } +} + +// IsWinResizeSignal Return true if SIGWINCH signal +func IsWinResizeSignal(sig os.Signal) bool { + // Not supported/implemented yet + return false +} + +// IsInterruptSignal Return true if SIGINT signal +func IsInterruptSignal(sig os.Signal) bool { + return sig == syscall.SIGINT +} diff --git a/utils.go b/utils.go index afa0942..0ca6471 100644 --- a/utils.go +++ b/utils.go @@ -123,7 +123,15 @@ func LogPost(format string, data interface{}) { // GetID Return a string ID set with --id option or as simple parameter func GetID(ctx *cli.Context) string { - id := ctx.String("id") + return GetIDName(ctx, "id") +} + +// GetIDName Return a string ID set with --XXX option or as simple parameter +func GetIDName(ctx *cli.Context, idName string) string { + if idName == "" { + return "" + } + id := ctx.String(idName) idArgs := ctx.Args().First() if id == "" && idArgs != "" { id = idArgs @@ -139,3 +147,8 @@ func Confirm(question string) bool { ans := strings.ToLower(strings.TrimSpace(answer)) return (ans == "y" || ans == "yes") } + +// compareID Compare an ID to a reference ID +func compareID(refID, ID string) bool { + return refID != "" && ID != "" && strings.Contains(refID, ID) +} -- 2.16.6