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

View File

@ -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

View File

@ -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")

View File

@ -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 {

View File

@ -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)