From e55f7fb4fe892663cb73e8cd9fee641b6cd0fab6 Mon Sep 17 00:00:00 2001 From: sawka Date: Tue, 18 Oct 2022 18:03:02 -0700 Subject: [PATCH] working with new static commands --- pkg/cmdrunner/cmdrunner.go | 154 ++++++++++++++++++++++++++----------- pkg/cmdrunner/shparse.go | 1 + pkg/remote/remote.go | 10 +-- pkg/scpacket/scpacket.go | 26 +++++++ 4 files changed, 140 insertions(+), 51 deletions(-) diff --git a/pkg/cmdrunner/cmdrunner.go b/pkg/cmdrunner/cmdrunner.go index 7e4d2f68..9a23aaa7 100644 --- a/pkg/cmdrunner/cmdrunner.go +++ b/pkg/cmdrunner/cmdrunner.go @@ -247,43 +247,12 @@ func RunCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.U if err != nil { return nil, err } - rtnLine, err := sstore.AddCmdLine(ctx, ids.SessionId, ids.WindowId, DefaultUserId, cmd) + update, err := addLineForCmd(ctx, "/run", true, ids, cmd) if err != nil { return nil, err } - sw, err := sstore.GetScreenWindowByIds(ctx, ids.SessionId, ids.ScreenId, ids.WindowId) - if err != nil { - // ignore error here, because the command has already run (nothing to do) - fmt.Printf("/run error getting screen-window: %v\n", err) - } - if sw != nil { - updateMap := make(map[string]interface{}) - updateMap[sstore.SWField_SelectedLine] = rtnLine.LineNum - updateMap[sstore.SWField_Focus] = sstore.SWFocusCmdFg - sw, err = sstore.UpdateScreenWindow(ctx, ids.SessionId, ids.ScreenId, ids.WindowId, updateMap) - if err != nil { - // ignore error again (nothing to do) - fmt.Printf("/run error updating screen-window selected line: %v\n", err) - } - } - update := sstore.ModelUpdate{ - Line: rtnLine, - Cmd: cmd, - ScreenWindows: []*sstore.ScreenWindowType{sw}, - Interactive: pk.Interactive, - } + update.Interactive = pk.Interactive sstore.MainBus.SendUpdate(ids.SessionId, update) - ctxVal := ctx.Value(historyContextKey) - if ctxVal != nil { - hctx := ctxVal.(*historyContextType) - if rtnLine != nil { - hctx.LineId = rtnLine.LineId - } - if cmd != nil { - hctx.CmdId = cmd.CmdId - hctx.RemotePtr = &cmd.Remote - } - } return nil, nil } @@ -520,19 +489,27 @@ func UnSetCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore delete(envMap, argStr) unsetVars[argStr] = true } + if len(unsetVars) == 0 { + return nil, fmt.Errorf("no variables provided to unset") + } state := *ids.Remote.RemoteState state.Env0 = shexec.MakeEnv0(envMap) - remote, err := sstore.UpdateRemoteState(ctx, ids.SessionId, ids.WindowId, ids.Remote.RemotePtr, state) + remoteInst, err := sstore.UpdateRemoteState(ctx, ids.SessionId, ids.WindowId, ids.Remote.RemotePtr, state) if err != nil { return nil, err } - update := sstore.ModelUpdate{ - Sessions: sstore.MakeSessionsUpdateForRemote(ids.SessionId, remote), - Info: &sstore.InfoMsgType{ - InfoMsg: fmt.Sprintf("[%s] unset vars: %s", ids.Remote.DisplayName, formatStrs(mapToStrs(unsetVars), "and", false)), - TimeoutMs: 2000, - }, + var cmdOutput bytes.Buffer + for varName, _ := range unsetVars { + cmdOutput.WriteString(fmt.Sprintf("unset %s\r\n", shellescape.Quote(varName))) } + cmd, err := makeStaticCmd(ctx, "unset", ids, pk.GetRawStr(), cmdOutput.Bytes()) + update, err := addLineForCmd(ctx, "/unset", false, ids, cmd) + if err != nil { + // TODO tricky error since the command was a success, but we can't show the output + return nil, err + } + update.Interactive = pk.Interactive + update.Sessions = sstore.MakeSessionsUpdateForRemote(ids.SessionId, remoteInst) return update, nil } @@ -1043,16 +1020,101 @@ func CdCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.Up if err != nil { return nil, err } - update := sstore.ModelUpdate{ - Sessions: sstore.MakeSessionsUpdateForRemote(ids.SessionId, remoteInst), - Info: &sstore.InfoMsgType{ - InfoMsg: fmt.Sprintf("[%s] current directory = %s", ids.Remote.DisplayName, newDir), - TimeoutMs: 2000, - }, + cmdOutput := fmt.Sprintf("cwd = %s", shellescape.Quote(newDir)) + cmd, err := makeStaticCmd(ctx, "cd", ids, pk.GetRawStr(), []byte(cmdOutput)) + if err != nil { + // TODO tricky error since the command was a success, but we can't show the output + return nil, err } + update, err := addLineForCmd(ctx, "/cd", false, ids, cmd) + if err != nil { + // TODO tricky error since the command was a success, but we can't show the output + return nil, err + } + update.Interactive = pk.Interactive + update.Sessions = sstore.MakeSessionsUpdateForRemote(ids.SessionId, remoteInst) + //update.Info = &sstore.InfoMsgType{ + // InfoMsg: fmt.Sprintf("[%s] current directory = %s", ids.Remote.DisplayName, newDir), + // TimeoutMs: 2000, + //} return update, nil } +func makeStaticCmd(ctx context.Context, metaCmd string, ids resolvedIds, cmdStr string, cmdOutput []byte) (*sstore.CmdType, error) { + cmd := &sstore.CmdType{ + SessionId: ids.SessionId, + CmdId: scbase.GenSCUUID(), + CmdStr: cmdStr, + Remote: ids.Remote.RemotePtr, + TermOpts: sstore.TermOpts{Rows: shexec.DefaultTermRows, Cols: shexec.DefaultTermCols, FlexRows: true, MaxPtySize: remote.DefaultMaxPtySize}, + Status: sstore.CmdStatusDone, + StartPk: nil, + DonePk: nil, + RunOut: nil, + } + if ids.Remote.RemoteState != nil { + cmd.RemoteState = *ids.Remote.RemoteState + } + err := sstore.CreateCmdPtyFile(ctx, cmd.SessionId, cmd.CmdId, cmd.TermOpts.MaxPtySize) + if err != nil { + // TODO tricky error since the command was a success, but we can't show the output + return nil, fmt.Errorf("cannot create local ptyout file for %s command: %w", metaCmd, err) + } + // can ignore ptyupdate + _, err = sstore.AppendToCmdPtyBlob(ctx, cmd.SessionId, cmd.CmdId, cmdOutput, 0) + if err != nil { + // TODO tricky error since the command was a success, but we can't show the output + return nil, fmt.Errorf("cannot append to local ptyout file for %s command: %v", metaCmd, err) + } + return cmd, nil +} + +func addLineForCmd(ctx context.Context, metaCmd string, shouldFocus bool, ids resolvedIds, cmd *sstore.CmdType) (*sstore.ModelUpdate, error) { + rtnLine, err := sstore.AddCmdLine(ctx, ids.SessionId, ids.WindowId, DefaultUserId, cmd) + if err != nil { + return nil, err + } + sw, err := sstore.GetScreenWindowByIds(ctx, ids.SessionId, ids.ScreenId, ids.WindowId) + if err != nil { + // ignore error here, because the command has already run (nothing to do) + fmt.Printf("%s error getting screen-window: %v\n", metaCmd, err) + } + if sw != nil { + updateMap := make(map[string]interface{}) + updateMap[sstore.SWField_SelectedLine] = rtnLine.LineNum + if shouldFocus { + updateMap[sstore.SWField_Focus] = sstore.SWFocusCmdFg + } + sw, err = sstore.UpdateScreenWindow(ctx, ids.SessionId, ids.ScreenId, ids.WindowId, updateMap) + if err != nil { + // ignore error again (nothing to do) + fmt.Printf("%s error updating screen-window selected line: %v\n", metaCmd, err) + } + } + update := &sstore.ModelUpdate{ + Line: rtnLine, + Cmd: cmd, + ScreenWindows: []*sstore.ScreenWindowType{sw}, + } + updateHistoryContext(ctx, rtnLine, cmd) + return update, nil +} + +func updateHistoryContext(ctx context.Context, line *sstore.LineType, cmd *sstore.CmdType) { + ctxVal := ctx.Value(historyContextKey) + if ctxVal == nil { + return + } + hctx := ctxVal.(*historyContextType) + if line != nil { + hctx.LineId = line.LineId + } + if cmd != nil { + hctx.CmdId = cmd.CmdId + hctx.RemotePtr = &cmd.Remote + } +} + func getStrArr(v interface{}, field string) []string { if v == nil { return nil diff --git a/pkg/cmdrunner/shparse.go b/pkg/cmdrunner/shparse.go index aa8592f0..a788ce1f 100644 --- a/pkg/cmdrunner/shparse.go +++ b/pkg/cmdrunner/shparse.go @@ -164,6 +164,7 @@ func EvalMetaCommand(ctx context.Context, origPk *scpacket.FeCommandPacketType) rtnPk.MetaSubCmd = metaSubCmd rtnPk.Kwargs = make(map[string]string) rtnPk.UIContext = origPk.UIContext + rtnPk.RawStr = origPk.RawStr for key, val := range origPk.Kwargs { rtnPk.Kwargs[key] = val } diff --git a/pkg/remote/remote.go b/pkg/remote/remote.go index d2b889a8..6d077fcc 100644 --- a/pkg/remote/remote.go +++ b/pkg/remote/remote.go @@ -927,14 +927,14 @@ func (msh *MShellProc) Launch() { msh.MakeClientCancelFn = makeClientCancelFn go msh.NotifyRemoteUpdate() }) - cproc, uname, err := shexec.MakeClientProc(makeClientCtx, ecmd) + cproc, initPk, err := shexec.MakeClientProc(makeClientCtx, ecmd) var mshellVersion string msh.WithLock(func() { - msh.UName = uname msh.MakeClientCancelFn = nil - if cproc != nil && cproc.InitPk != nil { - msh.Remote.InitPk = cproc.InitPk - mshellVersion = cproc.InitPk.Version + if initPk != nil { + msh.Remote.InitPk = initPk + msh.UName = initPk.UName + mshellVersion = initPk.Version if semver.Compare(mshellVersion, MShellVersion) < 0 { // only set NeedsMShellUpgrade if we got an InitPk msh.NeedsMShellUpgrade = true diff --git a/pkg/scpacket/scpacket.go b/pkg/scpacket/scpacket.go index aab8a9e3..c27d367c 100644 --- a/pkg/scpacket/scpacket.go +++ b/pkg/scpacket/scpacket.go @@ -1,8 +1,11 @@ package scpacket import ( + "fmt" "reflect" + "strings" + "github.com/alessio/shellescape" "github.com/scripthaus-dev/mshell/pkg/base" "github.com/scripthaus-dev/mshell/pkg/packet" "github.com/scripthaus-dev/sh2-server/pkg/sstore" @@ -19,10 +22,33 @@ type FeCommandPacketType struct { MetaSubCmd string `json:"metasubcmd,omitempty"` Args []string `json:"args,omitempty"` Kwargs map[string]string `json:"kwargs,omitempty"` + RawStr string `json:"rawstr,omitempty"` UIContext *UIContextType `json:"uicontext,omitempty"` Interactive bool `json:"interactive"` } +func (pk *FeCommandPacketType) GetRawStr() string { + if pk.RawStr != "" { + return pk.RawStr + } + cmd := "/" + pk.MetaCmd + if pk.MetaSubCmd != "" { + cmd = cmd + ":" + pk.MetaSubCmd + } + var args []string + for k, v := range pk.Kwargs { + argStr := fmt.Sprintf("%s=%s", shellescape.Quote(k), shellescape.Quote(v)) + args = append(args, argStr) + } + for _, arg := range pk.Args { + args = append(args, shellescape.Quote(arg)) + } + if len(args) == 0 { + return cmd + } + return cmd + " " + strings.Join(args, " ") +} + type UIContextType struct { SessionId string `json:"sessionid"` ScreenId string `json:"screenid"`