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,64 @@ 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 signal .Stop (interruptChannel )
49
+ defer close (interruptChannel )
50
+
51
+ go func () {
52
+ for range interruptChannel {
53
+ if oldState != nil {
54
+ term .Restore (fd , oldState )
55
+ }
56
+ signal .Stop (interruptChannel )
57
+ syscall .Kill (syscall .Getpid (), syscall .SIGINT )
58
+ }
59
+ }()
60
+ return term .ReadPassword (fd )
61
+ } else {
62
+ /* Suggestion 2: Gracefully handle the interrupt signal, but leave the terminal input blocked */
63
+
64
+ type ByteBuffer struct {
65
+ buf []byte
66
+ err error
67
+ }
68
+
69
+ oldState , _ := term .GetState (fd )
70
+ interruptChannel := make (chan os.Signal , 1 )
71
+ signal .Notify (interruptChannel , os .Interrupt )
72
+ defer signal .Stop (interruptChannel )
73
+ defer close (interruptChannel )
74
+
75
+ // Read from the file descriptor or wait for the interrupt signal
76
+ inputChannel := make (chan ByteBuffer , 1 )
77
+ defer close (inputChannel )
78
+ go func () {
79
+ var buf ByteBuffer
80
+ buf .buf , buf .err = term .ReadPassword (fd )
81
+ inputChannel <- buf
82
+ }()
83
+ select {
84
+ case <- interruptChannel :
85
+ if oldState != nil {
86
+ term .Restore (fd , oldState )
87
+ }
88
+ return make ([]byte , 0 ), fmt .Errorf ("interrupted" )
89
+ case buf := <- inputChannel :
90
+ return buf .buf , buf .err
91
+ }
92
+ }
93
+ }
0 commit comments