From 86a7cd59e628ca652dbba9d618c64b81bde3e49e Mon Sep 17 00:00:00 2001 From: sawka Date: Fri, 19 Aug 2022 15:28:32 -0700 Subject: [PATCH] use cirfile for detached commands --- main-mshell.go | 3 +++ pkg/base/base.go | 25 ------------------------- pkg/packet/packet.go | 15 +++++++++++---- pkg/shexec/shexec.go | 34 +++++++++++++++++++++++++++++++--- 4 files changed, 45 insertions(+), 32 deletions(-) diff --git a/main-mshell.go b/main-mshell.go index 6029c773..0cc8c493 100644 --- a/main-mshell.go +++ b/main-mshell.go @@ -377,6 +377,9 @@ func handleClient() (int, error) { if err != nil { return 1, err } + if runPacket.Detached { + return 1, fmt.Errorf("cannot run detached command from command line client") + } donePacket, err := shexec.RunClientSSHCommandAndWait(runPacket, shexec.StdContext{}, opts.SSHOpts, nil, opts.Debug) if err != nil { return 1, err diff --git a/pkg/base/base.go b/pkg/base/base.go index c14736d5..972d447e 100644 --- a/pkg/base/base.go +++ b/pkg/base/base.go @@ -118,22 +118,6 @@ func GetMShellHomeDir() string { return ExpandHomeDir(DefaultMShellHome) } -func GetPtyOutFile(ck CommandKey, seqNum int) (string, error) { - if err := ck.Validate("ck"); err != nil { - return "", fmt.Errorf("cannot get command files: %w", err) - } - if seqNum < 0 { - return "", fmt.Errorf("invalid seqnum, cannot be negative") - } - sessionId, cmdId := ck.Split() - sdir, err := EnsureSessionDir(sessionId) - if err != nil { - return "", err - } - base := path.Join(sdir, cmdId) - return fmt.Sprintf("%s.%d.ptyout", base, seqNum), nil -} - func GetCommandFileNames(ck CommandKey) (*CommandFileNames, error) { if err := ck.Validate("ck"); err != nil { return nil, fmt.Errorf("cannot get command files: %w", err) @@ -151,15 +135,6 @@ func GetCommandFileNames(ck CommandKey) (*CommandFileNames, error) { }, nil } -func MakeCommandFileNamesWithHome(mhome string, ck CommandKey) *CommandFileNames { - base := path.Join(mhome, SessionsDirBaseName, ck.GetSessionId(), ck.GetCmdId()) - return &CommandFileNames{ - PtyOutFile: base + ".ptyout", - StdinFifo: base + ".stdin", - RunnerOutFile: base + ".runout", - } -} - func CleanUpCmdFiles(sessionId string, cmdId string) error { if cmdId == "" { return fmt.Errorf("bad cmdid, cannot clean up") diff --git a/pkg/packet/packet.go b/pkg/packet/packet.go index 74c7e36a..35a8d86d 100644 --- a/pkg/packet/packet.go +++ b/pkg/packet/packet.go @@ -378,6 +378,13 @@ func (p *ResponsePacketType) Err() error { return nil } +func (p *ResponsePacketType) String() string { + if p.Success { + return "response[success]" + } + return fmt.Sprintf("response[error:%s]", p.Error) +} + func MakeErrorResponsePacket(reqId string, err error) *ResponsePacketType { return &ResponsePacketType{Type: ResponsePacketStr, RespId: reqId, Error: err.Error()} } @@ -504,10 +511,10 @@ func MakeCmdStartPacket(reqId string) *CmdStartPacketType { } type TermOpts struct { - Rows int `json:"rows"` - Cols int `json:"cols"` - Term string `json:"term"` - CmdSize int64 `json:"cmdsize,omitempty"` + Rows int `json:"rows"` + Cols int `json:"cols"` + Term string `json:"term"` + MaxPtySize int64 `json:"maxptysize,omitempty"` } type RemoteFd struct { diff --git a/pkg/shexec/shexec.go b/pkg/shexec/shexec.go index df740a81..5dae3e40 100644 --- a/pkg/shexec/shexec.go +++ b/pkg/shexec/shexec.go @@ -23,6 +23,7 @@ import ( "github.com/alessio/shellescape" "github.com/creack/pty" "github.com/scripthaus-dev/mshell/pkg/base" + "github.com/scripthaus-dev/mshell/pkg/cirfile" "github.com/scripthaus-dev/mshell/pkg/mpio" "github.com/scripthaus-dev/mshell/pkg/packet" "golang.org/x/sys/unix" @@ -35,6 +36,7 @@ const MaxCols = 1024 const MaxFdNum = 1023 const FirstExtraFilesFdNum = 3 const DefaultTermType = "xterm-256color" +const DefaultMaxPtySize = 1024 * 1024 const ClientCommand = ` PATH=$PATH:~/.mshell; @@ -67,6 +69,7 @@ type ShExecType struct { FileNames *base.CommandFileNames Cmd *exec.Cmd CmdPty *os.File + MaxPtySize int64 Multiplexer *mpio.Multiplexer Detached bool DetachedOutput *packet.PacketSender @@ -933,6 +936,26 @@ func SetupSignalsForDetach() { }() } +func copyToCirFile(dest *cirfile.File, src io.Reader) error { + buf := make([]byte, 64*1024) + for { + var appendErr error + nr, readErr := src.Read(buf) + if nr > 0 { + appendErr = dest.AppendData(context.Background(), buf[0:nr]) + } + if readErr != nil && readErr != io.EOF { + return readErr + } + if appendErr != nil { + return appendErr + } + if readErr == io.EOF { + return nil + } + } +} + func (cmd *ShExecType) DetachedWait(startPacket *packet.CmdStartPacketType) { // after Start(), any output/errors must go to DetachedOutput // close stdin, redirect stdout/stderr to /dev/null, but wait for cmdstart packet to get sent @@ -949,7 +972,7 @@ func (cmd *ShExecType) DetachedWait(startPacket *packet.CmdStartPacketType) { if err != nil { cmd.DetachedOutput.SendCmdError(cmd.CK, fmt.Errorf("cannot dup2 stdin to runout: %w", err)) } - ptyOutFd, err := os.OpenFile(cmd.FileNames.PtyOutFile, os.O_TRUNC|os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600) + ptyOutFile, err := cirfile.CreateCirFile(cmd.FileNames.PtyOutFile, cmd.MaxPtySize) if err != nil { cmd.DetachedOutput.SendCmdError(cmd.CK, fmt.Errorf("cannot open ptyout file '%s': %w", cmd.FileNames.PtyOutFile, err)) // don't return (command is already running) @@ -958,8 +981,8 @@ func (cmd *ShExecType) DetachedWait(startPacket *packet.CmdStartPacketType) { go func() { // copy pty output to .ptyout file defer close(ptyCopyDone) - defer ptyOutFd.Close() - _, copyErr := io.Copy(ptyOutFd, cmd.CmdPty) + defer ptyOutFile.Close() + copyErr := copyToCirFile(ptyOutFile, cmd.CmdPty) if copyErr != nil { cmd.DetachedOutput.SendCmdError(cmd.CK, fmt.Errorf("copying pty output to ptyout file: %w", copyErr)) } @@ -1002,6 +1025,11 @@ func RunCommandDetached(pk *packet.RunPacketType, sender *packet.PacketSender) ( cmd.FileNames = fileNames cmd.CmdPty = cmdPty cmd.Detached = true + if pk.TermOpts != nil && pk.TermOpts.MaxPtySize != 0 { + cmd.MaxPtySize = pk.TermOpts.MaxPtySize + } else { + cmd.MaxPtySize = DefaultMaxPtySize + } cmd.RunnerOutFd, err = os.OpenFile(fileNames.RunnerOutFile, os.O_TRUNC|os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600) if err != nil { return nil, nil, fmt.Errorf("cannot open runout file '%s': %w", fileNames.RunnerOutFile, err)