From 2b7045443d1459e5feaf9ba0813cf3f52483760c Mon Sep 17 00:00:00 2001 From: sawka Date: Mon, 5 Sep 2022 20:08:59 -0700 Subject: [PATCH] update termopts for running commands, send specialinput packet --- pkg/cmdrunner/cmdrunner.go | 56 ++++++++++++++++++++++++++++++++++++++ pkg/cmdrunner/resolver.go | 20 ++++++++++---- pkg/remote/remote.go | 10 +++++++ pkg/scpacket/scpacket.go | 2 +- pkg/scws/scws.go | 22 +++++++++++---- pkg/sstore/dbops.go | 25 +++++++++++++++++ 6 files changed, 122 insertions(+), 13 deletions(-) diff --git a/pkg/cmdrunner/cmdrunner.go b/pkg/cmdrunner/cmdrunner.go index f74c8a04..cd0e33a8 100644 --- a/pkg/cmdrunner/cmdrunner.go +++ b/pkg/cmdrunner/cmdrunner.go @@ -80,6 +80,8 @@ func init() { registerCmdFn("remote:disconnect", RemoteDisconnectCommand) registerCmdFn("remote:connect", RemoteConnectCommand) + registerCmdFn("window:resize", WindowResizeCommand) + registerCmdFn("history", HistoryCommand) } @@ -1123,3 +1125,57 @@ func splitLinesForInfo(str string) []string { } return rtn } + +func resizeRunningCommand(ctx context.Context, cmd *sstore.CmdType, newCols int) error { + fmt.Printf("resize running cmd %s/%s %d => %d\n", cmd.SessionId, cmd.CmdId, cmd.TermOpts.Cols, newCols) + siPk := packet.MakeSpecialInputPacket() + siPk.CK = base.MakeCommandKey(cmd.SessionId, cmd.CmdId) + siPk.WinSize = &packet.WinSize{Rows: int(cmd.TermOpts.Rows), Cols: newCols} + msh := remote.GetRemoteById(cmd.Remote.RemoteId) + if msh == nil { + return fmt.Errorf("cannot resize, cmd remote not found") + } + err := msh.SendSpecialInput(siPk) + if err != nil { + return err + } + newTermOpts := cmd.TermOpts + newTermOpts.Cols = int64(newCols) + err = sstore.UpdateCmdTermOpts(ctx, cmd.SessionId, cmd.CmdId, newTermOpts) + if err != nil { + return err + } + return nil +} + +func WindowResizeCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) { + ids, err := resolveUiIds(ctx, pk, R_Session|R_Screen|R_Window) + if err != nil { + return nil, err + } + colsStr := pk.Kwargs["cols"] + if colsStr == "" { + return nil, fmt.Errorf("/window:resize requires a numeric 'cols' argument") + } + cols, err := strconv.Atoi(colsStr) + if err != nil { + return nil, fmt.Errorf("/window:resize requires a numeric 'cols' argument: %v", err) + } + if cols <= 0 { + return nil, fmt.Errorf("/window:resize invalid zero/negative 'cols' argument") + } + cols = base.BoundInt(cols, shexec.MinTermCols, shexec.MaxTermCols) + runningCmds, err := sstore.GetRunningWindowCmds(ctx, ids.SessionId, ids.WindowId) + if err != nil { + return nil, fmt.Errorf("/window:resize cannot get running commands: %v", err) + } + if len(runningCmds) == 0 { + return nil, nil + } + for _, cmd := range runningCmds { + if int(cmd.TermOpts.Cols) != cols { + resizeRunningCommand(ctx, cmd, cols) + } + } + return nil, nil +} diff --git a/pkg/cmdrunner/resolver.go b/pkg/cmdrunner/resolver.go index ca3e89fc..046337d2 100644 --- a/pkg/cmdrunner/resolver.go +++ b/pkg/cmdrunner/resolver.go @@ -137,6 +137,15 @@ func resolveUiIds(ctx context.Context, pk *scpacket.FeCommandPacketType, rtype i } } } + if pk.Kwargs["window"] != "" { + windowId, err := resolveWindowArg(rtn.SessionId, rtn.ScreenId, pk.Kwargs["window"]) + if err != nil { + return rtn, err + } + if windowId != "" { + rtn.WindowId = windowId + } + } if rtype&R_Session > 0 && rtn.SessionId == "" { return rtn, fmt.Errorf("no session") } @@ -225,15 +234,14 @@ func resolveSessionId(pk *scpacket.FeCommandPacketType) (string, error) { return sessionId, nil } -func resolveWindowId(pk *scpacket.FeCommandPacketType, sessionId string) (string, error) { - windowId := pk.Kwargs["window"] - if windowId == "" { +func resolveWindowArg(sessionId string, screenId string, windowArg string) (string, error) { + if windowArg == "" { return "", nil } - if _, err := uuid.Parse(windowId); err != nil { - return "", fmt.Errorf("invalid windowid '%s'", windowId) + if _, err := uuid.Parse(windowArg); err != nil { + return "", fmt.Errorf("invalid window arg specified (must be windowid) '%s'", windowArg) } - return windowId, nil + return windowArg, nil } func resolveScreenId(ctx context.Context, pk *scpacket.FeCommandPacketType, sessionId string) (string, error) { diff --git a/pkg/remote/remote.go b/pkg/remote/remote.go index ffebfa83..d28f028c 100644 --- a/pkg/remote/remote.go +++ b/pkg/remote/remote.go @@ -540,6 +540,16 @@ func (msh *MShellProc) SendInput(dataPk *packet.DataPacketType) error { return msh.ServerProc.Input.SendPacket(dataPk) } +func (msh *MShellProc) SendSpecialInput(siPk *packet.SpecialInputPacketType) error { + if !msh.IsConnected() { + return fmt.Errorf("remote is not connected, cannot send input") + } + if !msh.IsCmdRunning(siPk.CK) { + return fmt.Errorf("cannot send input, cmd is not running") + } + return msh.ServerProc.Input.SendPacket(siPk) +} + func makeTermOpts(runPk *packet.RunPacketType) sstore.TermOpts { return sstore.TermOpts{Rows: int64(runPk.TermOpts.Rows), Cols: int64(runPk.TermOpts.Cols), FlexRows: true, MaxPtySize: DefaultMaxPtySize} } diff --git a/pkg/scpacket/scpacket.go b/pkg/scpacket/scpacket.go index b6d2ff95..0a7d7b95 100644 --- a/pkg/scpacket/scpacket.go +++ b/pkg/scpacket/scpacket.go @@ -34,7 +34,7 @@ type FeInputPacketType struct { Type string `json:"type"` CK base.CommandKey `json:"ck"` Remote sstore.RemotePtrType `json:"remote"` - InputData64 string `json:"inputdata"` + InputData64 string `json:"inputdata64"` SigNum int `json:"signum,omitempty"` WinSize *packet.WinSize `json:"winsize,omitempty"` } diff --git a/pkg/scws/scws.go b/pkg/scws/scws.go index fb642696..a64d6772 100644 --- a/pkg/scws/scws.go +++ b/pkg/scws/scws.go @@ -217,23 +217,33 @@ func sendCmdInput(pk *scpacket.FeInputPacketType) error { if pk.Remote.RemoteId == "" { return fmt.Errorf("input must set remoteid") } + msh := remote.GetRemoteById(pk.Remote.RemoteId) + if msh == nil { + return fmt.Errorf("remote %d not found", pk.Remote.RemoteId) + } if len(pk.InputData64) > 0 { inputLen := packet.B64DecodedLen(pk.InputData64) if inputLen > MaxInputDataSize { return fmt.Errorf("input data size too large, len=%d (max=%d)", inputLen, MaxInputDataSize) } - msh := remote.GetRemoteById(pk.Remote.RemoteId) - if msh == nil { - return fmt.Errorf("remote %d not found", pk.Remote.RemoteId) - } dataPk := packet.MakeDataPacket() dataPk.CK = pk.CK dataPk.FdNum = 0 // stdin dataPk.Data64 = pk.InputData64 - return msh.SendInput(dataPk) + err = msh.SendInput(dataPk) + if err != nil { + return err + } } if pk.SigNum != 0 || pk.WinSize != nil { - return fmt.Errorf("signum / winsize not supported") + siPk := packet.MakeSpecialInputPacket() + siPk.CK = pk.CK + siPk.SigNum = pk.SigNum + siPk.WinSize = pk.WinSize + err = msh.SendSpecialInput(siPk) + if err != nil { + return err + } } return nil } diff --git a/pkg/sstore/dbops.go b/pkg/sstore/dbops.go index e747fe54..1c2fd590 100644 --- a/pkg/sstore/dbops.go +++ b/pkg/sstore/dbops.go @@ -885,3 +885,28 @@ func ClearWindow(ctx context.Context, sessionId string, windowId string) (*Model } return &ModelUpdate{Window: win}, nil } + +func GetRunningWindowCmds(ctx context.Context, sessionId string, windowId string) ([]*CmdType, error) { + var rtn []*CmdType + txErr := WithTx(ctx, func(tx *TxWrap) error { + query := `SELECT * from cmd WHERE cmdid IN (SELECT cmdid FROM line WHERE sessionid = ? AND windowid = ?) AND status = ?` + cmdMaps := tx.SelectMaps(query, sessionId, windowId, CmdStatusRunning) + for _, m := range cmdMaps { + rtn = append(rtn, CmdFromMap(m)) + } + return nil + }) + if txErr != nil { + return nil, txErr + } + return rtn, nil +} + +func UpdateCmdTermOpts(ctx context.Context, sessionId string, cmdId string, termOpts TermOpts) error { + txErr := WithTx(ctx, func(tx *TxWrap) error { + query := `UPDATE cmd SET termopts = ? WHERE sessionid = ? AND cmdid = ?` + tx.ExecWrap(query, termOpts, sessionId, cmdId) + return nil + }) + return txErr +}