Skip to content

Commit

Permalink
Add break sequence framework to support sysrq
Browse files Browse the repository at this point in the history
Support at most 9 break sequences which can be invoked with
'Ctrl + e + c + l + [1-9]'. By default all of the break sequence
is '~B'.
This patch includes the following:

1. Design prefix tree framework to help register and search the
   sequence keys.
2. Support define break sequences in server.conf
3. Add sequence handler at both server and client side
4. Add rest api and console command to list the break sequences.
   Ctrl + e + c + l + ?

TODO: Manage the sequence keys definition via rest api and store
them in etcd.
  • Loading branch information
chenglch committed May 28, 2018
1 parent 1951736 commit 0b1f22b
Show file tree
Hide file tree
Showing 12 changed files with 594 additions and 154 deletions.
49 changes: 49 additions & 0 deletions api/escape.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package api

import (
"fmt"
"net/http"

"encoding/json"
"github.com/gorilla/mux"
"github.com/xcat2/goconserver/console"
)

type EscapeApi struct {
routes Routes
}

func NewEscapeApi(router *mux.Router) *CommandApi {
api := CommandApi{}
routes := Routes{
Route{"Command", "GET", "/breaksequence", api.listSequence},
}
api.routes = routes
for _, route := range routes {
router.
Methods(route.Method).
Path(route.Pattern).
Name(route.Name).
Handler(route.HandlerFunc)
}
return &api
}

func (api *CommandApi) listSequence(w http.ResponseWriter, req *http.Request) {
plog.Debug(fmt.Sprintf("Receive %s request %s from %s.", req.Method, req.URL.Path, req.RemoteAddr))
var resp []byte
var err error
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(http.StatusOK)
serverEscape := console.GetServerEscape()
if serverEscape == nil {
plog.HandleHttp(w, req, http.StatusInternalServerError, err.Error())
return
}
seqs := serverEscape.GetSequences()
if resp, err = json.Marshal(seqs); err != nil {
plog.HandleHttp(w, req, http.StatusInternalServerError, err.Error())
return
}
fmt.Fprintf(w, "%s\n", resp)
}
26 changes: 16 additions & 10 deletions common/conf.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ type EtcdCfg struct {
RpcPort string `yaml:"rpcport"`
}

type BreakSequenceCfg struct {
Sequence string `yaml:"sequence"`
Delay int `yaml:"delay"`
}

type ServerConfig struct {
Global struct {
Host string `yaml:"host"`
Expand All @@ -115,16 +120,17 @@ type ServerConfig struct {
DistDir string `yaml:"dist_dir"`
}
Console struct {
Port string `yaml:"port"`
DataDir string `yaml:"datadir"`
LogTimestamp bool `yaml:"log_timestamp"`
TimePrecision string `yaml:"time_precision"`
TimeFormat string `yaml:"-"`
ReplayLines int `yaml:"replay_lines"`
ClientTimeout int `yaml:"client_timeout"`
TargetTimeout int `yaml:"target_timeout"`
ReconnectInterval int `yaml:"reconnect_interval"`
Loggers LoggerCfg `yaml:"logger"`
Port string `yaml:"port"`
DataDir string `yaml:"datadir"`
LogTimestamp bool `yaml:"log_timestamp"`
TimePrecision string `yaml:"time_precision"`
TimeFormat string `yaml:"-"`
ReplayLines int `yaml:"replay_lines"`
ClientTimeout int `yaml:"client_timeout"`
TargetTimeout int `yaml:"target_timeout"`
ReconnectInterval int `yaml:"reconnect_interval"`
Loggers LoggerCfg `yaml:"logger"`
BreakSequences []BreakSequenceCfg `yaml:"break_sequence"`
}
Etcd EtcdCfg `yaml:"etcd"`
}
Expand Down
11 changes: 10 additions & 1 deletion common/signal.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"os"
"os/signal"
"sync"
)

var (
Expand All @@ -15,9 +16,11 @@ func DoSignal(done <-chan struct{}) {
for {
c := make(chan os.Signal)
var sigs []os.Signal
s.rwLock.RLock()
for sig := range s.GetSigMap() {
sigs = append(sigs, sig)
}
s.rwLock.RUnlock()
signal.Notify(c, sigs...)
select {
case sig := <-c:
Expand All @@ -35,22 +38,28 @@ func DoSignal(done <-chan struct{}) {
type SignalHandler func(s os.Signal, arg interface{})

type SignalSet struct {
m map[os.Signal]SignalHandler
rwLock *sync.RWMutex
m map[os.Signal]SignalHandler
}

func GetSignalSet() *SignalSet {
if signalSet == nil {
signalSet = new(SignalSet)
signalSet.m = make(map[os.Signal]SignalHandler)
signalSet.rwLock = new(sync.RWMutex)
}
return signalSet
}

func (set *SignalSet) Register(s os.Signal, handler SignalHandler) {
set.rwLock.Lock()
set.m[s] = handler
set.rwLock.Unlock()
}

func (set *SignalSet) Handle(sig os.Signal, arg interface{}) (err error) {
set.rwLock.RLock()
defer set.rwLock.RUnlock()
if _, found := set.m[sig]; found {
set.m[sig](sig, arg)
return nil
Expand Down
14 changes: 14 additions & 0 deletions common/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,3 +304,17 @@ func ReadTail(path string, tail int) (string, error) {
}
return strings.Join(ret[cur:], "\n"), nil
}

func SafeWrite(writer io.Writer, b []byte) error {
n := len(b)
tmp := 0
for n > 0 {
count, err := writer.Write(b[tmp:])
if err != nil {
return err
}
tmp += count
n -= count
}
return nil
}
11 changes: 9 additions & 2 deletions console/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,6 @@ func (c *CongoCli) waitInput(args interface{}) {
terminal.Restore(int(os.Stdin.Fd()), client.origState)
if exit == true {
if err == nil {
fmt.Printf("Disconnected\n")
os.Exit(0)
} else {
fmt.Fprintf(os.Stderr, err.Error())
Expand Down Expand Up @@ -345,7 +344,14 @@ func (c *CongoCli) waitInput(args interface{}) {
exit = true
return
}
exit, _ = client.checkEscape(b, n, "")
err = client.processClientSession(nil, b, n, "")
if err != nil {
fmt.Printf("\r\nError : %s\r\n", err.Error())
return
}
if client.retry == false {
exit = true
}
}
}

Expand All @@ -361,6 +367,7 @@ func (c *CongoCli) console(cmd *cobra.Command, args []string) {
}
retry := true
common.NewTaskManager(100, 16)
clientEscape = NewEscapeClientSystem()
for retry {
client, conn, err := initConsoleSessionClient(args[0], clientConfig.ServerHost, clientConfig.ConsolePort)
if err != nil {
Expand Down
Loading

0 comments on commit 0b1f22b

Please sign in to comment.