checkpoint, getting tty output working in non-detached mode

This commit is contained in:
sawka 2022-07-06 11:21:15 -07:00
parent 9aa684882b
commit 95f11fb418
3 changed files with 66 additions and 28 deletions

View File

@ -304,6 +304,10 @@ func parseClientOpts() (*shexec.ClientOpts, error) {
opts.Detach = true
continue
}
if argStr == "--pty" {
opts.UsePty = true
continue
}
if argStr == "--debug" {
opts.Debug = true
continue

View File

@ -481,6 +481,7 @@ type RunPacketType struct {
Command string `json:"command"`
Cwd string `json:"cwd,omitempty"`
Env map[string]string `json:"env,omitempty"`
UsePty bool `json:"usepty,omitempty"`
TermSize *TermSize `json:"termsize,omitempty"`
Fds []RemoteFd `json:"fds,omitempty"`
RunData []RunDataType `json:"rundata,omitempty"`

View File

@ -196,9 +196,10 @@ func MakeDetachedExecCmd(pk *packet.RunPacketType, cmdTty *os.File) (*exec.Cmd,
if pk.Cwd != "" {
ecmd.Dir = base.ExpandHomeDir(pk.Cwd)
}
if !HasDupStdin(pk.Fds) {
ecmd.Stdin = cmdTty
if HasDupStdin(pk.Fds) {
return nil, fmt.Errorf("cannot detach command with dup stdin")
}
ecmd.Stdin = cmdTty
ecmd.Stdout = cmdTty
ecmd.Stderr = cmdTty
ecmd.SysProcAttr = &syscall.SysProcAttr{
@ -206,15 +207,8 @@ func MakeDetachedExecCmd(pk *packet.RunPacketType, cmdTty *os.File) (*exec.Cmd,
Setctty: true,
}
extraFiles := make([]*os.File, 0, MaxFdNum+1)
for _, rfd := range pk.Fds {
if rfd.FdNum >= len(extraFiles) {
extraFiles = extraFiles[:rfd.FdNum+1]
}
if rfd.Read && rfd.DupStdin {
extraFiles[rfd.FdNum] = cmdTty
continue
}
return nil, fmt.Errorf("invalid fd %d passed to detached command", rfd.FdNum)
if len(pk.Fds) > 0 {
return nil, fmt.Errorf("invalid fd %d passed to detached command", pk.Fds[0].FdNum)
}
for _, runData := range pk.RunData {
if runData.FdNum >= len(extraFiles) {
@ -276,7 +270,10 @@ func ValidateRunPacket(pk *packet.RunPacketType) error {
if rfd.Write {
return fmt.Errorf("cannot detach command with writable remote files fd=%d", rfd.FdNum)
}
if rfd.Read && !rfd.DupStdin {
if rfd.Read && rfd.DupStdin {
return fmt.Errorf("cannot detach command with dup stdin fd=%d", rfd.FdNum)
}
if rfd.Read {
return fmt.Errorf("cannot detach command with readable remote files fd=%d", rfd.FdNum)
}
}
@ -306,6 +303,9 @@ func ValidateRunPacket(pk *packet.RunPacketType) error {
return fmt.Errorf("rundata length mismatch, fd=%d, datalen=%d, expected=%d", runData.FdNum, len(runData.Data), runData.DataLen)
}
}
if pk.UsePty && HasDupStdin(pk.Fds) {
return fmt.Errorf("cannot use pty with command that has dup stdin")
}
return nil
}
@ -347,6 +347,7 @@ type ClientOpts struct {
SudoWithPass bool
SudoPw string
Detach bool
UsePty bool
}
func (opts SSHOpts) MakeSSHInstallCmd() (*exec.Cmd, error) {
@ -416,6 +417,7 @@ func (opts *ClientOpts) MakeRunPacket() (*packet.RunPacketType, error) {
runPacket.Detached = opts.Detach
runPacket.Cwd = opts.Cwd
runPacket.Fds = opts.Fds
runPacket.UsePty = opts.UsePty
if !opts.Sudo {
// normal, non-sudo command
runPacket.Command = fmt.Sprintf(RunCommandFmt, opts.Command)
@ -777,20 +779,51 @@ func RunCommandSimple(pk *packet.RunPacketType, sender *packet.PacketSender) (*S
cmd.Close()
return nil, err
}
cmd.Cmd.Stdin, err = cmd.Multiplexer.MakeWriterPipe(0)
if err != nil {
cmd.Close()
return nil, err
var cmdPty *os.File
var cmdTty *os.File
if pk.UsePty {
cmdPty, cmdTty, err = pty.Open()
if err != nil {
return nil, fmt.Errorf("opening new pty: %w", err)
}
pty.Setsize(cmdPty, GetWinsize(pk))
defer func() {
cmdTty.Close()
}()
cmd.CmdPty = cmdPty
}
cmd.Cmd.Stdout, err = cmd.Multiplexer.MakeReaderPipe(1)
if err != nil {
cmd.Close()
return nil, err
}
cmd.Cmd.Stderr, err = cmd.Multiplexer.MakeReaderPipe(2)
if err != nil {
cmd.Close()
return nil, err
if cmdTty != nil {
cmd.Cmd.Stdin = cmdTty
cmd.Cmd.Stdout = cmdTty
cmd.Cmd.Stderr = cmdTty
cmd.Cmd.SysProcAttr = &syscall.SysProcAttr{
Setsid: true,
Setctty: true,
}
cmd.Multiplexer.MakeRawFdWriter(0, cmdPty, false)
cmd.Multiplexer.MakeRawFdReader(1, cmdPty, false)
nullFd, err := os.Open("/dev/null")
if err != nil {
cmd.Close()
return nil, fmt.Errorf("cannot open /dev/null: %w", err)
}
cmd.Multiplexer.MakeRawFdReader(2, nullFd, true)
} else {
cmd.Cmd.Stdin, err = cmd.Multiplexer.MakeWriterPipe(0)
if err != nil {
cmd.Close()
return nil, err
}
cmd.Cmd.Stdout, err = cmd.Multiplexer.MakeReaderPipe(1)
if err != nil {
cmd.Close()
return nil, err
}
cmd.Cmd.Stderr, err = cmd.Multiplexer.MakeReaderPipe(2)
if err != nil {
cmd.Close()
return nil, err
}
}
extraFiles := make([]*os.File, 0, MaxFdNum+1)
for _, runData := range pk.RunData {
@ -898,11 +931,11 @@ func RunCommandDetached(pk *packet.RunPacketType, sender *packet.PacketSender) (
if err != nil {
return nil, nil, err
}
ptyOutInfo, err := os.Stat(fileNames.PtyOutFile)
runOutInfo, err := os.Stat(fileNames.RunnerOutFile)
if err == nil { // non-nil error will be caught by regular OpenFile below
// must have size 0
if ptyOutInfo.Size() != 0 {
return nil, nil, fmt.Errorf("cmdkey '%s' was already used (ptyout len=%d)", pk.CK, ptyOutInfo.Size())
if runOutInfo.Size() != 0 {
return nil, nil, fmt.Errorf("cmdkey '%s' was already used (runout len=%d)", pk.CK, runOutInfo.Size())
}
}
cmdPty, cmdTty, err := pty.Open()