mirror of
https://github.com/wavetermdev/waveterm.git
synced 2024-12-22 16:48:23 +01:00
refactoring for versioned mshell binaries on remotes
This commit is contained in:
parent
4550e18b6b
commit
b5c67b6260
5
go.mod
5
go.mod
@ -10,4 +10,7 @@ require (
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad
|
||||
)
|
||||
|
||||
require github.com/Masterminds/semver/v3 v3.1.1 // indirect
|
||||
require (
|
||||
github.com/Masterminds/semver/v3 v3.1.1 // indirect
|
||||
golang.org/x/mod v0.5.1 // indirect
|
||||
)
|
||||
|
2
go.sum
2
go.sum
@ -8,5 +8,7 @@ github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwV
|
||||
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38=
|
||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
@ -417,14 +417,14 @@ func handleInstall() (int, error) {
|
||||
if !base.ValidGoArch(goos, goarch) {
|
||||
return 1, fmt.Errorf("invalid arch '%s' passed to mshell --install", fullArch)
|
||||
}
|
||||
optName := base.GoArchOptFile(goos, goarch)
|
||||
optName := base.GoArchOptFile(base.MShellVersion, goos, goarch)
|
||||
_, err = os.Stat(optName)
|
||||
if err != nil {
|
||||
return 1, fmt.Errorf("cannot install mshell to remote host, cannot read '%s': %w", optName, err)
|
||||
}
|
||||
opts.OptName = optName
|
||||
}
|
||||
err = shexec.RunInstallSSHCommand(opts)
|
||||
err = shexec.RunInstallFromOpts(opts)
|
||||
if err != nil {
|
||||
return 1, err
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
||||
const HomeVarName = "HOME"
|
||||
@ -26,10 +27,12 @@ const DefaultMShellHome = "~/.mshell"
|
||||
const DefaultMShellName = "mshell"
|
||||
const MShellPathVarName = "MSHELL_PATH"
|
||||
const MShellHomeVarName = "MSHELL_HOME"
|
||||
const MShellInstallBinVarName = "MSHELL_INSTALLBIN_PATH"
|
||||
const SSHCommandVarName = "SSH_COMMAND"
|
||||
const SessionsDirBaseName = "sessions"
|
||||
const MShellVersion = "v0.1.0"
|
||||
const RemoteIdFile = "remoteid"
|
||||
const DefaultMShellInstallBinDir = "/opt/mshell/bin"
|
||||
|
||||
var sessionDirCache = make(map[string]string)
|
||||
var baseLock = &sync.Mutex{}
|
||||
@ -229,8 +232,17 @@ func ValidGoArch(goos string, goarch string) bool {
|
||||
return (goos == "darwin" || goos == "linux") && (goarch == "amd64" || goarch == "arm64")
|
||||
}
|
||||
|
||||
func GoArchOptFile(goos string, goarch string) string {
|
||||
return fmt.Sprintf("/opt/mshell/bin/mshell.%s.%s", goos, goarch)
|
||||
func GoArchOptFile(version string, goos string, goarch string) string {
|
||||
installBinDir := os.Getenv(MShellInstallBinVarName)
|
||||
if installBinDir == "" {
|
||||
installBinDir = DefaultMShellInstallBinDir
|
||||
}
|
||||
versionStr := semver.MajorMinor(version)
|
||||
if versionStr == "" {
|
||||
versionStr = "unknown"
|
||||
}
|
||||
binBaseName := fmt.Sprintf("mshell-%s-%s.%s", versionStr, goos, goarch)
|
||||
return fmt.Sprintf(path.Join(installBinDir, binBaseName))
|
||||
}
|
||||
|
||||
func GetRemoteId() (string, error) {
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/scripthaus-dev/mshell/pkg/base"
|
||||
"github.com/scripthaus-dev/mshell/pkg/packet"
|
||||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
||||
// TODO - track buffer sizes for sending input
|
||||
@ -72,11 +73,11 @@ func MakeClientProc(ctx context.Context, ecmd *exec.Cmd) (*ClientProc, string, e
|
||||
initPk := pk.(*packet.InitPacketType)
|
||||
if initPk.NotFound {
|
||||
cproc.Close()
|
||||
return nil, initPk.UName, fmt.Errorf("mshell command not found on local server")
|
||||
return nil, initPk.UName, fmt.Errorf("mshell-%s command not found on local server", semver.MajorMinor(base.MShellVersion))
|
||||
}
|
||||
if initPk.Version != base.MShellVersion {
|
||||
if semver.MajorMinor(initPk.Version) != semver.MajorMinor(base.MShellVersion) {
|
||||
cproc.Close()
|
||||
return nil, initPk.UName, fmt.Errorf("invalid remote mshell version '%s', must be %s", initPk.Version, base.MShellVersion)
|
||||
return nil, initPk.UName, fmt.Errorf("invalid remote mshell version '%s', must be '=%s'", initPk.Version, semver.MajorMinor(base.MShellVersion))
|
||||
}
|
||||
cproc.InitPk = initPk
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ import (
|
||||
"github.com/scripthaus-dev/mshell/pkg/cirfile"
|
||||
"github.com/scripthaus-dev/mshell/pkg/mpio"
|
||||
"github.com/scripthaus-dev/mshell/pkg/packet"
|
||||
"golang.org/x/mod/semver"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
@ -45,29 +46,37 @@ const MaxMaxPtySize = 100 * 1024 * 1024
|
||||
|
||||
const GetStateTimeout = 5 * time.Second
|
||||
|
||||
const ClientCommand = `
|
||||
const ClientCommandFmt = `
|
||||
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 --single
|
||||
mshell-[%VERSION%] --single
|
||||
fi
|
||||
`
|
||||
|
||||
const InstallCommand = `
|
||||
func MakeClientCommandStr() string {
|
||||
return strings.ReplaceAll(ClientCommandFmt, "[%VERSION%]", semver.MajorMinor(base.MShellVersion))
|
||||
}
|
||||
|
||||
const InstallCommandFmt = `
|
||||
printf "\n##N{\"type\": \"init\", \"notfound\": true, \"uname\": \"%s|%s\"}\n" "$(uname -s)" "$(uname -m)";
|
||||
mkdir -p ~/.mshell/;
|
||||
cat > ~/.mshell/mshell.temp;
|
||||
if [[ -s ~/.mshell/mshell.temp ]]
|
||||
then
|
||||
mv ~/.mshell/mshell.temp ~/.mshell/mshell;
|
||||
chmod a+x ~/.mshell/mshell;
|
||||
~/.mshell/mshell --single --version
|
||||
mv ~/.mshell/mshell.temp ~/.mshell/mshell-[%VERSION%];
|
||||
chmod a+x ~/.mshell/mshell-[%VERSION%];
|
||||
~/.mshell/mshell-[%VERSION%] --single --version
|
||||
fi
|
||||
`
|
||||
|
||||
func MakeInstallCommandStr() string {
|
||||
return strings.ReplaceAll(InstallCommandFmt, "[%VERSION%]", semver.MajorMinor(base.MShellVersion))
|
||||
}
|
||||
|
||||
const RunCommandFmt = `%s`
|
||||
const RunSudoCommandFmt = `sudo -n -C %d bash /dev/fd/%d`
|
||||
const RunSudoPasswordCommandFmt = `cat /dev/fd/%d | sudo -k -S -C %d bash -c "echo '[from-mshell]'; exec %d>&-; bash /dev/fd/%d < /dev/fd/%d"`
|
||||
@ -389,6 +398,7 @@ type InstallOpts struct {
|
||||
ArchStr string
|
||||
OptName string
|
||||
Detect bool
|
||||
CmdPty *os.File
|
||||
}
|
||||
|
||||
type ClientOpts struct {
|
||||
@ -408,7 +418,8 @@ func (opts SSHOpts) MakeSSHInstallCmd() (*exec.Cmd, error) {
|
||||
if opts.SSHHost == "" {
|
||||
return nil, fmt.Errorf("no ssh host provided, can only install to a remote host")
|
||||
}
|
||||
return opts.MakeSSHExecCmd(InstallCommand), nil
|
||||
cmdStr := MakeInstallCommandStr()
|
||||
return opts.MakeSSHExecCmd(cmdStr), nil
|
||||
}
|
||||
|
||||
func (opts SSHOpts) MakeMShellServerCmd() (*exec.Cmd, error) {
|
||||
@ -434,7 +445,8 @@ func (opts SSHOpts) MakeMShellSingleCmd(fromServer bool) (*exec.Cmd, error) {
|
||||
}
|
||||
return ecmd, nil
|
||||
}
|
||||
return opts.MakeSSHExecCmd(ClientCommand), nil
|
||||
cmdStr := MakeClientCommandStr()
|
||||
return opts.MakeSSHExecCmd(cmdStr), nil
|
||||
}
|
||||
|
||||
func (opts SSHOpts) MakeSSHExecCmd(remoteCommand string) *exec.Cmd {
|
||||
@ -632,12 +644,7 @@ func sendOptFile(input io.WriteCloser, optName string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func RunInstallSSHCommand(opts *InstallOpts) error {
|
||||
tryDetect := opts.Detect
|
||||
ecmd, err := opts.SSHOpts.MakeSSHInstallCmd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
func RunInstallFromCmd(ecmd *exec.Cmd, tryDetect bool, optName string, msgFn func(string)) error {
|
||||
inputWriter, err := ecmd.StdinPipe()
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating stdin pipe: %v", err)
|
||||
@ -653,8 +660,11 @@ func RunInstallSSHCommand(opts *InstallOpts) error {
|
||||
go func() {
|
||||
io.Copy(os.Stderr, stderrReader)
|
||||
}()
|
||||
if opts.OptName != "" {
|
||||
sendOptFile(inputWriter, opts.OptName)
|
||||
if optName != "" {
|
||||
err = sendOptFile(inputWriter, optName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot send mshell binary: %v", err)
|
||||
}
|
||||
}
|
||||
packetParser := packet.MakePacketParser(stdoutReader)
|
||||
err = ecmd.Start()
|
||||
@ -677,15 +687,19 @@ func RunInstallSSHCommand(opts *InstallOpts) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("arch cannot be detected (might be incompatible with mshell): %w", err)
|
||||
}
|
||||
fmt.Printf("mshell detected remote architecture as '%s.%s'\n", goos, goarch)
|
||||
optName := base.GoArchOptFile(goos, goarch)
|
||||
sendOptFile(inputWriter, optName)
|
||||
msgStr := fmt.Sprintf("mshell detected remote architecture as '%s.%s'\n", goos, goarch)
|
||||
msgFn(msgStr)
|
||||
optName := base.GoArchOptFile(base.MShellVersion, goos, goarch)
|
||||
fmt.Printf("optname %s\n", optName)
|
||||
err = sendOptFile(inputWriter, optName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot send mshell binary: %v", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if pk.GetType() == packet.InitPacketStr && !firstInit {
|
||||
initPacket := pk.(*packet.InitPacketType)
|
||||
if initPacket.Version == base.MShellVersion {
|
||||
fmt.Printf("mshell %s, installed successfully at %s:~/.mshell/mshell\n", initPacket.Version, opts.SSHOpts.SSHHost)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("invalid version '%s' received from client, expecting '%s'", initPacket.Version, base.MShellVersion)
|
||||
@ -700,6 +714,23 @@ func RunInstallSSHCommand(opts *InstallOpts) error {
|
||||
return fmt.Errorf("did not receive version string from client, install not successful")
|
||||
}
|
||||
|
||||
func RunInstallFromOpts(opts *InstallOpts) error {
|
||||
ecmd, err := opts.SSHOpts.MakeSSHInstallCmd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
msgFn := func(str string) {
|
||||
fmt.Printf("%s", str)
|
||||
}
|
||||
err = RunInstallFromCmd(ecmd, opts.Detect, opts.OptName, msgFn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mmVersion := semver.MajorMinor(base.MShellVersion)
|
||||
fmt.Printf("mshell installed successfully at %s:~/.mshell/mshell%s\n", opts.SSHOpts.SSHHost, mmVersion)
|
||||
return nil
|
||||
}
|
||||
|
||||
func HasDupStdin(fds []packet.RemoteFd) bool {
|
||||
for _, rfd := range fds {
|
||||
if rfd.Read && rfd.DupStdin {
|
||||
@ -764,22 +795,23 @@ func RunClientSSHCommandAndWait(runPacket *packet.RunPacketType, fdContext FdCon
|
||||
}
|
||||
if pk.GetType() == packet.InitPacketStr {
|
||||
initPk := pk.(*packet.InitPacketType)
|
||||
mmVersion := semver.MajorMinor(base.MShellVersion)
|
||||
if initPk.NotFound {
|
||||
if sshOpts.SSHHost == "" {
|
||||
return nil, fmt.Errorf("mshell command not found on local server")
|
||||
return nil, fmt.Errorf("mshell-%s command not found on local server", mmVersion)
|
||||
}
|
||||
if initPk.UName == "" {
|
||||
return nil, fmt.Errorf("mshell command not found on remote server, no uname detected")
|
||||
return nil, fmt.Errorf("mshell-%s command not found on remote server, no uname detected", mmVersion)
|
||||
}
|
||||
goos, goarch, err := DetectGoArch(initPk.UName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("mshell command not found on remote server, architecture cannot be detected (might be incompatible with mshell): %w", err)
|
||||
return nil, fmt.Errorf("mshell-%s command not found on remote server, architecture cannot be detected (might be incompatible with mshell): %w", mmVersion, err)
|
||||
}
|
||||
sshOptsStr := sshOpts.MakeMShellSSHOpts()
|
||||
return nil, fmt.Errorf("mshell command not found on remote server, can install with 'mshell --install %s %s.%s'", sshOptsStr, goos, goarch)
|
||||
return nil, fmt.Errorf("mshell-%s command not found on remote server, can install with 'mshell --install %s %s.%s'", mmVersion, sshOptsStr, goos, goarch)
|
||||
}
|
||||
if initPk.Version != base.MShellVersion {
|
||||
return nil, fmt.Errorf("invalid remote mshell version '%s', must be %s", initPk.Version, base.MShellVersion)
|
||||
if semver.MajorMinor(initPk.Version) != semver.MajorMinor(base.MShellVersion) {
|
||||
return nil, fmt.Errorf("invalid remote mshell version '%s', must be '=%s'", initPk.Version, semver.MajorMinor(base.MShellVersion))
|
||||
}
|
||||
versionOk = true
|
||||
if debug {
|
||||
|
@ -1,16 +1,16 @@
|
||||
|
||||
```bash
|
||||
# @scripthaus command build
|
||||
go build -ldflags="-s -w" -o /Users/mike/.mshell/mshell main-mshell.go
|
||||
go build -ldflags="-s -w" -o /Users/mike/.mshell/mshell-v0.1 main-mshell.go
|
||||
```
|
||||
|
||||
```bash
|
||||
# @scripthaus command fullbuild
|
||||
go build -ldflags="-s -w" -o /Users/mike/.mshell/mshell main-mshell.go
|
||||
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o /opt/mshell/bin/mshell.linux.amd64 main-mshell.go
|
||||
GOOS=linux GOARCH=arm64 go build -ldflags="-s -w" -o /opt/mshell/bin/mshell.linux.arm64 main-mshell.go
|
||||
GOOS=darwin GOARCH=amd64 go build -ldflags="-s -w" -o /opt/mshell/bin/mshell.darwin.amd64 main-mshell.go
|
||||
GOOS=darwin GOARCH=arm64 go build -ldflags="-s -w" -o /opt/mshell/bin/mshell.darwin.arm64 main-mshell.go
|
||||
go build -ldflags="-s -w" -o /Users/mike/.mshell/mshell-v0.1 main-mshell.go
|
||||
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o /opt/mshell/bin/mshell-v0.1-linux.amd64 main-mshell.go
|
||||
GOOS=linux GOARCH=arm64 go build -ldflags="-s -w" -o /opt/mshell/bin/mshell-v0.1-linux.arm64 main-mshell.go
|
||||
GOOS=darwin GOARCH=amd64 go build -ldflags="-s -w" -o /opt/mshell/bin/mshell-v0.1-darwin.amd64 main-mshell.go
|
||||
GOOS=darwin GOARCH=arm64 go build -ldflags="-s -w" -o /opt/mshell/bin/mshell-v0.1-darwin.arm64 main-mshell.go
|
||||
```
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user