2018-02-05 16:13:06 -05:00
|
|
|
// +build linux darwin freebsd netbsd openbsd solaris dragonfly
|
|
|
|
// +build !appengine
|
|
|
|
|
|
|
|
package pb
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"os/signal"
|
|
|
|
"sync"
|
|
|
|
"syscall"
|
|
|
|
|
|
|
|
"golang.org/x/sys/unix"
|
|
|
|
)
|
|
|
|
|
|
|
|
var ErrPoolWasStarted = errors.New("Bar pool was started")
|
|
|
|
|
|
|
|
var (
|
|
|
|
echoLockMutex sync.Mutex
|
|
|
|
origTermStatePtr *unix.Termios
|
|
|
|
tty *os.File
|
|
|
|
)
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
echoLockMutex.Lock()
|
|
|
|
defer echoLockMutex.Unlock()
|
|
|
|
|
|
|
|
var err error
|
|
|
|
tty, err = os.Open("/dev/tty")
|
|
|
|
if err != nil {
|
|
|
|
tty = os.Stdin
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// terminalWidth returns width of the terminal.
|
|
|
|
func terminalWidth() (int, error) {
|
|
|
|
echoLockMutex.Lock()
|
|
|
|
defer echoLockMutex.Unlock()
|
|
|
|
|
|
|
|
fd := int(tty.Fd())
|
|
|
|
|
|
|
|
ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return int(ws.Col), nil
|
|
|
|
}
|
|
|
|
|
2018-04-27 02:20:56 -04:00
|
|
|
func lockEcho() (shutdownCh chan struct{}, err error) {
|
2018-02-05 16:13:06 -05:00
|
|
|
echoLockMutex.Lock()
|
|
|
|
defer echoLockMutex.Unlock()
|
|
|
|
if origTermStatePtr != nil {
|
2018-04-27 02:20:56 -04:00
|
|
|
return shutdownCh, ErrPoolWasStarted
|
2018-02-05 16:13:06 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
fd := int(tty.Fd())
|
|
|
|
|
2018-04-27 02:20:56 -04:00
|
|
|
origTermStatePtr, err = unix.IoctlGetTermios(fd, ioctlReadTermios)
|
2018-02-05 16:13:06 -05:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("Can't get terminal settings: %v", err)
|
|
|
|
}
|
|
|
|
|
2018-04-27 02:20:56 -04:00
|
|
|
oldTermios := *origTermStatePtr
|
2018-02-05 16:13:06 -05:00
|
|
|
newTermios := oldTermios
|
|
|
|
newTermios.Lflag &^= syscall.ECHO
|
|
|
|
newTermios.Lflag |= syscall.ICANON | syscall.ISIG
|
|
|
|
newTermios.Iflag |= syscall.ICRNL
|
|
|
|
if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, &newTermios); err != nil {
|
|
|
|
return nil, fmt.Errorf("Can't set terminal settings: %v", err)
|
|
|
|
}
|
|
|
|
|
2018-04-27 02:20:56 -04:00
|
|
|
shutdownCh = make(chan struct{})
|
|
|
|
go catchTerminate(shutdownCh)
|
2018-02-05 16:13:06 -05:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func unlockEcho() error {
|
|
|
|
echoLockMutex.Lock()
|
|
|
|
defer echoLockMutex.Unlock()
|
|
|
|
if origTermStatePtr == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
fd := int(tty.Fd())
|
|
|
|
|
|
|
|
if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, origTermStatePtr); err != nil {
|
|
|
|
return fmt.Errorf("Can't set terminal settings: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
origTermStatePtr = nil
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// listen exit signals and restore terminal state
|
2018-04-27 02:20:56 -04:00
|
|
|
func catchTerminate(shutdownCh chan struct{}) {
|
2018-02-05 16:13:06 -05:00
|
|
|
sig := make(chan os.Signal, 1)
|
|
|
|
signal.Notify(sig, os.Interrupt, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGKILL)
|
|
|
|
defer signal.Stop(sig)
|
|
|
|
select {
|
2018-04-27 02:20:56 -04:00
|
|
|
case <-shutdownCh:
|
2018-02-05 16:13:06 -05:00
|
|
|
unlockEcho()
|
|
|
|
case <-sig:
|
|
|
|
unlockEcho()
|
|
|
|
}
|
|
|
|
}
|