pty_other.go

  1//go:build !windows
  2// +build !windows
  3
  4package pty
  5
  6import (
  7	"context"
  8	"errors"
  9	"os"
 10	"os/exec"
 11
 12	"github.com/creack/pty"
 13	"golang.org/x/sys/unix"
 14)
 15
 16type unixPty struct {
 17	master, slave *os.File
 18	closed        bool
 19}
 20
 21var _ Pty = &unixPty{}
 22
 23// Close implements Pty.
 24func (p *unixPty) Close() error {
 25	if p.closed {
 26		return nil
 27	}
 28	defer func() {
 29		p.closed = true
 30	}()
 31	return errors.Join(p.master.Close(), p.slave.Close())
 32}
 33
 34// Command implements Pty.
 35func (p *unixPty) Command(name string, args ...string) *Cmd {
 36	return p.CommandContext(nil, name, args...) // nolint:staticcheck
 37}
 38
 39// CommandContext implements Pty.
 40func (p *unixPty) CommandContext(ctx context.Context, name string, args ...string) *Cmd {
 41	cmd := exec.Command(name, args...)
 42	if ctx != nil {
 43		cmd = exec.CommandContext(ctx, name, args...)
 44	}
 45	c := &Cmd{
 46		ctx:  ctx,
 47		pty:  p,
 48		sys:  cmd,
 49		Path: name,
 50		Args: append([]string{name}, args...),
 51	}
 52	return c
 53}
 54
 55// Name implements Pty.
 56func (p *unixPty) Name() string {
 57	return p.slave.Name()
 58}
 59
 60// Read implements Pty.
 61func (p *unixPty) Read(b []byte) (n int, err error) {
 62	return p.master.Read(b)
 63}
 64
 65func (p *unixPty) Control(f func(fd uintptr)) error {
 66	conn, err := p.master.SyscallConn()
 67	if err != nil {
 68		return err
 69	}
 70	return conn.Control(f)
 71}
 72
 73// Resize implements Pty.
 74func (p *unixPty) Resize(rows int, cols int) error {
 75	var ctrlErr error
 76	if err := p.Control(func(fd uintptr) {
 77		ctrlErr = unix.IoctlSetWinsize(int(fd), unix.TIOCSWINSZ, &unix.Winsize{
 78			Row: uint16(rows),
 79			Col: uint16(cols),
 80		})
 81	}); err != nil {
 82		return err
 83	}
 84
 85	return ctrlErr
 86}
 87
 88// Write implements Pty.
 89func (p *unixPty) Write(b []byte) (n int, err error) {
 90	return p.master.Write(b)
 91}
 92
 93func newPty() (Pty, error) {
 94	master, slave, err := pty.Open()
 95	if err != nil {
 96		return nil, err
 97	}
 98
 99	return &unixPty{
100		master: master,
101		slave:  slave,
102	}, nil
103}
104
105func (c *Cmd) start() error {
106	cmd, ok := c.sys.(*exec.Cmd)
107	if !ok {
108		return ErrInvalidCommand
109	}
110	pty, ok := c.pty.(*unixPty)
111	if !ok {
112		return ErrInvalidCommand
113	}
114
115	cmd.Stdin = pty.slave
116	cmd.Stdout = pty.slave
117	cmd.Stderr = pty.slave
118	cmd.SysProcAttr = &unix.SysProcAttr{
119		Setsid:  true,
120		Setctty: true,
121	}
122	if err := cmd.Start(); err != nil {
123		return err
124	}
125
126	c.Process = cmd.Process
127	return nil
128}
129
130func (c *Cmd) wait() error {
131	cmd, ok := c.sys.(*exec.Cmd)
132	if !ok {
133		return ErrInvalidCommand
134	}
135	err := cmd.Wait()
136	c.ProcessState = cmd.ProcessState
137	return err
138}