From 249bf88a4d1ca71ae8609f9ac8d3ca74a55f7e4b Mon Sep 17 00:00:00 2001 From: sawka Date: Wed, 17 Aug 2022 12:24:09 -0700 Subject: [PATCH] testing a 2nd remote with mshell --server --- cmd/main-server.go | 5 +++ db/schema.sql | 24 +++++++++++-- pkg/cmdrunner/cmdrunner.go | 71 +++++++++++++++++++++++++++++++++----- pkg/remote/remote.go | 53 +++++++++++++++++++++++----- pkg/sstore/dbops.go | 27 +++++++++++++++ pkg/sstore/sstore.go | 42 +++++++++++++++++++--- pkg/sstore/updatebus.go | 5 --- 7 files changed, 198 insertions(+), 29 deletions(-) diff --git a/cmd/main-server.go b/cmd/main-server.go index 320893be..cfc23754 100644 --- a/cmd/main-server.go +++ b/cmd/main-server.go @@ -451,6 +451,11 @@ func main() { fmt.Printf("[error] ensuring local remote: %v\n", err) return } + err = sstore.AddTest01Remote(context.Background()) + if err != nil { + fmt.Printf("[error] ensuring test01 remote: %v\n", err) + return + } _, err = sstore.EnsureDefaultSession(context.Background()) if err != nil { fmt.Printf("[error] ensuring default session: %v\n", err) diff --git a/db/schema.sql b/db/schema.sql index 043cc348..7a32d70c 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -1,17 +1,28 @@ CREATE TABLE schema_migrations (version uint64,dirty bool); CREATE UNIQUE INDEX version_unique ON schema_migrations (version); +CREATE TABLE client ( + userid varchar(36) NOT NULL, + userpublickeybytes blob NOT NULL, + userprivatekeybytes blob NOT NULL +); CREATE TABLE session ( sessionid varchar(36) PRIMARY KEY, name varchar(50) NOT NULL, sessionidx int NOT NULL, activescreenid varchar(36) NOT NULL, - notifynum int NOT NULL + notifynum int NOT NULL, + owneruserid varchar(36) NOT NULL, + sharemode varchar(12) NOT NULL, + accesskey varchar(36) NOT NULL ); CREATE TABLE window ( sessionid varchar(36) NOT NULL, windowid varchar(36) NOT NULL, curremote varchar(50) NOT NULL, winopts json NOT NULL, + owneruserid varchar(36) NOT NULL, + sharemode varchar(12) NOT NULL, + shareopts json NOT NULL, PRIMARY KEY (sessionid, windowid) ); CREATE TABLE screen ( @@ -21,6 +32,8 @@ CREATE TABLE screen ( activewindowid varchar(36) NOT NULL, screenidx int NOT NULL, screenopts json NOT NULL, + owneruserid varchar(36) NOT NULL, + sharemode varchar(12) NOT NULL, PRIMARY KEY (sessionid, screenid) ); CREATE TABLE screen_window ( @@ -43,7 +56,7 @@ CREATE TABLE remote_instance ( CREATE TABLE line ( sessionid varchar(36) NOT NULL, windowid varchar(36) NOT NULL, - lineid int NOT NULL, + lineid varchar(36) NOT NULL, userid varchar(36) NOT NULL, ts bigint NOT NULL, linetype varchar(10) NOT NULL, @@ -53,8 +66,13 @@ CREATE TABLE line ( ); CREATE TABLE remote ( remoteid varchar(36) PRIMARY KEY, + physicalid varchar(36) NOT NULL, remotetype varchar(10) NOT NULL, - remotename varchar(50) NOT NULL, + remotealias varchar(50) NOT NULL, + remotecanonicalname varchar(200) NOT NULL, + remotesudo boolean NOT NULL, + remoteuser varchar(50) NOT NULL, + remotehost varchar(200) NOT NULL, autoconnect boolean NOT NULL, initpk json NOT NULL, sshopts json NOT NULL, diff --git a/pkg/cmdrunner/cmdrunner.go b/pkg/cmdrunner/cmdrunner.go index 479ed4a3..03909ef0 100644 --- a/pkg/cmdrunner/cmdrunner.go +++ b/pkg/cmdrunner/cmdrunner.go @@ -88,6 +88,9 @@ func HandleCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstor case "cd": return CdCommand(ctx, pk) + case "cr": + return CrCommand(ctx, pk) + case "compgen": return CompGenCommand(ctx, pk) @@ -189,8 +192,8 @@ func resolveScreenId(ctx context.Context, pk *scpacket.FeCommandPacketType, sess return resolveSessionScreen(ctx, sessionId, screenArg) } -func resolveRemote(ctx context.Context, pk *scpacket.FeCommandPacketType, sessionId string, windowId string) (string, string, *sstore.RemoteState, error) { - remoteName := pk.Kwargs["remote"] +// returns (remoteName, remoteId, state, err) +func resolveRemote(ctx context.Context, remoteName string, sessionId string, windowId string) (string, string, *sstore.RemoteState, error) { if remoteName == "" { return "", "", nil, nil } @@ -242,7 +245,7 @@ func resolveIds(ctx context.Context, pk *scpacket.FeCommandPacketType, rtype int } } if (rtype&R_Remote)+(rtype&R_RemoteOpt) > 0 { - rtn.RemoteName, rtn.RemoteId, rtn.RemoteState, err = resolveRemote(ctx, pk, rtn.SessionId, rtn.WindowId) + rtn.RemoteName, rtn.RemoteId, rtn.RemoteState, err = resolveRemote(ctx, pk.Kwargs["remote"], rtn.SessionId, rtn.WindowId) if err != nil { return rtn, err } @@ -332,6 +335,9 @@ func evalCommandInternal(ctx context.Context, pk *scpacket.FeCommandPacketType) if commandStr == "cd" || strings.HasPrefix(commandStr, "cd ") { metaCmd = "cd" commandStr = strings.TrimSpace(commandStr[2:]) + } else if commandStr == "cr" || strings.HasPrefix(commandStr, "cr ") { + metaCmd = "cr" + commandStr = strings.TrimSpace(commandStr[2:]) } else if commandStr[0] == '/' { spaceIdx := strings.Index(commandStr, " ") if spaceIdx == -1 { @@ -419,14 +425,67 @@ func ScreenCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstor return update, nil } +func CrCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) { + ids, err := resolveIds(ctx, pk, R_Session|R_Window) + if err != nil { + return nil, fmt.Errorf("/cr error: %w", err) + } + newRemote := firstArg(pk) + if newRemote == "" { + return nil, nil + } + remoteName, remoteId, _, err := resolveRemote(ctx, newRemote, ids.SessionId, ids.WindowId) + fmt.Printf("found: name[%s] id[%s] err[%v]\n", remoteName, remoteId, err) + if err != nil { + return nil, err + } + if remoteId == "" { + return nil, fmt.Errorf("/cr error: remote not found") + } + err = sstore.UpdateCurRemote(ctx, ids.SessionId, ids.WindowId, remoteName) + if err != nil { + return nil, fmt.Errorf("/cr error: cannot update curremote: %w", err) + } + update := sstore.WindowUpdate{ + Window: sstore.WindowType{ + SessionId: ids.SessionId, + WindowId: ids.WindowId, + CurRemote: remoteName, + }, + Info: &sstore.InfoMsgType{ + InfoMsg: fmt.Sprintf("current remote = %s", remoteName), + TimeoutMs: 2000, + }, + } + return update, nil +} + func CdCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) { ids, err := resolveIds(ctx, pk, R_Session|R_Window|R_Remote) if err != nil { return nil, fmt.Errorf("/cd error: %w", err) } newDir := firstArg(pk) + curRemote := remote.GetRemoteById(ids.RemoteId) + if curRemote == nil { + return nil, fmt.Errorf("invalid remote, cannot change directory") + } + if !curRemote.IsConnected() { + return nil, fmt.Errorf("remote is not connected, cannot change directory") + } if newDir == "" { - return nil, nil + if ids.RemoteState == nil { + return nil, fmt.Errorf("remote state is not available") + } + return sstore.InfoUpdate{ + Info: &sstore.InfoMsgType{ + InfoMsg: fmt.Sprintf("[%s] current directory = %s", ids.RemoteName, ids.RemoteState.Cwd), + }, + }, nil + } + newDir, err = curRemote.ExpandHomeDir(newDir) + if err != nil { + return nil, err } if !strings.HasPrefix(newDir, "/") { if ids.RemoteState == nil { @@ -442,10 +501,6 @@ func CdCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.Up cdPacket := packet.MakeCdPacket() cdPacket.ReqId = uuid.New().String() cdPacket.Dir = newDir - curRemote := remote.GetRemoteById(ids.RemoteId) - if curRemote == nil { - return nil, fmt.Errorf("invalid remote, cannot execute command") - } resp, err := curRemote.PacketRpc(ctx, cdPacket) if err != nil { return nil, err diff --git a/pkg/remote/remote.go b/pkg/remote/remote.go index 7d168736..c90ebd87 100644 --- a/pkg/remote/remote.go +++ b/pkg/remote/remote.go @@ -5,7 +5,8 @@ import ( "encoding/base64" "errors" "fmt" - "os/exec" + "path" + "strings" "sync" "github.com/scripthaus-dev/mshell/pkg/base" @@ -19,6 +20,17 @@ const DefaultTermRows = 25 const DefaultTermCols = 80 const DefaultTerm = "xterm-256color" +const MShellServerCommand = ` +PATH=$PATH:~/.mshell; +which mshell > /dev/null; +if [[ "$?" -ne 0 ]] +then + printf "\n##N{\"type\": \"init\", \"notfound\": true, \"uname\": \"%s | %s\"}\n" "$(uname -s)" "$(uname -m)" +else + mshell --server +fi +` + const ( StatusInit = "init" StatusConnected = "connected" @@ -154,17 +166,23 @@ func MakeMShell(r *sstore.RemoteType) *MShellProc { return rtn } +func convertSSHOpts(opts *sstore.SSHOpts) shexec.SSHOpts { + if opts == nil { + return shexec.SSHOpts{} + } + return shexec.SSHOpts{ + SSHHost: opts.SSHHost, + SSHOptsStr: opts.SSHOptsStr, + SSHIdentity: opts.SSHIdentity, + SSHUser: opts.SSHUser, + } +} + func (msh *MShellProc) Launch() { msh.Lock.Lock() defer msh.Lock.Unlock() - msPath, err := base.GetMShellPath() - if err != nil { - msh.Status = StatusError - msh.Err = err - return - } - ecmd := exec.Command(msPath, "--server") + ecmd := convertSSHOpts(msh.Remote.SSHOpts).MakeSSHExecCmd(MShellServerCommand) cproc, err := shexec.MakeClientProc(ecmd) if err != nil { msh.Status = StatusError @@ -203,6 +221,25 @@ func (msh *MShellProc) GetDefaultState() *sstore.RemoteState { return &sstore.RemoteState{Cwd: msh.ServerProc.InitPk.HomeDir} } +func (msh *MShellProc) ExpandHomeDir(pathStr string) (string, error) { + if pathStr != "~" && !strings.HasPrefix(pathStr, "~/") { + return pathStr, nil + } + msh.Lock.Lock() + defer msh.Lock.Unlock() + if msh.ServerProc.InitPk == nil { + return "", fmt.Errorf("remote not connected, does not have home directory set for ~ expansion") + } + homeDir := msh.ServerProc.InitPk.HomeDir + if homeDir == "" { + return "", fmt.Errorf("remote does not have HOME set, cannot do ~ expansion") + } + if pathStr == "~" { + return homeDir, nil + } + return path.Join(homeDir, pathStr[2:]), nil +} + func (msh *MShellProc) IsCmdRunning(ck base.CommandKey) bool { msh.Lock.Lock() defer msh.Lock.Unlock() diff --git a/pkg/sstore/dbops.go b/pkg/sstore/dbops.go index a9b5c434..14fde7c0 100644 --- a/pkg/sstore/dbops.go +++ b/pkg/sstore/dbops.go @@ -40,6 +40,20 @@ func GetAllRemotes(ctx context.Context) ([]*RemoteType, error) { return rtn, nil } +func GetRemoteByAlias(ctx context.Context, alias string) (*RemoteType, error) { + var remote *RemoteType + err := WithTx(ctx, func(tx *TxWrap) error { + query := `SELECT * FROM remote WHERE remotealias = ?` + m := tx.GetMap(query, alias) + remote = RemoteFromMap(m) + return nil + }) + if err != nil { + return nil, err + } + return remote, nil +} + func GetRemoteById(ctx context.Context, remoteId string) (*RemoteType, error) { var remote *RemoteType err := WithTx(ctx, func(tx *TxWrap) error { @@ -573,3 +587,16 @@ func UpdateRemoteCwd(ctx context.Context, rname string, sessionId string, window }) return &ri, txErr } + +func UpdateCurRemote(ctx context.Context, sessionId string, windowId string, remoteName string) error { + txErr := WithTx(ctx, func(tx *TxWrap) error { + query := `SELECT windowid FROM window WHERE sessionid = ? AND windowid = ?` + if !tx.Exists(query, sessionId, windowId) { + return fmt.Errorf("cannot update curremote, no window found") + } + query = `UPDATE window SET curremote = ? WHERE sessionid = ? AND windowid = ?` + tx.ExecWrap(query, remoteName, sessionId, windowId) + return nil + }) + return txErr +} diff --git a/pkg/sstore/sstore.go b/pkg/sstore/sstore.go index 47401eaa..04656982 100644 --- a/pkg/sstore/sstore.go +++ b/pkg/sstore/sstore.go @@ -432,13 +432,13 @@ func AddCmdLine(ctx context.Context, sessionId string, windowId string, userId s } func EnsureLocalRemote(ctx context.Context) error { - remoteId, err := base.GetRemoteId() + physicalId, err := base.GetRemoteId() if err != nil { - return fmt.Errorf("getting local remoteid: %w", err) + return fmt.Errorf("getting local physical remoteid: %w", err) } - remote, err := GetRemoteById(ctx, remoteId) + remote, err := GetRemoteByPhysicalId(ctx, physicalId) if err != nil { - return fmt.Errorf("getting remote[%s] from db: %w", remoteId, err) + return fmt.Errorf("getting remote[%s] from db: %w", physicalId, err) } if remote != nil { return nil @@ -453,7 +453,8 @@ func EnsureLocalRemote(ctx context.Context) error { } // create the local remote localRemote := &RemoteType{ - RemoteId: remoteId, + RemoteId: uuid.New().String(), + PhysicalId: physicalId, RemoteType: "ssh", RemoteAlias: LocalRemoteName, RemoteCanonicalName: fmt.Sprintf("%s@%s", user.Username, hostName), @@ -470,6 +471,37 @@ func EnsureLocalRemote(ctx context.Context) error { return nil } +func AddTest01Remote(ctx context.Context) error { + remote, err := GetRemoteByAlias(ctx, "test01") + if err != nil { + return fmt.Errorf("getting remote[test01] from db: %w", err) + } + if remote != nil { + return nil + } + testRemote := &RemoteType{ + RemoteId: uuid.New().String(), + RemoteType: "ssh", + RemoteAlias: "test01", + RemoteCanonicalName: "ubuntu@test01.ec2", + RemoteSudo: false, + RemoteUser: "ubuntu", + RemoteHost: "test01.ec2", + SSHOpts: &SSHOpts{ + SSHHost: "test01.ec2", + SSHUser: "ubuntu", + SSHIdentity: "/Users/mike/aws/mfmt.pem", + }, + AutoConnect: true, + } + err = InsertRemote(ctx, testRemote) + if err != nil { + return err + } + log.Printf("[db] added remote '%s', id=%s\n", testRemote.GetName(), testRemote.RemoteId) + return nil +} + func EnsureDefaultSession(ctx context.Context) (*SessionType, error) { session, err := GetSessionByName(ctx, DefaultSessionName) if err != nil { diff --git a/pkg/sstore/updatebus.go b/pkg/sstore/updatebus.go index 6915fa2e..3ec9708c 100644 --- a/pkg/sstore/updatebus.go +++ b/pkg/sstore/updatebus.go @@ -16,11 +16,6 @@ type UpdatePacket interface { UpdateType() string } -type UpdateCmd struct { - CmdId string - Status string -} - type PtyDataUpdate struct { SessionId string `json:"sessionid"` CmdId string `json:"cmdid"`