From b9c3940b9968b70543c7557bbaa089f8983ed183 Mon Sep 17 00:00:00 2001 From: sawka Date: Sun, 16 Oct 2022 23:46:59 -0700 Subject: [PATCH] big change to execution, run command as a script and set aliases/funcs --- pkg/base/base.go | 2 +- pkg/packet/packet.go | 52 ++++++++++++----------- pkg/shexec/shexec.go | 98 ++++++++++++++++++++++++++------------------ scripthaus.md | 12 +++--- 4 files changed, 94 insertions(+), 70 deletions(-) diff --git a/pkg/base/base.go b/pkg/base/base.go index 4a43a4ed..08a23a11 100644 --- a/pkg/base/base.go +++ b/pkg/base/base.go @@ -30,7 +30,7 @@ const MShellHomeVarName = "MSHELL_HOME" const MShellInstallBinVarName = "MSHELL_INSTALLBIN_PATH" const SSHCommandVarName = "SSH_COMMAND" const SessionsDirBaseName = "sessions" -const MShellVersion = "v0.1.0" +const MShellVersion = "v0.2.0" const RemoteIdFile = "remoteid" const DefaultMShellInstallBinDir = "/opt/mshell/bin" diff --git a/pkg/packet/packet.go b/pkg/packet/packet.go index db98f8d8..46f4c29c 100644 --- a/pkg/packet/packet.go +++ b/pkg/packet/packet.go @@ -108,6 +108,13 @@ func MakePacket(packetType string) (PacketType, error) { return rtn.Interface().(PacketType), nil } +type ShellState struct { + Cwd string `json:"cwd,omitempty"` + Env0 []byte `json:"env0,omitempty"` + Aliases string `json:"aliases,omitempty"` + Funcs string `json:"funcs,omitempty"` +} + type CmdDataPacketType struct { Type string `json:"type"` RespId string `json:"respid"` @@ -435,18 +442,16 @@ func FmtMessagePacket(fmtStr string, args ...interface{}) *MessagePacketType { } type InitPacketType struct { - Type string `json:"type"` - Version string `json:"version"` - MShellHomeDir string `json:"mshellhomedir,omitempty"` - HomeDir string `json:"homedir,omitempty"` - Cwd string `json:"cwd,omitempty"` - Env0 []byte `json:"env0,omitempty"` // "env -0" format - Aliases string `json:"aliases,omitempty"` - User string `json:"user,omitempty"` - HostName string `json:"hostname,omitempty"` - NotFound bool `json:"notfound,omitempty"` - UName string `json:"uname,omitempty"` - RemoteId string `json:"remoteid,omitempty"` + Type string `json:"type"` + Version string `json:"version"` + MShellHomeDir string `json:"mshellhomedir,omitempty"` + HomeDir string `json:"homedir,omitempty"` + State *ShellState `json:"state,omitempty"` + User string `json:"user,omitempty"` + HostName string `json:"hostname,omitempty"` + NotFound bool `json:"notfound,omitempty"` + UName string `json:"uname,omitempty"` + RemoteId string `json:"remoteid,omitempty"` } func (*InitPacketType) GetType() string { @@ -535,18 +540,17 @@ type RunDataType struct { } type RunPacketType struct { - Type string `json:"type"` - ReqId string `json:"reqid"` - CK base.CommandKey `json:"ck"` - Command string `json:"command"` - Cwd string `json:"cwd,omitempty"` - Env0 []byte `json:"env0,omitempty"` // in "env -0" format - EnvComplete bool `json:"envcomplete,omitempty"` // set to true if env0 is complete (the default env should not be set) - UsePty bool `json:"usepty,omitempty"` - TermOpts *TermOpts `json:"termopts,omitempty"` - Fds []RemoteFd `json:"fds,omitempty"` - RunData []RunDataType `json:"rundata,omitempty"` - Detached bool `json:"detached,omitempty"` + Type string `json:"type"` + ReqId string `json:"reqid"` + CK base.CommandKey `json:"ck"` + Command string `json:"command"` + State *ShellState `json:"state"` + StateComplete bool `json:"statecomplete,omitempty"` // set to true if state is complete (the default env should not be set) + UsePty bool `json:"usepty,omitempty"` + TermOpts *TermOpts `json:"termopts,omitempty"` + Fds []RemoteFd `json:"fds,omitempty"` + RunData []RunDataType `json:"rundata,omitempty"` + Detached bool `json:"detached,omitempty"` } func (*RunPacketType) GetType() string { diff --git a/pkg/shexec/shexec.go b/pkg/shexec/shexec.go index 3e45494a..22f6a385 100644 --- a/pkg/shexec/shexec.go +++ b/pkg/shexec/shexec.go @@ -81,12 +81,6 @@ 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"` -type CurrentState struct { - Cwd string - Env0 []byte - Aliases string -} - type ShExecType struct { StartTs time.Time CK base.CommandKey @@ -259,14 +253,18 @@ func MakeSimpleStaticWriterPipe(data []byte) (*os.File, error) { } func MakeDetachedExecCmd(pk *packet.RunPacketType, cmdTty *os.File) (*exec.Cmd, error) { + state := pk.State + if state == nil { + state = &packet.ShellState{} + } ecmd := exec.Command("bash", "-c", pk.Command) - if !pk.EnvComplete { + if !pk.StateComplete { ecmd.Env = os.Environ() } - UpdateCmdEnv(ecmd, ParseEnv0(pk.Env0)) + UpdateCmdEnv(ecmd, ParseEnv0(state.Env0)) UpdateCmdEnv(ecmd, map[string]string{"TERM": getTermType(pk)}) - if pk.Cwd != "" { - ecmd.Dir = base.ExpandHomeDir(pk.Cwd) + if state.Cwd != "" { + ecmd.Dir = base.ExpandHomeDir(state.Cwd) } if HasDupStdin(pk.Fds) { return nil, fmt.Errorf("cannot detach command with dup stdin") @@ -360,8 +358,8 @@ func ValidateRunPacket(pk *packet.RunPacketType) error { return fmt.Errorf("cannot detach command, constant rundata input too large len=%d, max=%d", totalRunData, mpio.MaxTotalRunDataSize) } } - if pk.Cwd != "" { - realCwd := base.ExpandHomeDir(pk.Cwd) + if pk.State != nil && pk.State.Cwd != "" { + realCwd := base.ExpandHomeDir(pk.State.Cwd) dirInfo, err := os.Stat(realCwd) if err != nil { return fmt.Errorf("invalid cwd '%s' for command: %v", realCwd, err) @@ -533,7 +531,8 @@ func GetTerminalSize() (int, int, error) { func (opts *ClientOpts) MakeRunPacket() (*packet.RunPacketType, error) { runPacket := packet.MakeRunPacket() runPacket.Detached = opts.Detach - runPacket.Cwd = opts.Cwd + runPacket.State = &packet.ShellState{} + runPacket.State.Cwd = opts.Cwd runPacket.Fds = opts.Fds if opts.UsePty { runPacket.UsePty = true @@ -940,7 +939,26 @@ func getTermType(pk *packet.RunPacketType) string { return termType } +func makeEnvCommandStr(pk *packet.RunPacketType) string { + fmtStr := ` +shopt -q -s expand_aliases +set +m +%s +%s +%s +` + state := pk.State + if state == nil { + state = &packet.ShellState{} + } + return fmt.Sprintf(fmtStr, state.Aliases, state.Funcs, pk.Command) +} + func RunCommandSimple(pk *packet.RunPacketType, sender *packet.PacketSender, fromServer bool) (*ShExecType, error) { + state := pk.State + if state == nil { + state = &packet.ShellState{} + } cmd := MakeShExec(pk.CK, nil) if fromServer { msgUpr := packet.MessageUPR{CK: pk.CK, Sender: sender} @@ -948,19 +966,24 @@ func RunCommandSimple(pk *packet.RunPacketType, sender *packet.PacketSender, fro cmd.Multiplexer.UPR = upr cmd.MsgSender = sender } - if pk.UsePty { - cmd.Cmd = exec.Command("bash", "-i", "-c", pk.Command) - } else { - cmd.Cmd = exec.Command("bash", "-c", pk.Command) + commandStr := makeEnvCommandStr(pk) + commandFdNum, err := AddRunData(pk, commandStr, "command") + if err != nil { + return nil, err } - if !pk.EnvComplete { + if pk.UsePty { + cmd.Cmd = exec.Command("bash", "-i", fmt.Sprintf("/dev/fd/%d", commandFdNum)) + } else { + cmd.Cmd = exec.Command("bash", fmt.Sprintf("/dev/fd/%d", commandFdNum)) + } + if !pk.StateComplete { cmd.Cmd.Env = os.Environ() } - UpdateCmdEnv(cmd.Cmd, ParseEnv0(pk.Env0)) - if pk.Cwd != "" { - cmd.Cmd.Dir = base.ExpandHomeDir(pk.Cwd) + UpdateCmdEnv(cmd.Cmd, ParseEnv0(state.Env0)) + if state.Cwd != "" { + cmd.Cmd.Dir = base.ExpandHomeDir(state.Cwd) } - err := ValidateRemoteFds(pk.Fds) + err = ValidateRemoteFds(pk.Fds) if err != nil { cmd.Close() return nil, err @@ -1227,13 +1250,11 @@ func MakeInitPacket() *packet.InitPacketType { func MakeServerInitPacket() (*packet.InitPacketType, error) { var err error initPacket := MakeInitPacket() - cstate, err := GetCurrentState() + shellState, err := GetShellState() if err != nil { return nil, err } - initPacket.Cwd = cstate.Cwd - initPacket.Env0 = cstate.Env0 - initPacket.Aliases = cstate.Aliases + initPacket.State = shellState initPacket.RemoteId, err = base.GetRemoteId() if err != nil { return nil, err @@ -1322,28 +1343,27 @@ func runSimpleCmdInPty(ecmd *exec.Cmd) ([]byte, error) { return outputBuf.Bytes(), nil } -func GetCurrentState() (*CurrentState, error) { +func GetShellState() (*packet.ShellState, error) { execFile, err := os.Executable() if err != nil { return nil, fmt.Errorf("cannot find local mshell executable: %w", err) } ctx, _ := context.WithTimeout(context.Background(), GetStateTimeout) - ecmd := exec.CommandContext(ctx, "bash", "-l", "-i", "-c", fmt.Sprintf("%s --env; alias -p", shellescape.Quote(execFile))) + ecmd := exec.CommandContext(ctx, "bash", "-l", "-i", "-c", fmt.Sprintf("%s --env; alias -p; printf \"\\x00\\x00\"; declare -f", shellescape.Quote(execFile))) outputBytes, err := runSimpleCmdInPty(ecmd) if err != nil { return nil, err } - firstSep := bytes.Index(outputBytes, []byte{0, 0}) - if firstSep == -1 { - return nil, fmt.Errorf("invalid current state output no NUL separator") + fields := bytes.Split(outputBytes, []byte{0, 0}) + if len(fields) != 4 { + return nil, fmt.Errorf("invalid shell state output, wrong number of fields, fields=%d", len(fields)) } - cwd := string(outputBytes[0:firstSep]) - secondSep := bytes.Index(outputBytes[firstSep+2:], []byte{0, 0}) - if secondSep == -1 { - return nil, fmt.Errorf("invalid current state output, no second NUL separator") + rtn := &packet.ShellState{} + rtn.Cwd = string(fields[0]) + if len(fields[1]) > 0 { + rtn.Env0 = append(fields[1], '\x00') } - secondSep += firstSep + 2 - env0 := outputBytes[firstSep+2 : secondSep+1] // grab one of the NUL bytes (end of env0) - aliases := string(outputBytes[secondSep+2:]) - return &CurrentState{Cwd: cwd, Env0: env0, Aliases: aliases}, nil + rtn.Aliases = strings.ReplaceAll(string(fields[2]), "\r\n", "\n") + rtn.Funcs = strings.ReplaceAll(string(fields[3]), "\r\n", "\n") + return rtn, nil } diff --git a/scripthaus.md b/scripthaus.md index 3d7df182..bdcff57c 100644 --- a/scripthaus.md +++ b/scripthaus.md @@ -1,16 +1,16 @@ ```bash # @scripthaus command build -go build -ldflags="-s -w" -o /Users/mike/.mshell/mshell-v0.1 main-mshell.go +go build -ldflags="-s -w" -o /Users/mike/.mshell/mshell-v0.2 main-mshell.go ``` ```bash # @scripthaus command fullbuild -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 +go build -ldflags="-s -w" -o /Users/mike/.mshell/mshell-v0.2 main-mshell.go +GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o /opt/mshell/bin/mshell-v0.2-linux.amd64 main-mshell.go +GOOS=linux GOARCH=arm64 go build -ldflags="-s -w" -o /opt/mshell/bin/mshell-v0.2-linux.arm64 main-mshell.go +GOOS=darwin GOARCH=amd64 go build -ldflags="-s -w" -o /opt/mshell/bin/mshell-v0.2-darwin.amd64 main-mshell.go +GOOS=darwin GOARCH=arm64 go build -ldflags="-s -w" -o /opt/mshell/bin/mshell-v0.2-darwin.arm64 main-mshell.go ```