Files
coder/pty/pty_other.go
T

111 lines
1.7 KiB
Go

//go:build !windows
// +build !windows
package pty
import (
"os"
"os/exec"
"runtime"
"sync"
"github.com/creack/pty"
"golang.org/x/xerrors"
)
func newPty() (PTY, error) {
ptyFile, ttyFile, err := pty.Open()
if err != nil {
return nil, err
}
return &otherPty{
pty: ptyFile,
tty: ttyFile,
}, nil
}
type otherPty struct {
mutex sync.Mutex
closed bool
err error
pty, tty *os.File
}
type otherProcess struct {
pty *os.File
cmd *exec.Cmd
// cmdDone protects access to cmdErr: anything reading cmdErr should read from cmdDone first.
cmdDone chan any
cmdErr error
}
func (p *otherPty) Input() ReadWriter {
return ReadWriter{
Reader: p.tty,
Writer: p.pty,
}
}
func (p *otherPty) Output() ReadWriter {
return ReadWriter{
Reader: p.pty,
Writer: p.tty,
}
}
func (p *otherPty) Resize(height uint16, width uint16) error {
p.mutex.Lock()
defer p.mutex.Unlock()
if p.closed {
return p.err
}
return pty.Setsize(p.pty, &pty.Winsize{
Rows: height,
Cols: width,
})
}
func (p *otherPty) Close() error {
p.mutex.Lock()
defer p.mutex.Unlock()
if p.closed {
return p.err
}
p.closed = true
err := p.pty.Close()
err2 := p.tty.Close()
if err == nil {
err = err2
}
if err != nil {
p.err = err
} else {
p.err = xerrors.New("pty: closed")
}
return err
}
func (p *otherProcess) Wait() error {
<-p.cmdDone
return p.cmdErr
}
func (p *otherProcess) Kill() error {
return p.cmd.Process.Kill()
}
func (p *otherProcess) waitInternal() {
// The GC can garbage collect the TTY FD before the command
// has finished running. See:
// https://github.com/creack/pty/issues/127#issuecomment-932764012
p.cmdErr = p.cmd.Wait()
runtime.KeepAlive(p.pty)
close(p.cmdDone)
}