summaryrefslogtreecommitdiff
path: root/src/mongo/gotools/common/password/pass_util_solaris.go
blob: f5f22dc83f86261cdff1487a1ea30e414ab40356 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
package password

import (
	"os"
	"syscall"
	"unsafe"
)

// This file is a mess based primarily on
// 		"github.com/howeyc/gopass"
// 		"golang.org/x/crypto/ssh/terminal"
// with extra unistd.h ripped from solaris on amd64
//
// TODO: get some of these changes merged into the above two packages

// ioctl constants -- not defined in solaris syscall pkg
const (
	SYS_IOCTL = 54
	TCGETS    = 21517
	TCSETS    = 21518
	ttyfd     = 0 //STDIN
)

// getTermios reads the current termios settings into the
// given termios struct.
func getTermios(term *syscall.Termios) error {
	_, _, errno := syscall.Syscall(SYS_IOCTL,
		uintptr(ttyfd), uintptr(TCGETS),
		uintptr(unsafe.Pointer(term)))
	if errno != 0 {
		return os.NewSyscallError("SYS_IOCTL", errno)
	}
	return nil
}

// setTermios applies the supplied termios settings
func setTermios(term *syscall.Termios) error {
	_, _, errno := syscall.Syscall(SYS_IOCTL,
		uintptr(ttyfd), uintptr(TCSETS),
		uintptr(unsafe.Pointer(term)))
	if errno != 0 {
		return os.NewSyscallError("SYS_IOCTL", errno)
	}
	return nil
}

// setRaw puts the terminal into "raw" mode, which takes
// in all key presses and does not echo them.
func setRaw(term syscall.Termios) error {
	termCopy := term
	termCopy.Iflag &^= syscall.ISTRIP | syscall.INLCR |
		syscall.ICRNL | syscall.IGNCR | syscall.IXON | syscall.IXOFF
	termCopy.Lflag &^= syscall.ECHO | syscall.ICANON | syscall.ISIG
	return setTermios(&termCopy)
}

// isTerminal checks if we are reading from a terminal (instead of a pipe).
func IsTerminal() bool {
	var termios syscall.Termios
	_, _, errno := syscall.Syscall(SYS_IOCTL,
		uintptr(ttyfd), TCGETS,
		uintptr(unsafe.Pointer(&termios)))
	return errno == 0
}

// readChar safely gets one byte from stdin
func readChar() byte {
	var originalTerm syscall.Termios
	if err := getTermios(&originalTerm); err != nil {
		panic(err) // should not happen on amd64 solaris (untested on sparc)
	}
	if err := setRaw(originalTerm); err != nil {
		panic(err)
	}
	defer func() {
		// make sure we return the termios back to normal
		if err := setTermios(&originalTerm); err != nil {
			panic(err)
		}
	}()

	// read a single byte then reset the terminal state
	var singleChar [1]byte
	if n, err := syscall.Read(ttyfd, singleChar[:]); n == 0 || err != nil {
		panic(err)
	}
	return singleChar[0]
}

// get password from terminal
func GetPass() string {
	// keep reading in characters until we hit a stopping point
	pass := []byte{}
	for {
		ch := readChar()
		if ch == backspaceKey || ch == deleteKey {
			if len(pass) > 0 {
				pass = pass[:len(pass)-1]
			}
		} else if ch == carriageReturnKey || ch == newLineKey || ch == eotKey || ch == eofKey {
			break
		} else if ch != 0 {
			pass = append(pass, ch)
		}
	}
	return string(pass)
}