pass uname back when mshell isn't found, parse, and give install command

This commit is contained in:
sawka 2022-06-27 18:42:56 -07:00
parent ec4bd5eaa1
commit 26479f59c0
5 changed files with 85 additions and 9 deletions

View File

@ -320,6 +320,10 @@ func parseClientOpts() (*shexec.ClientOpts, error) {
opts.Cwd = iter.Next()
continue
}
if argStr == "--detach" {
opts.Detach = true
continue
}
if argStr == "--debug" {
opts.Debug = true
continue

View File

@ -16,8 +16,8 @@ import (
"github.com/scripthaus-dev/mshell/pkg/packet"
)
const ReadBufSize = 32 * 1024
const WriteBufSize = 32 * 1024
const ReadBufSize = 128 * 1024
const WriteBufSize = 128 * 1024
const MaxSingleWriteSize = 4 * 1024
type Multiplexer struct {

View File

@ -341,6 +341,7 @@ type InitPacketType struct {
Env []string `json:"env,omitempty"`
User string `json:"user,omitempty"`
NotFound bool `json:"notfound,omitempty"`
UName string `json:"uname,omitempty"`
}
func (*InitPacketType) GetType() string {

View File

@ -76,10 +76,13 @@ func MakePacketParser(input io.Reader) *PacketParser {
parser.MainCh <- MakeRawPacket(line[:len(line)-1])
continue
}
packetLen, err := strconv.Atoi(line[2:bracePos])
if err != nil || packetLen != len(line)-bracePos-1 {
parser.MainCh <- MakeRawPacket(line[:len(line)-1])
continue
packetLen := -1
if line[2:bracePos] != "N" {
packetLen, err = strconv.Atoi(line[2:bracePos])
if err != nil || packetLen != len(line)-bracePos-1 {
parser.MainCh <- MakeRawPacket(line[:len(line)-1])
continue
}
}
pk, err := ParseJsonPacket([]byte(line[bracePos:]))
if err != nil {

View File

@ -32,10 +32,10 @@ const FirstExtraFilesFdNum = 3
const ClientCommand = `
PATH=$PATH:~/.mshell;
which mshell > /dev/null;
which mshell2 > /dev/null;
if [[ "$?" -ne 0 ]]
then
printf "\n##34{\"type\": \"init\", \"notfound\": true}\n"
printf "\n##N{\"type\": \"init\", \"notfound\": true, \"uname\": \"%s | %s\"}\n" "$(uname -s)" "$(uname -m)"
else
mshell --single
fi
@ -175,6 +175,19 @@ func ValidateRunPacket(pk *packet.RunPacketType) error {
if err != nil {
return err
}
for _, rfd := range pk.Fds {
if rfd.Write {
return fmt.Errorf("cannot detach command with writable remote files fd=%d", rfd.FdNum)
}
if rfd.Read {
if rfd.Content == "" {
return fmt.Errorf("cannot detach command with readable remote files fd=%d", rfd.FdNum)
}
if len(rfd.Content) > mpio.ReadBufSize {
return fmt.Errorf("cannot detach command, constant readable input too large fd=%d, len=%d, max=%d", rfd.FdNum, len(rfd.Content), mpio.ReadBufSize)
}
}
}
}
if pk.Cwd != "" {
realCwd := base.ExpandHomeDir(pk.Cwd)
@ -227,6 +240,7 @@ type ClientOpts struct {
SudoWithPass bool
SudoPw string
CommandStdinFdNum int
Detach bool
}
func (opts *ClientOpts) MakeExecCmd() *exec.Cmd {
@ -251,8 +265,30 @@ func (opts *ClientOpts) MakeExecCmd() *exec.Cmd {
}
}
func (opts *ClientOpts) MakeInstallCommandString(goos string, goarch string) string {
var moreSSHOpts []string
if opts.SSHIdentity != "" {
identityOpt := fmt.Sprintf("-i %s", shellescape.Quote(opts.SSHIdentity))
moreSSHOpts = append(moreSSHOpts, identityOpt)
}
if opts.SSHUser != "" {
userOpt := fmt.Sprintf("-l %s", shellescape.Quote(opts.SSHUser))
moreSSHOpts = append(moreSSHOpts, userOpt)
}
if opts.SSHOptsStr != "" {
optsOpt := fmt.Sprintf("--ssh-opts %s", shellescape.Quote(opts.SSHOptsStr))
moreSSHOpts = append(moreSSHOpts, optsOpt)
}
if opts.SSHHost != "" {
sshArg := fmt.Sprintf("--ssh %s", shellescape.Quote(opts.SSHHost))
moreSSHOpts = append(moreSSHOpts, sshArg)
}
return fmt.Sprintf("mshell --install %s %s_%s", strings.Join(moreSSHOpts, " "), goos, goarch)
}
func (opts *ClientOpts) MakeRunPacket() (*packet.RunPacketType, error) {
runPacket := packet.MakeRunPacket()
runPacket.Detached = opts.Detach
runPacket.Cwd = opts.Cwd
runPacket.Fds = opts.Fds
if !opts.Sudo {
@ -417,7 +453,16 @@ func RunClientSSHCommandAndWait(opts *ClientOpts) (*packet.CmdDonePacketType, er
if pk.GetType() == packet.InitPacketStr {
initPk := pk.(*packet.InitPacketType)
if initPk.NotFound {
return nil, fmt.Errorf("mshell command not found on remote server, can install with 'mshell --install'")
fmt.Printf("UNAME> %s\n", initPk.UName)
if initPk.UName == "" {
return nil, fmt.Errorf("mshell command not found on remote server, no uname detected")
}
goos, goarch, err := UNameStringToGoArch(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)
}
installCmd := opts.MakeInstallCommandString(goos, goarch)
return nil, fmt.Errorf("mshell command not found on remote server, can install with '%s'", installCmd)
}
if initPk.Version != "0.1.0" {
return nil, fmt.Errorf("invalid remote mshell version 'v%s', must be v0.1.0", initPk.Version)
@ -444,6 +489,29 @@ func RunClientSSHCommandAndWait(opts *ClientOpts) (*packet.CmdDonePacketType, er
return donePacket, nil
}
func UNameStringToGoArch(uname string) (string, string, error) {
fields := strings.SplitN(uname, "|", 2)
if len(fields) != 2 {
return "", "", fmt.Errorf("invalid uname string returned")
}
osVal := strings.TrimSpace(strings.ToLower(fields[0]))
archVal := strings.TrimSpace(strings.ToLower(fields[1]))
if osVal != "darwin" && osVal != "linux" {
return "", "", fmt.Errorf("invalid uname OS '%s', mshell only supports OS X (darwin) and linux", osVal)
}
goos := osVal
goarch := ""
if archVal == "x86_64" || archVal == "i686" || archVal == "amd64" {
goarch = "amd64"
} else if archVal == "aarch64" || archVal == "amd64" {
goarch = "arm64"
}
if goarch == "" {
return "", "", fmt.Errorf("invalid uname machine type '%s', mshell only supports aarch64 (amd64) and x86_64 (amd64)", archVal)
}
return goos, goarch, nil
}
func (cmd *ShExecType) RunRemoteIOAndWait(packetParser *packet.PacketParser, sender *packet.PacketSender) {
defer cmd.Close()
cmd.Multiplexer.RunIOAndWait(packetParser, sender, true, false, false)