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