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 }