Compare commits
7 Commits
Author | SHA1 | Date |
---|---|---|
Evan Chen | ddf5e86cbc | |
Evan Chen | faf8300d8f | |
Evan Chen | f309009a76 | |
Evan Chen | 7aec30d473 | |
Evan Chen | 9728bfab58 | |
Evan Chen | 6828e6397a | |
Evan Chen | b6b0774531 |
|
@ -3,12 +3,14 @@ package main
|
|||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"kumoly.io/lib/klog"
|
||||
"kumoly.io/lib/ksrv"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"kumoly.io/lib/guard"
|
||||
"kumoly.io/tools/gterm"
|
||||
)
|
||||
|
||||
|
@ -27,16 +29,20 @@ var Version = "0.0.0"
|
|||
var Build = "alpha"
|
||||
|
||||
var (
|
||||
flagAllowIP string
|
||||
flagAppName string
|
||||
flagAddr string
|
||||
flagShell string
|
||||
flagDir string
|
||||
flagLogLevel int
|
||||
flagDev bool
|
||||
flagVer bool
|
||||
flagArgs arrayFlags
|
||||
flagProfile bool
|
||||
flagAllowIPNet string
|
||||
flagAppName string
|
||||
flagAddr string
|
||||
flagShell string
|
||||
flagDir string
|
||||
flagLogLevel int
|
||||
flagLogPretty bool
|
||||
flagDev bool
|
||||
flagVer bool
|
||||
flagArgs arrayFlags
|
||||
flagProfile bool
|
||||
flagSalt string
|
||||
flagUsr string
|
||||
flagPasswd string
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -46,10 +52,14 @@ func init() {
|
|||
flag.StringVar(&flagDir, "dir", "", "the working dir that the shell will start from")
|
||||
flag.Var(&flagArgs, "arg", "additional args to pass to cmd, multiple args can be passed, ex. -arg a -arg b")
|
||||
flag.BoolVar(&flagDev, "dev", false, "set the system to development mode")
|
||||
flag.IntVar(&flagLogLevel, "log-level", 9, "log level, error:1 debug:2 warn:4 info:8")
|
||||
flag.StringVar(&flagAllowIP, "allow", "", "restrict ip")
|
||||
flag.IntVar(&flagLogLevel, "log-level", 1, "log level [-1(trace):5(panic)] 7 to disable")
|
||||
flag.StringVar(&flagAllowIPNet, "allow", "", "restrict ip in a specific ip net")
|
||||
flag.BoolVar(&flagProfile, "profile", false, "print default profile, could be invoked with <(..)")
|
||||
flag.StringVar(&flagSalt, "salt", "", "add salt to encoded keyparam")
|
||||
flag.BoolVar(&flagVer, "v", false, "show version")
|
||||
flag.BoolVar(&flagLogPretty, "pretty", false, "log message in human readable format (the original log is json)")
|
||||
flag.StringVar(&flagUsr, "usr", "", "username, use basic auth for authentication if user and password are set")
|
||||
flag.StringVar(&flagPasswd, "passwd", "", "password, use basic auth for authentication if user and password are set")
|
||||
|
||||
flag.Usage = func() {
|
||||
fmt.Fprintf(os.Stderr, "Usage: gterm [options]\n")
|
||||
|
@ -68,42 +78,45 @@ func main() {
|
|||
return
|
||||
}
|
||||
|
||||
klog.LEVEL = klog.Llevel(flagLogLevel)
|
||||
klog.PROD = !flagDev
|
||||
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
|
||||
zerolog.SetGlobalLevel(zerolog.Level(flagLogLevel))
|
||||
if flagLogPretty {
|
||||
log.Logger = log.Output(zerolog.ConsoleWriter{
|
||||
Out: os.Stdout,
|
||||
TimeFormat: "2006/01/02 15:04:05",
|
||||
})
|
||||
}
|
||||
if flagDev {
|
||||
log.Logger = log.With().Caller().Logger()
|
||||
}
|
||||
log.Logger = log.With().Str("mod", "gtrem").Logger()
|
||||
|
||||
g := gterm.New()
|
||||
g.AppName = flagAppName
|
||||
g.Cmd = flagShell
|
||||
g.Args = flagArgs
|
||||
g.Dir = flagDir
|
||||
g.Salt = flagSalt
|
||||
|
||||
gd := guard.New()
|
||||
if flagAllowIPNet != "" {
|
||||
_, ipnet, err := net.ParseCIDR(flagAllowIPNet)
|
||||
if err != nil {
|
||||
log.Panic().Err(err).Msg("")
|
||||
}
|
||||
gd.AllowIPNet = ipnet
|
||||
}
|
||||
if flagUsr != "" && flagPasswd != "" {
|
||||
gd.SetBasicAuth(flagUsr, flagPasswd)
|
||||
}
|
||||
|
||||
server := &http.Server{
|
||||
Addr: flagAddr,
|
||||
Handler: Middleware(g),
|
||||
Handler: gd.Guard(g),
|
||||
}
|
||||
klog.Info("gterm starting at ", flagAddr)
|
||||
log.Info().Msgf("gterm starting at %s", flagAddr)
|
||||
err := server.ListenAndServe()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Middleware(next http.Handler) http.Handler {
|
||||
log := klog.Sub(flagAppName)
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}()
|
||||
ip := ksrv.GetIP(r)
|
||||
if flagAllowIP != "" {
|
||||
if !ksrv.MatchIPGlob(ip, flagAllowIP) {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
w.Write([]byte("permission denied"))
|
||||
}
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
log.Debug(ip, " ", r.URL.String())
|
||||
})
|
||||
}
|
||||
|
|
5
go.mod
5
go.mod
|
@ -5,11 +5,14 @@ go 1.17
|
|||
require (
|
||||
github.com/creack/pty v1.1.17
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
kumoly.io/lib/klog v0.0.8
|
||||
github.com/rs/zerolog v1.26.0
|
||||
kumoly.io/lib/guard v0.1.1
|
||||
kumoly.io/lib/ksrv v0.0.2-0.20211112060911-0d61b343a298
|
||||
kumoly.io/lib/xorencrypt v0.1.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b // indirect
|
||||
kumoly.io/lib/klog v0.0.8 // indirect
|
||||
)
|
||||
|
|
35
go.sum
35
go.sum
|
@ -1,13 +1,48 @@
|
|||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
|
||||
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.26.0 h1:ORM4ibhEZeTeQlCojCK2kPz1ogAY4bGs4tD+SaAdGaE=
|
||||
github.com/rs/zerolog v1.26.0/go.mod h1:yBiM87lvSqX8h0Ww4sdzNSkVYZ8dL2xjZJG1lAuGZEo=
|
||||
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b h1:1VkfZQv42XQlA/jchYumAnv1UPo6RgF9rJFkTgZIxO4=
|
||||
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
kumoly.io/lib/guard v0.1.0 h1:PvFM0bcWbgfZQv4QfRwDmrK9FAjv9QYwfJY3Ffg6JA0=
|
||||
kumoly.io/lib/guard v0.1.0/go.mod h1:yWg9RDSI6YXkOPmP6Ad93aMqzlxhgW8LOe/ZRjjYX3U=
|
||||
kumoly.io/lib/guard v0.1.1 h1:aUcn0qVtX6TqRhp7bWSjIRlbWRWtuK0cjCArILTbAcY=
|
||||
kumoly.io/lib/guard v0.1.1/go.mod h1:yWg9RDSI6YXkOPmP6Ad93aMqzlxhgW8LOe/ZRjjYX3U=
|
||||
kumoly.io/lib/klog v0.0.8 h1:6hTfDlZh7KGnPrd2tUrauCKRImSnyyN9DHXpey3Czn8=
|
||||
kumoly.io/lib/klog v0.0.8/go.mod h1:Snm+c1xRrh/RbXsxQf7UGYbAJGPcIa6bEEN+CmzJh7M=
|
||||
kumoly.io/lib/ksrv v0.0.2-0.20211112060911-0d61b343a298 h1:0raqoIXmNpD6s1SrJbieAyIIkDyhe+aqfaXvx8wenrI=
|
||||
kumoly.io/lib/ksrv v0.0.2-0.20211112060911-0d61b343a298/go.mod h1:pwd+NspxnoxPJAETRY2V4i2qZc+orKLxvWzGUBiqBW8=
|
||||
kumoly.io/lib/xorencrypt v0.1.0 h1:VssGocaBAPyLn+QURVY8FdFYRBmwFr26z7ame0zwV44=
|
||||
kumoly.io/lib/xorencrypt v0.1.0/go.mod h1:+L3JtdD/CTlcXvE8X7AvOJBVbN16qvnxxv3zIWPZgQM=
|
||||
|
|
121
gterm.go
121
gterm.go
|
@ -13,9 +13,10 @@ import (
|
|||
|
||||
"github.com/creack/pty"
|
||||
"github.com/gorilla/websocket"
|
||||
"kumoly.io/lib/klog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"kumoly.io/lib/ksrv"
|
||||
"kumoly.io/lib/ksrv/engine"
|
||||
"kumoly.io/lib/xorencrypt"
|
||||
"kumoly.io/tools/gterm/public"
|
||||
)
|
||||
|
||||
|
@ -29,10 +30,12 @@ var tmpl *engine.Engine
|
|||
var servePublic = http.FileServer(http.FS(public.FS))
|
||||
|
||||
var pool map[string]chan struct{}
|
||||
var locks map[string]chan struct{}
|
||||
|
||||
func init() {
|
||||
tmpl = engine.Must(engine.New("").Parse(index))
|
||||
pool = map[string]chan struct{}{}
|
||||
locks = map[string]chan struct{}{}
|
||||
}
|
||||
|
||||
type GTerm struct {
|
||||
|
@ -53,6 +56,7 @@ type GTerm struct {
|
|||
// cycle should be tolerated, beyond this the connection should be deemed dead
|
||||
Timeout time.Duration
|
||||
BufferSize int
|
||||
Salt string
|
||||
}
|
||||
|
||||
func New() *GTerm {
|
||||
|
@ -73,13 +77,12 @@ func New() *GTerm {
|
|||
// }
|
||||
go func() {
|
||||
//print pool
|
||||
log := klog.Sub("gterm")
|
||||
for {
|
||||
cons := []string{}
|
||||
for k := range pool {
|
||||
cons = append(cons, k)
|
||||
}
|
||||
log.Info("current connections: ", cons)
|
||||
log.Info().Interface("connections", cons).Msg("")
|
||||
time.Sleep(5 * time.Minute)
|
||||
}
|
||||
}()
|
||||
|
@ -125,6 +128,29 @@ func (g *GTerm) App(w http.ResponseWriter, r *http.Request) {
|
|||
servePublic.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
type NewCmdRequest struct {
|
||||
Cmd string `json:"cmd"`
|
||||
Envs []string `json:"envs"`
|
||||
Args []string `json:"args"`
|
||||
Dir string `json:"dir"`
|
||||
Block bool `json:"block"`
|
||||
}
|
||||
|
||||
func (g *GTerm) defaultCmd() *exec.Cmd {
|
||||
cmd := exec.Command(g.Cmd, g.Args...)
|
||||
cmd.Env = g.Envs
|
||||
if g.Dir != "" && g.Dir != "." {
|
||||
cmd.Dir = g.Dir
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (g *GTerm) echo(msg string) *exec.Cmd {
|
||||
cmd := exec.Command("echo", msg)
|
||||
cmd.Env = g.Envs
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (g *GTerm) WS(w http.ResponseWriter, r *http.Request) {
|
||||
id := fmt.Sprintf("[%02d] %s", ctr(), ksrv.GetIP(r))
|
||||
pool[id] = make(chan struct{}, 1)
|
||||
|
@ -132,8 +158,43 @@ func (g *GTerm) WS(w http.ResponseWriter, r *http.Request) {
|
|||
close(pool[id])
|
||||
delete(pool, id)
|
||||
}()
|
||||
l := klog.Sub(id)
|
||||
l.Info("connection established.")
|
||||
l := log.With().Str("id", id).Logger()
|
||||
|
||||
var cmd *exec.Cmd
|
||||
|
||||
if newCmd := r.URL.Query().Get("cmd"); newCmd != "" {
|
||||
param, _ := xorencrypt.Decrypt(newCmd, g.Salt)
|
||||
req := &NewCmdRequest{}
|
||||
err := json.Unmarshal([]byte(param), req)
|
||||
if err != nil {
|
||||
l.Error().Err(err).Str("base", newCmd).Str("decrypt", param).Msg("cmd decode failed")
|
||||
cmd = g.echo("cmd decode failed")
|
||||
} else {
|
||||
cmd = exec.Command(req.Cmd, req.Args...)
|
||||
cmd.Env = append(req.Envs, "TERM=xterm-256color")
|
||||
cmd.Dir = req.Dir
|
||||
if req.Block {
|
||||
lock, ok := locks[newCmd]
|
||||
if !ok {
|
||||
locks[newCmd] = make(chan struct{}, 1)
|
||||
}
|
||||
if len(lock) == 1 {
|
||||
cmd = g.echo("cmd already running")
|
||||
} else {
|
||||
l.Info().Interface("cmd", req).Msg("starting cmd")
|
||||
locks[newCmd] <- struct{}{}
|
||||
defer func() {
|
||||
<-locks[newCmd]
|
||||
close(locks[newCmd])
|
||||
delete(locks, newCmd)
|
||||
}()
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cmd = g.defaultCmd()
|
||||
}
|
||||
|
||||
upgrader := websocket.Upgrader{
|
||||
HandshakeTimeout: 0,
|
||||
ReadBufferSize: g.BufferSize,
|
||||
|
@ -141,32 +202,28 @@ func (g *GTerm) WS(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
conn, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
l.Error(err)
|
||||
l.Error().Err(err).Msg("upgrade error")
|
||||
return
|
||||
}
|
||||
l.Info().Msg("connection established.")
|
||||
|
||||
cmd := exec.Command(g.Cmd, g.Args...)
|
||||
cmd.Env = g.Envs
|
||||
if g.Dir != "" && g.Dir != "." {
|
||||
cmd.Dir = g.Dir
|
||||
}
|
||||
tty, err := pty.Start(cmd)
|
||||
if err != nil {
|
||||
l.Error(err)
|
||||
l.Error().Err(err).Msg("start tty error")
|
||||
conn.WriteMessage(websocket.TextMessage, []byte(err.Error()))
|
||||
}
|
||||
defer func() {
|
||||
if err := cmd.Process.Kill(); err != nil {
|
||||
l.Error(err)
|
||||
l.Error().Err(err).Msg("proccess kill error")
|
||||
}
|
||||
if _, err := cmd.Process.Wait(); err != nil {
|
||||
l.Error(err)
|
||||
l.Error().Err(err).Msg("proccess wait error")
|
||||
}
|
||||
if err := tty.Close(); err != nil {
|
||||
l.Error(err)
|
||||
l.Error().Err(err).Msg("tty close error")
|
||||
}
|
||||
if err := conn.Close(); err != nil {
|
||||
l.Error(err)
|
||||
l.Error().Err(err).Msg("conn close error")
|
||||
}
|
||||
}()
|
||||
|
||||
|
@ -182,16 +239,16 @@ func (g *GTerm) WS(w http.ResponseWriter, r *http.Request) {
|
|||
go func() {
|
||||
for {
|
||||
if err := conn.WriteMessage(websocket.PingMessage, []byte("keepalive")); err != nil {
|
||||
l.Warn("failed to write ping message")
|
||||
l.Warn().Msg("failed to write ping message")
|
||||
return
|
||||
}
|
||||
time.Sleep(g.Timeout / 2)
|
||||
if time.Since(lastPongTime) > g.Timeout {
|
||||
l.Warn("failed to get response from ping, triggering disconnect now...")
|
||||
l.Warn().Msg("failed to get response from ping, triggering disconnect now...")
|
||||
waiter <- struct{}{}
|
||||
return
|
||||
}
|
||||
l.Debug("received response from ping successfully")
|
||||
l.Debug().Msg("received response from ping successfully")
|
||||
}
|
||||
}()
|
||||
|
||||
|
@ -209,19 +266,19 @@ func (g *GTerm) WS(w http.ResponseWriter, r *http.Request) {
|
|||
buffer := make([]byte, g.BufferSize)
|
||||
readLength, err := tty.Read(buffer)
|
||||
if err != nil {
|
||||
l.Warn("failed to read from tty: ", err)
|
||||
l.Warn().Err(err).Msg("failed to read from tty")
|
||||
if err := conn.WriteMessage(websocket.TextMessage, []byte("bye!")); err != nil {
|
||||
l.Warn("failed to send termination message from tty to xterm.js: ", err)
|
||||
l.Warn().Err(err).Msg("failed to send termination message from tty to xterm.js")
|
||||
}
|
||||
waiter <- struct{}{}
|
||||
return
|
||||
}
|
||||
if err := conn.WriteMessage(websocket.BinaryMessage, buffer[:readLength]); err != nil {
|
||||
l.Warn("failed to send ", readLength, " bytes from tty to xterm.js")
|
||||
l.Warn().Msgf("failed to send %s bytes from tty to xterm.js", readLength)
|
||||
errorCounter++
|
||||
continue
|
||||
}
|
||||
l.Debug("sent message of size ", readLength, " bytes from tty to xterm.js")
|
||||
l.Debug().Msgf("sent message of size %s bytes from tty to xterm.js", readLength)
|
||||
errorCounter = 0
|
||||
}
|
||||
}()
|
||||
|
@ -233,7 +290,7 @@ func (g *GTerm) WS(w http.ResponseWriter, r *http.Request) {
|
|||
messageType, data, err := conn.ReadMessage()
|
||||
if err != nil {
|
||||
if !connectionClosed {
|
||||
l.Warn("failed to get next reader: ", err)
|
||||
l.Warn().Err(err).Msg("failed to get next reader")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -243,11 +300,11 @@ func (g *GTerm) WS(w http.ResponseWriter, r *http.Request) {
|
|||
if !ok {
|
||||
dataType = "uunknown"
|
||||
}
|
||||
l.Debug(fmt.Sprintf("received %s (type: %v) message of size %v byte(s) from xterm.js with key sequence: %v", dataType, messageType, dataLength, dataBuffer))
|
||||
l.Debug().Msgf("received %s (type: %v) message of size %v byte(s) from xterm.js with key sequence: %v", dataType, messageType, dataLength, dataBuffer)
|
||||
|
||||
// process
|
||||
if dataLength == -1 { // invalid
|
||||
l.Warn("failed to get the correct number of bytes read, ignoring message")
|
||||
l.Warn().Msg("failed to get the correct number of bytes read, ignoring message")
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -257,15 +314,15 @@ func (g *GTerm) WS(w http.ResponseWriter, r *http.Request) {
|
|||
ttySize := &TTYSize{}
|
||||
resizeMessage := bytes.Trim(dataBuffer[1:], " \n\r\t\x00\x01")
|
||||
if err := json.Unmarshal(resizeMessage, ttySize); err != nil {
|
||||
l.Warn(fmt.Sprintf("failed to unmarshal received resize message '%s': %s", string(resizeMessage), err))
|
||||
l.Warn().Err(err).Msgf("failed to unmarshal received resize message '%s'", string(resizeMessage))
|
||||
continue
|
||||
}
|
||||
l.DebugF(klog.H{"rows": ttySize.Rows, "columns": ttySize.Cols}, "resizing tty...")
|
||||
l.Debug().Int("rows", int(ttySize.Rows)).Int("columns", int(ttySize.Cols)).Msg("resizing tty...")
|
||||
if err := pty.Setsize(tty, &pty.Winsize{
|
||||
Rows: ttySize.Rows,
|
||||
Cols: ttySize.Cols,
|
||||
}); err != nil {
|
||||
l.Warn("failed to resize tty, error: ", err)
|
||||
l.Warn().Err(err).Msg("failed to resize tty")
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
@ -274,16 +331,16 @@ func (g *GTerm) WS(w http.ResponseWriter, r *http.Request) {
|
|||
// write to tty
|
||||
bytesWritten, err := tty.Write(dataBuffer)
|
||||
if err != nil {
|
||||
l.Warn(fmt.Sprintf("failed to write %v bytes to tty: %s", len(dataBuffer), err))
|
||||
l.Warn().Err(err).Msgf("failed to write %v bytes to tty", len(dataBuffer))
|
||||
continue
|
||||
}
|
||||
l.Debug(bytesWritten, " bytes written to tty...")
|
||||
l.Debug().Msgf("%d bytes written to tty...", bytesWritten)
|
||||
}
|
||||
}()
|
||||
|
||||
<-waiter
|
||||
close(waiter)
|
||||
l.Info("closing connection...")
|
||||
l.Info().Msg("closing connection...")
|
||||
connectionClosed = true
|
||||
}
|
||||
|
||||
|
|
2
main.js
2
main.js
|
@ -18,7 +18,7 @@ import 'xterm/css/xterm.css'
|
|||
const fitAddon = new FitAddon();
|
||||
terminal.loadAddon(fitAddon);
|
||||
var protocol = (location.protocol === "https:") ? "wss://" : "ws://";
|
||||
var url = protocol + location.host + location.pathname + "ws"
|
||||
var url = protocol + location.host + location.pathname + "ws" + location.search
|
||||
const ws = new WebSocket(url);
|
||||
const attachAddon = new AttachAddon(ws);
|
||||
const webLinksAddon = new WebLinksAddon();
|
||||
|
|
3
make.sh
3
make.sh
|
@ -11,6 +11,7 @@ FAILURES=""
|
|||
|
||||
PLATFORMS="darwin/amd64 darwin/arm64"
|
||||
PLATFORMS="$PLATFORMS linux/amd64"
|
||||
PLATFORMS="$PLATFORMS linux/s390x"
|
||||
|
||||
for PLATFORM in $PLATFORMS; do
|
||||
GOOS=${PLATFORM%/*}
|
||||
|
@ -27,4 +28,4 @@ if [[ "${FAILURES}" != "" ]]; then
|
|||
echo ""
|
||||
echo "${SCRIPT_NAME} failed on: ${FAILURES}"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1 +1 @@
|
|||
<!DOCTYPE html><html><head><link rel="stylesheet" href="index.6999253a.css"><title>{{.AppName}}</title><style>body::-webkit-scrollbar,div::-webkit-scrollbar,html::-webkit-scrollbar{display:none;width:0}body,html{margin:0;overflow:hidden;padding:0}div#terminal{height:100%;left:0;position:absolute;top:0;width:100%}div#terminal div{height:100%}.xterm-screen,.xterm-viewport{height:100%;margin:0;padding:0}</style></head><body> <div id="terminal"></div> <script type="module" src="index.52560dc1.js"></script> </body></html>
|
||||
<!DOCTYPE html><html><head><link rel="stylesheet" href="index.6999253a.css"><title>{{.AppName}}</title><style>body::-webkit-scrollbar,div::-webkit-scrollbar,html::-webkit-scrollbar{display:none;width:0}body,html{margin:0;overflow:hidden;padding:0}div#terminal{height:100%;left:0;position:absolute;top:0;width:100%}div#terminal div{height:100%}.xterm-screen,.xterm-viewport{height:100%;margin:0;padding:0}</style></head><body> <div id="terminal"></div> <script type="module" src="index.a2a226ad.js"></script> </body></html>
|
Loading…
Reference in New Issue