diff --git a/cmd_other.go b/cmd_unix.go similarity index 81% rename from cmd_other.go rename to cmd_unix.go index bf64397..0032ba6 100644 --- a/cmd_other.go +++ b/cmd_unix.go @@ -1,5 +1,5 @@ -//go:build !windows -// +build !windows +//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +// +build darwin dragonfly freebsd linux netbsd openbsd solaris package pty diff --git a/pty.go b/pty.go index 583897c..df9aa41 100644 --- a/pty.go +++ b/pty.go @@ -9,6 +9,9 @@ import ( var ( // ErrInvalidCommand is returned when the command is invalid. ErrInvalidCommand = errors.New("pty: invalid command") + + // ErrUnsupported is returned when the platform is unsupported. + ErrUnsupported = errors.New("pty: unsupported platform") ) // New returns a new pseudo-terminal. diff --git a/pty_other.go b/pty_other.go index e20bfe6..ed3e64b 100644 --- a/pty_other.go +++ b/pty_other.go @@ -1,132 +1,8 @@ -//go:build !windows -// +build !windows +//go:build !linux && !darwin && !freebsd && !dragonfly && !netbsd && !openbsd && !solaris && !windows +// +build !linux,!darwin,!freebsd,!dragonfly,!netbsd,!openbsd,!solaris,!windows package pty -import ( - "context" - "errors" - "os" - "os/exec" - - "github.com/creack/pty" - "golang.org/x/sys/unix" -) - -// UnixPty is a POSIX compliant Unix pseudo-terminal. -// See: https://pubs.opengroup.org/onlinepubs/9699919799/ -type UnixPty struct { - master, slave *os.File - closed bool -} - -var _ Pty = &UnixPty{} - -// Close implements Pty. -func (p *UnixPty) Close() error { - if p.closed { - return nil - } - defer func() { - p.closed = true - }() - return errors.Join(p.master.Close(), p.slave.Close()) -} - -// Command implements Pty. -func (p *UnixPty) Command(name string, args ...string) *Cmd { - cmd := exec.Command(name, args...) - c := &Cmd{ - pty: p, - sys: cmd, - Path: name, - Args: append([]string{name}, args...), - } - c.sys = cmd - return c -} - -// CommandContext implements Pty. -func (p *UnixPty) CommandContext(ctx context.Context, name string, args ...string) *Cmd { - cmd := exec.CommandContext(ctx, name, args...) - c := p.Command(name, args...) - c.sys = cmd - c.ctx = ctx - c.Cancel = func() error { - return cmd.Cancel() - } - return c -} - -// Name implements Pty. -func (p *UnixPty) Name() string { - return p.slave.Name() -} - -// Read implements Pty. -func (p *UnixPty) Read(b []byte) (n int, err error) { - return p.master.Read(b) -} - -func (p *UnixPty) Control(f func(fd uintptr)) error { - conn, err := p.master.SyscallConn() - if err != nil { - return err - } - return conn.Control(f) -} - -// Master returns the pseudo-terminal master end (pty). -func (p *UnixPty) Master() *os.File { - return p.master -} - -// Slave returns the pseudo-terminal slave end (tty). -func (p *UnixPty) Slave() *os.File { - return p.slave -} - -// Winsize represents the terminal window size. -type Winsize = unix.Winsize - -// SetWinsize sets the pseudo-terminal window size. -func (p *UnixPty) SetWinsize(ws *Winsize) error { - var ctrlErr error - if err := p.Control(func(fd uintptr) { - ctrlErr = unix.IoctlSetWinsize(int(fd), unix.TIOCSWINSZ, ws) - }); err != nil { - return err - } - - return ctrlErr -} - -// Resize implements Pty. -func (p *UnixPty) Resize(width int, height int) error { - return p.SetWinsize(&Winsize{ - Row: uint16(height), - Col: uint16(width), - }) -} - -// Write implements Pty. -func (p *UnixPty) Write(b []byte) (n int, err error) { - return p.master.Write(b) -} - -// Fd implements Pty. -func (p *UnixPty) Fd() uintptr { - return p.master.Fd() -} - func newPty() (Pty, error) { - master, slave, err := pty.Open() - if err != nil { - return nil, err - } - - return &UnixPty{ - master: master, - slave: slave, - }, nil + return nil, ErrUnsupported } diff --git a/pty_unix.go b/pty_unix.go new file mode 100644 index 0000000..68c55e7 --- /dev/null +++ b/pty_unix.go @@ -0,0 +1,132 @@ +//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +// +build darwin dragonfly freebsd linux netbsd openbsd solaris + +package pty + +import ( + "context" + "errors" + "os" + "os/exec" + + "github.com/creack/pty" + "golang.org/x/sys/unix" +) + +// UnixPty is a POSIX compliant Unix pseudo-terminal. +// See: https://pubs.opengroup.org/onlinepubs/9699919799/ +type UnixPty struct { + master, slave *os.File + closed bool +} + +var _ Pty = &UnixPty{} + +// Close implements Pty. +func (p *UnixPty) Close() error { + if p.closed { + return nil + } + defer func() { + p.closed = true + }() + return errors.Join(p.master.Close(), p.slave.Close()) +} + +// Command implements Pty. +func (p *UnixPty) Command(name string, args ...string) *Cmd { + cmd := exec.Command(name, args...) + c := &Cmd{ + pty: p, + sys: cmd, + Path: name, + Args: append([]string{name}, args...), + } + c.sys = cmd + return c +} + +// CommandContext implements Pty. +func (p *UnixPty) CommandContext(ctx context.Context, name string, args ...string) *Cmd { + cmd := exec.CommandContext(ctx, name, args...) + c := p.Command(name, args...) + c.sys = cmd + c.ctx = ctx + c.Cancel = func() error { + return cmd.Cancel() + } + return c +} + +// Name implements Pty. +func (p *UnixPty) Name() string { + return p.slave.Name() +} + +// Read implements Pty. +func (p *UnixPty) Read(b []byte) (n int, err error) { + return p.master.Read(b) +} + +func (p *UnixPty) Control(f func(fd uintptr)) error { + conn, err := p.master.SyscallConn() + if err != nil { + return err + } + return conn.Control(f) +} + +// Master returns the pseudo-terminal master end (pty). +func (p *UnixPty) Master() *os.File { + return p.master +} + +// Slave returns the pseudo-terminal slave end (tty). +func (p *UnixPty) Slave() *os.File { + return p.slave +} + +// Winsize represents the terminal window size. +type Winsize = unix.Winsize + +// SetWinsize sets the pseudo-terminal window size. +func (p *UnixPty) SetWinsize(ws *Winsize) error { + var ctrlErr error + if err := p.Control(func(fd uintptr) { + ctrlErr = unix.IoctlSetWinsize(int(fd), unix.TIOCSWINSZ, ws) + }); err != nil { + return err + } + + return ctrlErr +} + +// Resize implements Pty. +func (p *UnixPty) Resize(width int, height int) error { + return p.SetWinsize(&Winsize{ + Row: uint16(height), + Col: uint16(width), + }) +} + +// Write implements Pty. +func (p *UnixPty) Write(b []byte) (n int, err error) { + return p.master.Write(b) +} + +// Fd implements Pty. +func (p *UnixPty) Fd() uintptr { + return p.master.Fd() +} + +func newPty() (Pty, error) { + master, slave, err := pty.Open() + if err != nil { + return nil, err + } + + return &UnixPty{ + master: master, + slave: slave, + }, nil +} diff --git a/ssh_other.go b/ssh_other.go index e7b3c3e..3b4a32e 100644 --- a/ssh_other.go +++ b/ssh_other.go @@ -1,53 +1,13 @@ -//go:build !windows -// +build !windows +//go:build !linux && !darwin && !freebsd && !dragonfly && !netbsd && !openbsd && !solaris && !windows +// +build !linux,!darwin,!freebsd,!dragonfly,!netbsd,!openbsd,!solaris,!windows package pty import ( - "fmt" - - "github.com/u-root/u-root/pkg/termios" "golang.org/x/crypto/ssh" ) func applyTerminalModesToFd(fd int, width int, height int, modes ssh.TerminalModes) error { - // Get the current TTY configuration. - tios, err := termios.GTTY(int(fd)) - if err != nil { - return fmt.Errorf("GTTY: %w", err) - } - - // Apply the modes from the SSH request. - tios.Row = height - tios.Col = width - - for c, v := range modes { - if c == ssh.TTY_OP_ISPEED { - tios.Ispeed = int(v) - continue - } - if c == ssh.TTY_OP_OSPEED { - tios.Ospeed = int(v) - continue - } - k, ok := terminalModeFlagNames[c] - if !ok { - continue - } - if _, ok := tios.CC[k]; ok { - tios.CC[k] = uint8(v) - continue - } - if _, ok := tios.Opts[k]; ok { - tios.Opts[k] = v > 0 - continue - } - } - - // Save the new TTY configuration. - if _, err := tios.STTY(int(fd)); err != nil { - return fmt.Errorf("STTY: %w", err) - } - + // TODO return nil } diff --git a/ssh_unix.go b/ssh_unix.go new file mode 100644 index 0000000..0905b8e --- /dev/null +++ b/ssh_unix.go @@ -0,0 +1,53 @@ +//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +// +build darwin dragonfly freebsd linux netbsd openbsd solaris + +package pty + +import ( + "fmt" + + "github.com/u-root/u-root/pkg/termios" + "golang.org/x/crypto/ssh" +) + +func applyTerminalModesToFd(fd int, width int, height int, modes ssh.TerminalModes) error { + // Get the current TTY configuration. + tios, err := termios.GTTY(int(fd)) + if err != nil { + return fmt.Errorf("GTTY: %w", err) + } + + // Apply the modes from the SSH request. + tios.Row = height + tios.Col = width + + for c, v := range modes { + if c == ssh.TTY_OP_ISPEED { + tios.Ispeed = int(v) + continue + } + if c == ssh.TTY_OP_OSPEED { + tios.Ospeed = int(v) + continue + } + k, ok := terminalModeFlagNames[c] + if !ok { + continue + } + if _, ok := tios.CC[k]; ok { + tios.CC[k] = uint8(v) + continue + } + if _, ok := tios.Opts[k]; ok { + tios.Opts[k] = v > 0 + continue + } + } + + // Save the new TTY configuration. + if _, err := tios.STTY(int(fd)); err != nil { + return fmt.Errorf("STTY: %w", err) + } + + return nil +} diff --git a/ssh_windows.go b/ssh_windows.go deleted file mode 100644 index 79a8ccf..0000000 --- a/ssh_windows.go +++ /dev/null @@ -1,13 +0,0 @@ -//go:build windows -// +build windows - -package pty - -import ( - "golang.org/x/crypto/ssh" -) - -func applyTerminalModesToFd(fd int, width int, height int, modes ssh.TerminalModes) error { - // TODO - return nil -}