@@ -7,13 +7,15 @@ import (
7
7
"errors"
8
8
"fmt"
9
9
"os"
10
+ "os/signal"
10
11
"path/filepath"
11
12
"sync"
12
13
"syscall"
13
14
14
15
"github.com/containers/storage/pkg/homedir"
15
16
"github.com/containers/storage/pkg/unshare"
16
17
"github.com/sirupsen/logrus"
18
+ terminal "golang.org/x/term"
17
19
)
18
20
19
21
var (
@@ -89,3 +91,45 @@ func GetRuntimeDir() (string, error) {
89
91
}
90
92
return rootlessRuntimeDir , nil
91
93
}
94
+
95
+ // ReadPassword reads a password from the terminal without echo.
96
+ func ReadPassword (fd int ) ([]byte , error ) {
97
+ // Store and restore the terminal status on interruptions to
98
+ // avoid that the terminal remains in the password state
99
+ // This is necessary as for https://github.com/golang/go/issues/31180
100
+
101
+ oldState , err := terminal .GetState (fd )
102
+ if err != nil {
103
+ return make ([]byte , 0 ), err
104
+ }
105
+
106
+ type Buffer struct {
107
+ Buffer []byte
108
+ Error error
109
+ }
110
+ errorChannel := make (chan Buffer , 1 )
111
+
112
+ // SIGINT and SIGTERM restore the terminal, otherwise the no-echo mode would remain intact
113
+ interruptChannel := make (chan os.Signal , 1 )
114
+ signal .Notify (interruptChannel , syscall .SIGINT , syscall .SIGTERM )
115
+ defer func () {
116
+ signal .Stop (interruptChannel )
117
+ close (interruptChannel )
118
+ }()
119
+ go func () {
120
+ for range interruptChannel {
121
+ if oldState != nil {
122
+ _ = terminal .Restore (fd , oldState )
123
+ }
124
+ errorChannel <- Buffer {Buffer : make ([]byte , 0 ), Error : ErrInterrupt }
125
+ }
126
+ }()
127
+
128
+ go func () {
129
+ buf , err := terminal .ReadPassword (fd )
130
+ errorChannel <- Buffer {Buffer : buf , Error : err }
131
+ }()
132
+
133
+ buf := <- errorChannel
134
+ return buf .Buffer , buf .Error
135
+ }
0 commit comments