github.com/maenmax/kairep@v0.0.0-20210218001208-55bf3df36788/src/golang.org/x/crypto/ssh/terminal/util.go (about) 1 // Copyright 2011 The Go 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 // +build darwin dragonfly freebsd linux,!appengine netbsd openbsd 6 7 // Package terminal provides support functions for dealing with terminals, as 8 // commonly found on UNIX systems. 9 // 10 // Putting a terminal into raw mode is the most common requirement: 11 // 12 // oldState, err := terminal.MakeRaw(0) 13 // if err != nil { 14 // panic(err) 15 // } 16 // defer terminal.Restore(0, oldState) 17 package terminal // import "golang.org/x/crypto/ssh/terminal" 18 19 import ( 20 "io" 21 "syscall" 22 "unsafe" 23 ) 24 25 // State contains the state of a terminal. 26 type State struct { 27 termios syscall.Termios 28 } 29 30 // IsTerminal returns true if the given file descriptor is a terminal. 31 func IsTerminal(fd int) bool { 32 var termios syscall.Termios 33 _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) 34 return err == 0 35 } 36 37 // MakeRaw put the terminal connected to the given file descriptor into raw 38 // mode and returns the previous state of the terminal so that it can be 39 // restored. 40 func MakeRaw(fd int) (*State, error) { 41 var oldState State 42 if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 { 43 return nil, err 44 } 45 46 newState := oldState.termios 47 // This attempts to replicate the behaviour documented for cfmakeraw in 48 // the termios(3) manpage. 49 newState.Iflag &^= syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON 50 newState.Oflag &^= syscall.OPOST 51 newState.Lflag &^= syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN 52 newState.Cflag &^= syscall.CSIZE | syscall.PARENB 53 newState.Cflag |= syscall.CS8 54 if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 { 55 return nil, err 56 } 57 58 return &oldState, nil 59 } 60 61 // GetState returns the current state of a terminal which may be useful to 62 // restore the terminal after a signal. 63 func GetState(fd int) (*State, error) { 64 var oldState State 65 if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 { 66 return nil, err 67 } 68 69 return &oldState, nil 70 } 71 72 // Restore restores the terminal connected to the given file descriptor to a 73 // previous state. 74 func Restore(fd int, state *State) error { 75 _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&state.termios)), 0, 0, 0) 76 return err 77 } 78 79 // GetSize returns the dimensions of the given terminal. 80 func GetSize(fd int) (width, height int, err error) { 81 var dimensions [4]uint16 82 83 if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(&dimensions)), 0, 0, 0); err != 0 { 84 return -1, -1, err 85 } 86 return int(dimensions[1]), int(dimensions[0]), nil 87 } 88 89 // ReadPassword reads a line of input from a terminal without local echo. This 90 // is commonly used for inputting passwords and other sensitive data. The slice 91 // returned does not include the \n. 92 func ReadPassword(fd int) ([]byte, error) { 93 var oldState syscall.Termios 94 if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&oldState)), 0, 0, 0); err != 0 { 95 return nil, err 96 } 97 98 newState := oldState 99 newState.Lflag &^= syscall.ECHO 100 newState.Lflag |= syscall.ICANON | syscall.ISIG 101 newState.Iflag |= syscall.ICRNL 102 if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 { 103 return nil, err 104 } 105 106 defer func() { 107 syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&oldState)), 0, 0, 0) 108 }() 109 110 var buf [16]byte 111 var ret []byte 112 for { 113 n, err := syscall.Read(fd, buf[:]) 114 if err != nil { 115 return nil, err 116 } 117 if n == 0 { 118 if len(ret) == 0 { 119 return nil, io.EOF 120 } 121 break 122 } 123 if buf[n-1] == '\n' { 124 n-- 125 } 126 ret = append(ret, buf[:n]...) 127 if n < len(buf) { 128 break 129 } 130 } 131 132 return ret, nil 133 }