diff --git a/pkg/cmdrunner/cmdrunner.go b/pkg/cmdrunner/cmdrunner.go index 565aca6b2..25e11f7fa 100644 --- a/pkg/cmdrunner/cmdrunner.go +++ b/pkg/cmdrunner/cmdrunner.go @@ -98,7 +98,6 @@ func init() { registerCmdFn("remote:new", RemoteNewCommand) registerCmdFn("remote:archive", RemoteArchiveCommand) registerCmdFn("remote:set", RemoteSetCommand) - registerCmdAlias("remote:edit", RemoteSetCommand) registerCmdFn("remote:disconnect", RemoteDisconnectCommand) registerCmdFn("remote:connect", RemoteConnectCommand) registerCmdFn("remote:install", RemoteInstallCommand) diff --git a/pkg/remote/remote.go b/pkg/remote/remote.go index aa96f9046..6583710a7 100644 --- a/pkg/remote/remote.go +++ b/pkg/remote/remote.go @@ -23,6 +23,7 @@ import ( "github.com/scripthaus-dev/mshell/pkg/base" "github.com/scripthaus-dev/mshell/pkg/packet" "github.com/scripthaus-dev/mshell/pkg/shexec" + "github.com/scripthaus-dev/sh2-server/pkg/scbase" "github.com/scripthaus-dev/sh2-server/pkg/scpacket" "github.com/scripthaus-dev/sh2-server/pkg/sstore" "golang.org/x/mod/semver" @@ -88,6 +89,7 @@ type MShellProc struct { ControllingPty *os.File PtyBuffer *circbuf.Buffer MakeClientCancelFn context.CancelFunc + DefaultState *packet.ShellState // install InstallStatus string @@ -140,6 +142,12 @@ func (msh *MShellProc) GetStatus() string { return msh.Status } +func (msh *MShellProc) GetDefaultState() *packet.ShellState { + msh.Lock.Lock() + defer msh.Lock.Unlock() + return msh.DefaultState +} + func (msh *MShellProc) GetRemoteId() string { msh.Lock.Lock() defer msh.Lock.Unlock() @@ -475,7 +483,7 @@ func (msh *MShellProc) GetRemoteRuntimeState() RemoteRuntimeState { vars["color"] = msh.Remote.RemoteOpts.Color } if msh.ServerProc != nil && msh.ServerProc.InitPk != nil { - state.DefaultState = msh.ServerProc.InitPk.State + state.DefaultState = msh.DefaultState state.MShellVersion = msh.ServerProc.InitPk.Version vars["home"] = msh.ServerProc.InitPk.HomeDir vars["remoteuser"] = msh.ServerProc.InitPk.User @@ -863,7 +871,7 @@ func (msh *MShellProc) RunInstall() { msgFn := func(msg string) { msh.WriteToPtyBuffer("%s", msg) } - err = shexec.RunInstallFromCmd(clientCtx, ecmd, true, "", msgFn) + err = shexec.RunInstallFromCmd(clientCtx, ecmd, true, nil, scbase.MShellBinaryFromPackage, msgFn) if err == context.Canceled { msh.WriteToPtyBuffer("*install canceled\n") msh.WithLock(func() { @@ -906,10 +914,35 @@ func (msh *MShellProc) ReInit(ctx context.Context) (*packet.InitPacketType, erro } msh.WithLock(func() { msh.Remote.InitPk = initPk + msh.DefaultState = initPk.State }) return initPk, nil } +func addScVarsToState(state *packet.ShellState) *packet.ShellState { + if state == nil { + return nil + } + rtn := *state + envMap := shexec.DeclMapFromState(&rtn) + envMap["SCRIPTHAUS"] = &shexec.DeclareDeclType{Name: "SCRIPTHAUS", Value: "1"} + envMap["SCRIPTHAUS_VERSION"] = &shexec.DeclareDeclType{Name: "SCRIPTHAUS_VERSION", Value: scbase.ScriptHausVersion} + rtn.ShellVars = shexec.SerializeDeclMap(envMap) + return &rtn +} + +func stripScVarsFromState(state *packet.ShellState) *packet.ShellState { + if state == nil { + return nil + } + rtn := *state + envMap := shexec.DeclMapFromState(&rtn) + delete(envMap, "SCRIPTHAUS") + delete(envMap, "SCRIPTHAUS_VERSION") + rtn.ShellVars = shexec.SerializeDeclMap(envMap) + return &rtn +} + func (msh *MShellProc) Launch() { remoteCopy := msh.GetRemoteCopy() if remoteCopy.Archived { @@ -968,6 +1001,7 @@ func (msh *MShellProc) Launch() { msh.WithLock(func() { msh.MakeClientCancelFn = nil if initPk != nil { + msh.DefaultState = initPk.State msh.Remote.InitPk = initPk msh.UName = initPk.UName mshellVersion = initPk.Version @@ -1021,15 +1055,6 @@ func (msh *MShellProc) IsConnected() bool { return msh.Status == StatusConnected } -func (msh *MShellProc) GetDefaultState() *packet.ShellState { - msh.Lock.Lock() - defer msh.Lock.Unlock() - if msh.ServerProc == nil || msh.ServerProc.InitPk == nil { - return nil - } - return msh.ServerProc.InitPk.State -} - func replaceHomePath(pathStr string, homeDir string) string { if homeDir == "" { return pathStr @@ -1172,12 +1197,12 @@ func RunCommand(ctx context.Context, sessionId string, windowId string, remotePt return nil, nil, fmt.Errorf("cannot get current remote state: %w", err) } if currentState == nil { - currentState = msh.ServerProc.InitPk.State + currentState = msh.GetDefaultState() } if currentState == nil { return nil, nil, fmt.Errorf("cannot run command, no valid remote state") } - runPacket.State = currentState + runPacket.State = addScVarsToState(currentState) runPacket.StateComplete = true msh.ServerProc.Output.RegisterRpc(runPacket.ReqId) err = shexec.SendRunPacketAndRunData(ctx, msh.ServerProc.Input, runPacket) @@ -1203,12 +1228,13 @@ func RunCommand(ctx context.Context, sessionId string, windowId string, remotePt if runPacket.Detached { status = sstore.CmdStatusDetached } + cmdState := stripScVarsFromState(runPacket.State) cmd := &sstore.CmdType{ SessionId: runPacket.CK.GetSessionId(), CmdId: runPacket.CK.GetCmdId(), CmdStr: runPacket.Command, Remote: remotePtr, - RemoteState: *runPacket.State, + RemoteState: *cmdState, TermOpts: makeTermOpts(runPacket), Status: status, StartPk: startPk, @@ -1347,6 +1373,9 @@ func (msh *MShellProc) notifyHangups_nolock() { func (msh *MShellProc) handleCmdDonePacket(donePk *packet.CmdDonePacketType) { // this will remove from RunningCmds and from PendingStateCmds defer msh.RemoveRunningCmd(donePk.CK) + if donePk.FinalState != nil { + donePk.FinalState = stripScVarsFromState(donePk.FinalState) + } update, err := sstore.UpdateCmdDonePk(context.Background(), donePk) if err != nil { diff --git a/pkg/scbase/scbase.go b/pkg/scbase/scbase.go index dac39888e..13004c9b3 100644 --- a/pkg/scbase/scbase.go +++ b/pkg/scbase/scbase.go @@ -3,6 +3,7 @@ package scbase import ( "errors" "fmt" + "io" "io/fs" "log" "os" @@ -12,6 +13,7 @@ import ( "github.com/google/uuid" "github.com/scripthaus-dev/mshell/pkg/base" + "golang.org/x/mod/semver" "golang.org/x/sys/unix" ) @@ -20,10 +22,13 @@ const ScHomeVarName = "SCRIPTHAUS_HOME" const SessionsDirBaseName = "sessions" const SCLockFile = "sh2.lock" const ScriptHausDirName = "scripthaus" +const ScriptHausAppPathVarName = "SCRIPTHAUS_APP_PATH" +const ScriptHausVersion = "v0.1.0" var SessionDirCache = make(map[string]string) var BaseLock = &sync.Mutex{} +// must match js func GetScHomeDir() string { scHome := os.Getenv(ScHomeVarName) if scHome == "" { @@ -36,6 +41,28 @@ func GetScHomeDir() string { return scHome } +func MShellBinaryFromPackage(version string, goos string, goarch string) (io.ReadCloser, error) { + appPath := os.Getenv(ScriptHausAppPathVarName) + if appPath == "" { + return base.MShellBinaryFromOptDir(version, goos, goarch) + } + if !base.ValidGoArch(goos, goarch) { + return nil, fmt.Errorf("invalid goos/goarch combination: %s/%s", goos, goarch) + } + versionStr := semver.MajorMinor(version) + if versionStr == "" { + return nil, fmt.Errorf("invalid mshell version: %q", version) + } + fileName := fmt.Sprintf("mshell-%s-%s.%s", versionStr, goos, goarch) + fullFileName := path.Join(appPath, "bin", "mshell", fileName) + log.Printf("mshell-binary %q\n", fullFileName) + fd, err := os.Open(fullFileName) + if err != nil { + return nil, fmt.Errorf("cannot open mshell binary %q: %v", fullFileName, err) + } + return fd, nil +} + func AcquireSCLock() (*os.File, error) { homeDir := GetScHomeDir() err := ensureDir(homeDir)