1
1
package util
2
2
3
- import "regexp"
3
+ import (
4
+ "fmt"
5
+ "os"
6
+ "os/signal"
7
+ "regexp"
8
+ "syscall"
9
+
10
+ "golang.org/x/term"
11
+ )
4
12
5
13
// StringInSlice determines if a string is in a string slice, returns bool
6
14
func StringInSlice (s string , sl []string ) bool {
@@ -22,3 +30,68 @@ func StringMatchRegexSlice(s string, re []string) bool {
22
30
}
23
31
return false
24
32
}
33
+
34
+ // ReadPassword reads a password from the terminal and restores the terminal state on interruption
35
+ func ReadPassword (fd int ) ([]byte , error ) {
36
+ // Store and restore the terminal status on interruptions to
37
+ // avoid that the terminal remains in the password state
38
+ // This is necessary as for https://github.com/golang/go/issues/31180
39
+
40
+ /* WARNING: This commit contains two suggestions and should as such not be commited !! */
41
+ reemitSolution := true
42
+
43
+ /* Suggestion 1: Re-emit the interrupt signal, but restore the terminal in-between*/
44
+ if reemitSolution {
45
+ oldState , _ := term .GetState (fd )
46
+ interruptChannel := make (chan os.Signal , 1 )
47
+ signal .Notify (interruptChannel , os .Interrupt )
48
+ defer func () {
49
+ signal .Stop (interruptChannel )
50
+ close (interruptChannel )
51
+ }()
52
+ go func () {
53
+ for range interruptChannel {
54
+ if oldState != nil {
55
+ term .Restore (fd , oldState )
56
+ }
57
+ // Call the default SIGINT handler
58
+ signal .Reset (os .Interrupt )
59
+ syscall .Kill (syscall .Getpid (), syscall .SIGINT )
60
+ }
61
+ }()
62
+ return term .ReadPassword (fd )
63
+ } else {
64
+ /* Suggestion 2: Gracefully handle the interrupt signal, but leave the terminal input blocked */
65
+
66
+ type ByteBuffer struct {
67
+ buf []byte
68
+ err error
69
+ }
70
+
71
+ oldState , _ := term .GetState (fd )
72
+ interruptChannel := make (chan os.Signal , 1 )
73
+ signal .Notify (interruptChannel , os .Interrupt )
74
+ defer func () {
75
+ signal .Stop (interruptChannel )
76
+ close (interruptChannel )
77
+ }()
78
+
79
+ // Read from the file descriptor or wait for the interrupt signal
80
+ inputChannel := make (chan ByteBuffer , 1 )
81
+ defer close (inputChannel )
82
+ go func () {
83
+ var buf ByteBuffer
84
+ buf .buf , buf .err = term .ReadPassword (fd )
85
+ inputChannel <- buf
86
+ }()
87
+ select {
88
+ case <- interruptChannel :
89
+ if oldState != nil {
90
+ term .Restore (fd , oldState )
91
+ }
92
+ return make ([]byte , 0 ), fmt .Errorf ("interrupted" )
93
+ case buf := <- inputChannel :
94
+ return buf .buf , buf .err
95
+ }
96
+ }
97
+ }
0 commit comments