github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/pkg/termios/termios_bsd.go (about)

     1  // Copyright 2015-2017 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build darwin || freebsd || openbsd
     6  // +build darwin freebsd openbsd
     7  
     8  package termios
     9  
    10  import (
    11  	"fmt"
    12  	"os"
    13  	"os/exec"
    14  	"path/filepath"
    15  	"syscall"
    16  
    17  	"golang.org/x/sys/unix"
    18  )
    19  
    20  type TTYIO struct {
    21  	f *os.File
    22  }
    23  
    24  // Winsize embeds unix.Winsize.
    25  type Winsize struct {
    26  	unix.Winsize
    27  }
    28  
    29  // New creates a new TTYIO using /dev/tty
    30  func New() (*TTYIO, error) {
    31  	return NewWithDev("/dev/tty")
    32  }
    33  
    34  // NewWithDev creates a new TTYIO with the specified device
    35  func NewWithDev(device string) (*TTYIO, error) {
    36  	f, err := os.OpenFile(device, os.O_RDWR, 0)
    37  	if err != nil {
    38  		return nil, err
    39  	}
    40  	return &TTYIO{f: f}, nil
    41  }
    42  
    43  // NewTTYS returns a new TTYIO.
    44  func NewTTYS(port string) (*TTYIO, error) {
    45  	f, err := os.OpenFile(filepath.Join("/dev", port), unix.O_RDWR|unix.O_NOCTTY|unix.O_NONBLOCK, 0o620)
    46  	if err != nil {
    47  		return nil, err
    48  	}
    49  	return &TTYIO{f: f}, nil
    50  }
    51  
    52  // GetTermios returns a filled-in Termios, from an fd.
    53  func GetTermios(fd uintptr) (*Termios, error) {
    54  	t, err := unix.IoctlGetTermios(int(fd), unix.TIOCGETA)
    55  	if err != nil {
    56  		return nil, err
    57  	}
    58  	return &Termios{Termios: *t}, nil
    59  }
    60  
    61  // Get terms a Termios from a TTYIO.
    62  func (t *TTYIO) Get() (*Termios, error) {
    63  	return GetTermios(t.f.Fd())
    64  }
    65  
    66  // SetTermios sets tty parameters for an fd from a Termios.
    67  func SetTermios(fd uintptr, ti *Termios) error {
    68  	return unix.IoctlSetTermios(int(fd), unix.TIOCSETA, &ti.Termios)
    69  }
    70  
    71  // Set sets tty parameters for a TTYIO from a Termios.
    72  func (t *TTYIO) Set(ti *Termios) error {
    73  	return SetTermios(t.f.Fd(), ti)
    74  }
    75  
    76  // GetWinSize gets window size from an fd.
    77  func GetWinSize(fd uintptr) (*Winsize, error) {
    78  	w, err := unix.IoctlGetWinsize(int(fd), unix.TIOCGWINSZ)
    79  	return &Winsize{Winsize: *w}, err
    80  }
    81  
    82  // GetWinSize gets window size from a TTYIO.
    83  func (t *TTYIO) GetWinSize() (*Winsize, error) {
    84  	return GetWinSize(t.f.Fd())
    85  }
    86  
    87  // SetWinSize sets window size for an fd from a Winsize.
    88  func SetWinSize(fd uintptr, w *Winsize) error {
    89  	return unix.IoctlSetWinsize(int(fd), unix.TIOCSWINSZ, &w.Winsize)
    90  }
    91  
    92  // SetWinSize sets window size for a TTYIO from a Winsize.
    93  func (t *TTYIO) SetWinSize(w *Winsize) error {
    94  	return SetWinSize(t.f.Fd(), w)
    95  }
    96  
    97  // Ctty sets the control tty into a Cmd, from a TTYIO.
    98  func (t *TTYIO) Ctty(c *exec.Cmd) {
    99  	c.Stdin, c.Stdout, c.Stderr = t.f, t.f, t.f
   100  	if c.SysProcAttr == nil {
   101  		c.SysProcAttr = &syscall.SysProcAttr{}
   102  	}
   103  	c.SysProcAttr.Setctty = true
   104  	c.SysProcAttr.Setsid = true
   105  	c.SysProcAttr.Ctty = int(t.f.Fd())
   106  }
   107  
   108  // MakeRaw modifies Termio state so, if it used for an fd or tty, it will set it to raw mode.
   109  func MakeRaw(term *Termios) *Termios {
   110  	raw := *term
   111  	raw.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON
   112  	raw.Oflag &^= unix.OPOST
   113  	raw.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN
   114  	raw.Cflag &^= unix.CSIZE | unix.PARENB
   115  	raw.Cflag |= unix.CS8
   116  
   117  	raw.Cc[unix.VMIN] = 1
   118  	raw.Cc[unix.VTIME] = 0
   119  
   120  	return &raw
   121  }
   122  
   123  // MakeSerialBaud updates the Termios to set the baudrate
   124  func MakeSerialBaud(term *Termios, baud int) (*Termios, error) {
   125  	t := *term
   126  	rate, ok := baud2unixB[baud]
   127  	if !ok {
   128  		return nil, fmt.Errorf("%d: Unrecognized baud rate", baud)
   129  	}
   130  
   131  	//	t.Cflag &^= unix.CBAUD
   132  	t.Cflag |= toTermiosCflag(rate)
   133  	t.Ispeed = rate
   134  	t.Ospeed = rate
   135  
   136  	return &t, nil
   137  }
   138  
   139  // MakeSerialDefault updates the Termios to typical serial configuration:
   140  //   - Ignore all flow control (modem, hardware, software...)
   141  //   - Translate carriage return to newline on input
   142  //   - Enable canonical mode: Input is available line by line, with line editing
   143  //     enabled (ERASE, KILL are supported)
   144  //   - Local ECHO is added (and handled by line editing)
   145  //   - Map newline to carriage return newline on output
   146  func MakeSerialDefault(term *Termios) *Termios {
   147  	t := *term
   148  	/* Clear all except baud, stop bit and parity settings */
   149  	t.Cflag &= /*unix.CBAUD | */ unix.CSTOPB | unix.PARENB | unix.PARODD
   150  	/* Set: 8 bits; ignore Carrier Detect; enable receive */
   151  	t.Cflag |= unix.CS8 | unix.CLOCAL | unix.CREAD
   152  	t.Iflag = unix.ICRNL
   153  	t.Lflag = unix.ICANON | unix.ISIG | unix.ECHO | unix.ECHOE | unix.ECHOK | unix.ECHOKE | unix.ECHOCTL
   154  	/* non-raw output; add CR to each NL */
   155  	t.Oflag = unix.OPOST | unix.ONLCR
   156  	/* reads will block only if < 1 char is available */
   157  	t.Cc[unix.VMIN] = 1
   158  	/* no timeout (reads block forever) */
   159  	t.Cc[unix.VTIME] = 0
   160  	// t.Line = 0
   161  
   162  	return &t
   163  }