use cirfile for detached commands

This commit is contained in:
sawka
2022-08-19 15:28:32 -07:00
parent 26bd499fac
commit 86a7cd59e6
4 changed files with 45 additions and 32 deletions
+3
View File
@@ -377,6 +377,9 @@ func handleClient() (int, error) {
if err != nil { if err != nil {
return 1, err 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) donePacket, err := shexec.RunClientSSHCommandAndWait(runPacket, shexec.StdContext{}, opts.SSHOpts, nil, opts.Debug)
if err != nil { if err != nil {
return 1, err return 1, err
-25
View File
@@ -118,22 +118,6 @@ func GetMShellHomeDir() string {
return ExpandHomeDir(DefaultMShellHome) 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) { func GetCommandFileNames(ck CommandKey) (*CommandFileNames, error) {
if err := ck.Validate("ck"); err != nil { if err := ck.Validate("ck"); err != nil {
return nil, fmt.Errorf("cannot get command files: %w", err) return nil, fmt.Errorf("cannot get command files: %w", err)
@@ -151,15 +135,6 @@ func GetCommandFileNames(ck CommandKey) (*CommandFileNames, error) {
}, nil }, 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 { func CleanUpCmdFiles(sessionId string, cmdId string) error {
if cmdId == "" { if cmdId == "" {
return fmt.Errorf("bad cmdid, cannot clean up") return fmt.Errorf("bad cmdid, cannot clean up")
+11 -4
View File
@@ -378,6 +378,13 @@ func (p *ResponsePacketType) Err() error {
return nil 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 { func MakeErrorResponsePacket(reqId string, err error) *ResponsePacketType {
return &ResponsePacketType{Type: ResponsePacketStr, RespId: reqId, Error: err.Error()} return &ResponsePacketType{Type: ResponsePacketStr, RespId: reqId, Error: err.Error()}
} }
@@ -504,10 +511,10 @@ func MakeCmdStartPacket(reqId string) *CmdStartPacketType {
} }
type TermOpts struct { type TermOpts struct {
Rows int `json:"rows"` Rows int `json:"rows"`
Cols int `json:"cols"` Cols int `json:"cols"`
Term string `json:"term"` Term string `json:"term"`
CmdSize int64 `json:"cmdsize,omitempty"` MaxPtySize int64 `json:"maxptysize,omitempty"`
} }
type RemoteFd struct { type RemoteFd struct {
+31 -3
View File
@@ -23,6 +23,7 @@ import (
"github.com/alessio/shellescape" "github.com/alessio/shellescape"
"github.com/creack/pty" "github.com/creack/pty"
"github.com/scripthaus-dev/mshell/pkg/base" "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/mpio"
"github.com/scripthaus-dev/mshell/pkg/packet" "github.com/scripthaus-dev/mshell/pkg/packet"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
@@ -35,6 +36,7 @@ const MaxCols = 1024
const MaxFdNum = 1023 const MaxFdNum = 1023
const FirstExtraFilesFdNum = 3 const FirstExtraFilesFdNum = 3
const DefaultTermType = "xterm-256color" const DefaultTermType = "xterm-256color"
const DefaultMaxPtySize = 1024 * 1024
const ClientCommand = ` const ClientCommand = `
PATH=$PATH:~/.mshell; PATH=$PATH:~/.mshell;
@@ -67,6 +69,7 @@ type ShExecType struct {
FileNames *base.CommandFileNames FileNames *base.CommandFileNames
Cmd *exec.Cmd Cmd *exec.Cmd
CmdPty *os.File CmdPty *os.File
MaxPtySize int64
Multiplexer *mpio.Multiplexer Multiplexer *mpio.Multiplexer
Detached bool Detached bool
DetachedOutput *packet.PacketSender 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) { func (cmd *ShExecType) DetachedWait(startPacket *packet.CmdStartPacketType) {
// after Start(), any output/errors must go to DetachedOutput // 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 // 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 { if err != nil {
cmd.DetachedOutput.SendCmdError(cmd.CK, fmt.Errorf("cannot dup2 stdin to runout: %w", err)) 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 { if err != nil {
cmd.DetachedOutput.SendCmdError(cmd.CK, fmt.Errorf("cannot open ptyout file '%s': %w", cmd.FileNames.PtyOutFile, err)) cmd.DetachedOutput.SendCmdError(cmd.CK, fmt.Errorf("cannot open ptyout file '%s': %w", cmd.FileNames.PtyOutFile, err))
// don't return (command is already running) // don't return (command is already running)
@@ -958,8 +981,8 @@ func (cmd *ShExecType) DetachedWait(startPacket *packet.CmdStartPacketType) {
go func() { go func() {
// copy pty output to .ptyout file // copy pty output to .ptyout file
defer close(ptyCopyDone) defer close(ptyCopyDone)
defer ptyOutFd.Close() defer ptyOutFile.Close()
_, copyErr := io.Copy(ptyOutFd, cmd.CmdPty) copyErr := copyToCirFile(ptyOutFile, cmd.CmdPty)
if copyErr != nil { if copyErr != nil {
cmd.DetachedOutput.SendCmdError(cmd.CK, fmt.Errorf("copying pty output to ptyout file: %w", copyErr)) 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.FileNames = fileNames
cmd.CmdPty = cmdPty cmd.CmdPty = cmdPty
cmd.Detached = true 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) cmd.RunnerOutFd, err = os.OpenFile(fileNames.RunnerOutFile, os.O_TRUNC|os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("cannot open runout file '%s': %w", fileNames.RunnerOutFile, err) return nil, nil, fmt.Errorf("cannot open runout file '%s': %w", fileNames.RunnerOutFile, err)