Skip to content

Commit

Permalink
refactor: error on unsupported platforms
Browse files Browse the repository at this point in the history
  • Loading branch information
aymanbagabas committed Sep 22, 2023
1 parent 191ee86 commit ddb8a05
Show file tree
Hide file tree
Showing 7 changed files with 196 additions and 185 deletions.
4 changes: 2 additions & 2 deletions cmd_other.go → cmd_unix.go
Original file line number Diff line number Diff line change
@@ -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

Expand Down
3 changes: 3 additions & 0 deletions pty.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
130 changes: 3 additions & 127 deletions pty_other.go
Original file line number Diff line number Diff line change
@@ -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
}
132 changes: 132 additions & 0 deletions pty_unix.go
Original file line number Diff line number Diff line change
@@ -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
}
46 changes: 3 additions & 43 deletions ssh_other.go
Original file line number Diff line number Diff line change
@@ -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
}
53 changes: 53 additions & 0 deletions ssh_unix.go
Original file line number Diff line number Diff line change
@@ -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
}

0 comments on commit ddb8a05

Please sign in to comment.