github.com/lmars/docker@v1.6.0-rc2/pkg/term/term.go (about)

     1  // +build !windows
     2  
     3  package term
     4  
     5  import (
     6  	"errors"
     7  	"io"
     8  	"os"
     9  	"os/signal"
    10  	"syscall"
    11  	"unsafe"
    12  )
    13  
    14  var (
    15  	ErrInvalidState = errors.New("Invalid terminal state")
    16  )
    17  
    18  type State struct {
    19  	termios Termios
    20  }
    21  
    22  type Winsize struct {
    23  	Height uint16
    24  	Width  uint16
    25  	x      uint16
    26  	y      uint16
    27  }
    28  
    29  func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
    30  	return os.Stdin, os.Stdout, os.Stderr
    31  }
    32  
    33  func GetFdInfo(in interface{}) (uintptr, bool) {
    34  	var inFd uintptr
    35  	var isTerminalIn bool
    36  	if file, ok := in.(*os.File); ok {
    37  		inFd = file.Fd()
    38  		isTerminalIn = IsTerminal(inFd)
    39  	}
    40  	return inFd, isTerminalIn
    41  }
    42  
    43  func GetWinsize(fd uintptr) (*Winsize, error) {
    44  	ws := &Winsize{}
    45  	_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(ws)))
    46  	// Skipp errno = 0
    47  	if err == 0 {
    48  		return ws, nil
    49  	}
    50  	return ws, err
    51  }
    52  
    53  func SetWinsize(fd uintptr, ws *Winsize) error {
    54  	_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(syscall.TIOCSWINSZ), uintptr(unsafe.Pointer(ws)))
    55  	// Skipp errno = 0
    56  	if err == 0 {
    57  		return nil
    58  	}
    59  	return err
    60  }
    61  
    62  // IsTerminal returns true if the given file descriptor is a terminal.
    63  func IsTerminal(fd uintptr) bool {
    64  	var termios Termios
    65  	return tcget(fd, &termios) == 0
    66  }
    67  
    68  // Restore restores the terminal connected to the given file descriptor to a
    69  // previous state.
    70  func RestoreTerminal(fd uintptr, state *State) error {
    71  	if state == nil {
    72  		return ErrInvalidState
    73  	}
    74  	if err := tcset(fd, &state.termios); err != 0 {
    75  		return err
    76  	}
    77  	return nil
    78  }
    79  
    80  func SaveState(fd uintptr) (*State, error) {
    81  	var oldState State
    82  	if err := tcget(fd, &oldState.termios); err != 0 {
    83  		return nil, err
    84  	}
    85  
    86  	return &oldState, nil
    87  }
    88  
    89  func DisableEcho(fd uintptr, state *State) error {
    90  	newState := state.termios
    91  	newState.Lflag &^= syscall.ECHO
    92  
    93  	if err := tcset(fd, &newState); err != 0 {
    94  		return err
    95  	}
    96  	handleInterrupt(fd, state)
    97  	return nil
    98  }
    99  
   100  func SetRawTerminal(fd uintptr) (*State, error) {
   101  	oldState, err := MakeRaw(fd)
   102  	if err != nil {
   103  		return nil, err
   104  	}
   105  	handleInterrupt(fd, oldState)
   106  	return oldState, err
   107  }
   108  
   109  func handleInterrupt(fd uintptr, state *State) {
   110  	sigchan := make(chan os.Signal, 1)
   111  	signal.Notify(sigchan, os.Interrupt)
   112  
   113  	go func() {
   114  		_ = <-sigchan
   115  		RestoreTerminal(fd, state)
   116  		os.Exit(0)
   117  	}()
   118  }