diff --git a/pkg/cmdrunner/cmdrunner.go b/pkg/cmdrunner/cmdrunner.go index 733eb6f3..7e4d2f68 100644 --- a/pkg/cmdrunner/cmdrunner.go +++ b/pkg/cmdrunner/cmdrunner.go @@ -226,9 +226,8 @@ func RunCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.U runPacket := packet.MakeRunPacket() runPacket.ReqId = uuid.New().String() runPacket.CK = base.MakeCommandKey(ids.SessionId, cmdId) - runPacket.Cwd = ids.Remote.RemoteState.Cwd - runPacket.Env0 = ids.Remote.RemoteState.Env0 - runPacket.EnvComplete = true + runPacket.State = ids.Remote.RemoteState + runPacket.StateComplete = true runPacket.UsePty = true runPacket.TermOpts = &packet.TermOpts{Rows: shexec.DefaultTermRows, Cols: shexec.DefaultTermCols, Term: remote.DefaultTerm, MaxPtySize: shexec.DefaultMaxPtySize} if pk.UIContext != nil && pk.UIContext.TermOpts != nil { diff --git a/pkg/cmdrunner/resolver.go b/pkg/cmdrunner/resolver.go index 0db0dd41..2ab26915 100644 --- a/pkg/cmdrunner/resolver.go +++ b/pkg/cmdrunner/resolver.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/google/uuid" + "github.com/scripthaus-dev/mshell/pkg/packet" "github.com/scripthaus-dev/sh2-server/pkg/remote" "github.com/scripthaus-dev/sh2-server/pkg/scpacket" "github.com/scripthaus-dev/sh2-server/pkg/sstore" @@ -33,7 +34,7 @@ type ResolvedRemote struct { RemotePtr sstore.RemotePtrType MShell *remote.MShellProc RState remote.RemoteRuntimeState - RemoteState *sstore.RemoteState + RemoteState *packet.ShellState RemoteCopy *sstore.RemoteType } @@ -471,7 +472,7 @@ func resolveRemoteFromPtr(ctx context.Context, rptr *sstore.RemotePtrType, sessi } // returns (remoteDisplayName, remoteptr, state, rstate, err) -func resolveRemote(ctx context.Context, fullRemoteRef string, sessionId string, windowId string) (string, *sstore.RemotePtrType, *sstore.RemoteState, *remote.RemoteRuntimeState, error) { +func resolveRemote(ctx context.Context, fullRemoteRef string, sessionId string, windowId string) (string, *sstore.RemotePtrType, *packet.ShellState, *remote.RemoteRuntimeState, error) { if fullRemoteRef == "" { return "", nil, nil, nil, nil } diff --git a/pkg/remote/remote.go b/pkg/remote/remote.go index b1ea6983..d2b889a8 100644 --- a/pkg/remote/remote.go +++ b/pkg/remote/remote.go @@ -36,8 +36,8 @@ const RemoteTermRows = 8 const RemoteTermCols = 80 const PtyReadBufSize = 100 -const MShellVersion = "v0.1.0" -const MShellVersionConstraint = "^0.1" +const MShellVersion = "v0.2.0" +const MShellVersionConstraint = "^0.2" const MShellServerCommandFmt = ` PATH=$PATH:~/.mshell; @@ -98,26 +98,26 @@ type MShellProc struct { } type RemoteRuntimeState struct { - RemoteType string `json:"remotetype"` - RemoteId string `json:"remoteid"` - PhysicalId string `json:"physicalremoteid"` - RemoteAlias string `json:"remotealias,omitempty"` - RemoteCanonicalName string `json:"remotecanonicalname"` - RemoteVars map[string]string `json:"remotevars"` - Status string `json:"status"` - ErrorStr string `json:"errorstr,omitempty"` - InstallStatus string `json:"installstatus"` - InstallErrorStr string `json:"installerrorstr,omitempty"` - NeedsMShellUpgrade bool `json:"needsmshellupgrade,omitempty"` - DefaultState *sstore.RemoteState `json:"defaultstate"` - ConnectMode string `json:"connectmode"` - AutoInstall bool `json:"autoinstall"` - Archived bool `json:"archived,omitempty"` - RemoteIdx int64 `json:"remoteidx"` - UName string `json:"uname"` - MShellVersion string `json:"mshellversion"` - WaitingForPassword bool `json:"waitingforpassword,omitempty"` - Local bool `json:"local,omitempty"` + RemoteType string `json:"remotetype"` + RemoteId string `json:"remoteid"` + PhysicalId string `json:"physicalremoteid"` + RemoteAlias string `json:"remotealias,omitempty"` + RemoteCanonicalName string `json:"remotecanonicalname"` + RemoteVars map[string]string `json:"remotevars"` + Status string `json:"status"` + ErrorStr string `json:"errorstr,omitempty"` + InstallStatus string `json:"installstatus"` + InstallErrorStr string `json:"installerrorstr,omitempty"` + NeedsMShellUpgrade bool `json:"needsmshellupgrade,omitempty"` + DefaultState *packet.ShellState `json:"defaultstate"` + ConnectMode string `json:"connectmode"` + AutoInstall bool `json:"autoinstall"` + Archived bool `json:"archived,omitempty"` + RemoteIdx int64 `json:"remoteidx"` + UName string `json:"uname"` + MShellVersion string `json:"mshellversion"` + WaitingForPassword bool `json:"waitingforpassword,omitempty"` + Local bool `json:"local,omitempty"` } func (state RemoteRuntimeState) IsConnected() bool { @@ -465,10 +465,7 @@ func (msh *MShellProc) GetRemoteRuntimeState() RemoteRuntimeState { vars["color"] = msh.Remote.RemoteOpts.Color } if msh.ServerProc != nil && msh.ServerProc.InitPk != nil { - state.DefaultState = &sstore.RemoteState{ - Cwd: msh.ServerProc.InitPk.Cwd, - Env0: msh.ServerProc.InitPk.Env0, - } + state.DefaultState = msh.ServerProc.InitPk.State state.MShellVersion = msh.ServerProc.InitPk.Version vars["home"] = msh.ServerProc.InitPk.HomeDir vars["remoteuser"] = msh.ServerProc.InitPk.User @@ -512,7 +509,7 @@ func GetAllRemoteRuntimeState() []RemoteRuntimeState { return rtn } -func GetDefaultRemoteStateById(remoteId string) (*sstore.RemoteState, error) { +func GetDefaultRemoteStateById(remoteId string) (*packet.ShellState, error) { remote := GetRemoteById(remoteId) if remote == nil { return nil, fmt.Errorf("remote not found") @@ -988,13 +985,13 @@ func (msh *MShellProc) IsConnected() bool { return msh.Status == StatusConnected } -func (msh *MShellProc) GetDefaultState() *sstore.RemoteState { +func (msh *MShellProc) GetDefaultState() *packet.ShellState { msh.Lock.Lock() defer msh.Lock.Unlock() if msh.ServerProc == nil || msh.ServerProc.InitPk == nil { return nil } - return &sstore.RemoteState{Cwd: msh.ServerProc.InitPk.HomeDir, Env0: msh.ServerProc.InitPk.Env0} + return msh.ServerProc.InitPk.State } func replaceHomePath(pathStr string, homeDir string) string { @@ -1060,7 +1057,7 @@ func makeTermOpts(runPk *packet.RunPacketType) sstore.TermOpts { } // returns (cmdtype, allow-updates-callback, err) -func RunCommand(ctx context.Context, cmdId string, remotePtr sstore.RemotePtrType, remoteState *sstore.RemoteState, runPacket *packet.RunPacketType) (*sstore.CmdType, func(), error) { +func RunCommand(ctx context.Context, cmdId string, remotePtr sstore.RemotePtrType, remoteState *packet.ShellState, runPacket *packet.RunPacketType) (*sstore.CmdType, func(), error) { if remotePtr.OwnerId != "" { return nil, nil, fmt.Errorf("cannot run command against another user's remote '%s'", remotePtr.MakeFullRemoteRef()) } @@ -1332,7 +1329,7 @@ func isDigit(r rune) bool { return r >= '0' && r <= '9' // just check ascii digits (not unicode) } -func EvalPrompt(promptFmt string, vars map[string]string, state *sstore.RemoteState) string { +func EvalPrompt(promptFmt string, vars map[string]string, state *packet.ShellState) string { var buf bytes.Buffer promptRunes := []rune(promptFmt) for i := 0; i < len(promptRunes); i++ { @@ -1373,7 +1370,7 @@ func EvalPrompt(promptFmt string, vars map[string]string, state *sstore.RemoteSt return buf.String() } -func evalPromptEsc(escCode string, vars map[string]string, state *sstore.RemoteState) string { +func evalPromptEsc(escCode string, vars map[string]string, state *packet.ShellState) string { if strings.HasPrefix(escCode, "x{") && strings.HasSuffix(escCode, "}") { varName := escCode[2 : len(escCode)-1] return vars[varName] diff --git a/pkg/sstore/dbops.go b/pkg/sstore/dbops.go index bf3fbb55..986c022c 100644 --- a/pkg/sstore/dbops.go +++ b/pkg/sstore/dbops.go @@ -300,9 +300,9 @@ func GetAllSessions(ctx context.Context) (*ModelUpdate, error) { screen.Windows = append(screen.Windows, sw) } query = `SELECT * FROM remote_instance` - var ris []*RemoteInstance - tx.SelectWrap(&ris, query) - for _, ri := range ris { + riMaps := tx.SelectMaps(query) + for _, m := range riMaps { + ri := RIFromMap(m) s := sessionMap[ri.SessionId] if s != nil { s.Remotes = append(s.Remotes, ri) @@ -776,13 +776,13 @@ func DeleteScreen(ctx context.Context, sessionId string, screenId string) (Updat return update, nil } -func GetRemoteState(ctx context.Context, sessionId string, windowId string, remotePtr RemotePtrType) (*RemoteState, error) { - var remoteState *RemoteState +func GetRemoteState(ctx context.Context, sessionId string, windowId string, remotePtr RemotePtrType) (*packet.ShellState, error) { + var remoteState *packet.ShellState txErr := WithTx(ctx, func(tx *TxWrap) error { - var ri RemoteInstance query := `SELECT * FROM remote_instance WHERE sessionid = ? AND windowid = ? AND remoteownerid = ? AND remoteid = ? AND name = ?` - found := tx.GetWrap(&ri, query, sessionId, windowId, remotePtr.OwnerId, remotePtr.RemoteId, remotePtr.Name) - if found { + m := tx.GetMap(query, sessionId, windowId, remotePtr.OwnerId, remotePtr.RemoteId, remotePtr.Name) + ri := RIFromMap(m) + if ri != nil { remoteState = &ri.State return nil } @@ -807,20 +807,21 @@ func validateSessionWindow(tx *TxWrap, sessionId string, windowId string) error } } -func UpdateRemoteState(ctx context.Context, sessionId string, windowId string, remotePtr RemotePtrType, state RemoteState) (*RemoteInstance, error) { +func UpdateRemoteState(ctx context.Context, sessionId string, windowId string, remotePtr RemotePtrType, state packet.ShellState) (*RemoteInstance, error) { if remotePtr.IsSessionScope() { windowId = "" } - var ri RemoteInstance + var ri *RemoteInstance txErr := WithTx(ctx, func(tx *TxWrap) error { err := validateSessionWindow(tx, sessionId, windowId) if err != nil { return fmt.Errorf("cannot update remote instance cwd: %w", err) } query := `SELECT * FROM remote_instance WHERE sessionid = ? AND windowid = ? AND remoteownerid = ? AND remoteid = ? AND name = ?` - found := tx.GetWrap(&ri, query, sessionId, windowId, remotePtr.OwnerId, remotePtr.RemoteId, remotePtr.Name) - if !found { - ri = RemoteInstance{ + m := tx.GetMap(query, sessionId, windowId, remotePtr.OwnerId, remotePtr.RemoteId, remotePtr.Name) + ri = RIFromMap(m) + if ri == nil { + ri = &RemoteInstance{ RIId: scbase.GenSCUUID(), Name: remotePtr.Name, SessionId: sessionId, @@ -831,15 +832,15 @@ func UpdateRemoteState(ctx context.Context, sessionId string, windowId string, r } query = `INSERT INTO remote_instance ( riid, name, sessionid, windowid, remoteownerid, remoteid, state) VALUES (:riid,:name,:sessionid,:windowid,:remoteownerid,:remoteid,:state)` - tx.NamedExecWrap(query, ri) + tx.NamedExecWrap(query, ri.ToMap()) return nil } - query = `UPDATE remote_instance SET state = ? WHERE sessionid = ? AND windowid = ? AND remoteownerid = ? AND remoteid = ? AND name = ?` + query = `UPDATE remote_instance SET state = ? WHERE riid = ?` ri.State = state - tx.ExecWrap(query, ri.State, ri.SessionId, ri.WindowId, remotePtr.OwnerId, remotePtr.RemoteId, remotePtr.Name) + tx.ExecWrap(query, quickJson(ri.State), ri.RIId) return nil }) - return &ri, txErr + return ri, txErr } func UpdateCurRemote(ctx context.Context, sessionId string, windowId string, remotePtr RemotePtrType) error { diff --git a/pkg/sstore/sstore.go b/pkg/sstore/sstore.go index 2eb38a3f..0350296c 100644 --- a/pkg/sstore/sstore.go +++ b/pkg/sstore/sstore.go @@ -440,19 +440,6 @@ type HistoryQueryOpts struct { FromTs int64 } -type RemoteState struct { - Cwd string `json:"cwd"` - Env0 []byte `json:"env0"` // "env -0" format -} - -func (s *RemoteState) Scan(val interface{}) error { - return quickScanJson(s, val) -} - -func (s RemoteState) Value() (driver.Value, error) { - return quickValueJson(s) -} - type TermOpts struct { Rows int64 `json:"rows"` Cols int64 `json:"cols"` @@ -469,18 +456,45 @@ func (opts TermOpts) Value() (driver.Value, error) { } type RemoteInstance struct { - RIId string `json:"riid"` - Name string `json:"name"` - SessionId string `json:"sessionid"` - WindowId string `json:"windowid"` - RemoteOwnerId string `json:"remoteownerid"` - RemoteId string `json:"remoteid"` - State RemoteState `json:"state"` + RIId string `json:"riid"` + Name string `json:"name"` + SessionId string `json:"sessionid"` + WindowId string `json:"windowid"` + RemoteOwnerId string `json:"remoteownerid"` + RemoteId string `json:"remoteid"` + State packet.ShellState `json:"state"` // only for updates Remove bool `json:"remove,omitempty"` } +func (ri *RemoteInstance) ToMap() map[string]interface{} { + rtn := make(map[string]interface{}) + rtn["riid"] = ri.RIId + rtn["name"] = ri.Name + rtn["sessionid"] = ri.SessionId + rtn["windowid"] = ri.WindowId + rtn["remoteownerid"] = ri.RemoteOwnerId + rtn["remoteid"] = ri.RemoteId + rtn["state"] = quickJson(ri.State) + return rtn +} + +func RIFromMap(m map[string]interface{}) *RemoteInstance { + if len(m) == 0 { + return nil + } + var ri RemoteInstance + quickSetStr(&ri.RIId, m, "riid") + quickSetStr(&ri.Name, m, "name") + quickSetStr(&ri.SessionId, m, "sessionid") + quickSetStr(&ri.WindowId, m, "windowid") + quickSetStr(&ri.RemoteOwnerId, m, "remoteownerid") + quickSetStr(&ri.RemoteId, m, "remoteid") + quickSetJson(&ri.State, m, "state") + return &ri +} + type LineType struct { SessionId string `json:"sessionid"` WindowId string `json:"windowid"` @@ -557,7 +571,7 @@ type CmdType struct { CmdId string `json:"cmdid"` Remote RemotePtrType `json:"remote"` CmdStr string `json:"cmdstr"` - RemoteState RemoteState `json:"remotestate"` + RemoteState packet.ShellState `json:"remotestate"` TermOpts TermOpts `json:"termopts"` OrigTermOpts TermOpts `json:"origtermopts"` Status string `json:"status"`