github.com/glycerine/xcryptossh@v7.0.4+incompatible/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 18 19 import ( 20 "syscall" 21 "unsafe" 22 ) 23 24 // State contains the state of a terminal. 25 type State struct { 26 termios syscall.Termios 27 } 28 29 // IsTerminal returns true if the given file descriptor is a terminal. 30 func IsTerminal(fd int) bool { 31 var termios syscall.Termios 32 _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) 33 return err == 0 34 } 35 36 // MakeRaw put the terminal connected to the given file descriptor into raw 37 // mode and returns the previous state of the terminal so that it can be 38 // restored. 39 func MakeRaw(fd int) (*State, error) { 40 var oldState State 41 if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 { 42 return nil, err 43 } 44 45 newState := oldState.termios 46 // This attempts to replicate the behaviour documented for cfmakeraw in 47 // the termios(3) manpage. 48 newState.Iflag &^= syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON 49 newState.Oflag &^= syscall.OPOST 50 newState.Lflag &^= syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN 51 newState.Cflag &^= syscall.CSIZE | syscall.PARENB 52 newState.Cflag |= syscall.CS8 53 if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 { 54 return nil, err 55 } 56 57 return &oldState, nil 58 } 59 60 // GetState returns the current state of a terminal which may be useful to 61 // restore the terminal after a signal. 62 func GetState(fd int) (*State, error) { 63 var oldState State 64 if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 { 65 return nil, err 66 } 67 68 return &oldState, nil 69 } 70 71 // Restore restores the terminal connected to the given file descriptor to a 72 // previous state. 73 func Restore(fd int, state *State) error { 74 if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&state.termios)), 0, 0, 0); err != 0 { 75 return err 76 } 77 return nil 78 } 79 80 // GetSize returns the dimensions of the given terminal. 81 func GetSize(fd int) (width, height int, err error) { 82 var dimensions [4]uint16 83 84 if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(&dimensions)), 0, 0, 0); err != 0 { 85 return -1, -1, err 86 } 87 return int(dimensions[1]), int(dimensions[0]), nil 88 } 89 90 // passwordReader is an io.Reader that reads from a specific file descriptor. 91 type passwordReader int 92 93 func (r passwordReader) Read(buf []byte) (int, error) { 94 return syscall.Read(int(r), buf) 95 } 96 97 // ReadPassword reads a line of input from a terminal without local echo. This 98 // is commonly used for inputting passwords and other sensitive data. The slice 99 // returned does not include the \n. 100 func ReadPassword(fd int) ([]byte, error) { 101 var oldState syscall.Termios 102 if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&oldState)), 0, 0, 0); err != 0 { 103 return nil, err 104 } 105 106 newState := oldState 107 newState.Lflag &^= syscall.ECHO 108 newState.Lflag |= syscall.ICANON | syscall.ISIG 109 newState.Iflag |= syscall.ICRNL 110 if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 { 111 return nil, err 112 } 113 114 defer func() { 115 syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&oldState)), 0, 0, 0) 116 }() 117 118 return readPasswordLine(passwordReader(fd)) 119 }