Skip to content

Commit 4e05db3

Browse files
committed
Add restore handler for password input
This commit restores the terminal state in case the program is interrupted while being in password read mode. This ensures the terminal remains usable, also if the password input is being cancelled. Signed-off-by: Felix Niederwanger <[email protected]>
1 parent 17f7e40 commit 4e05db3

File tree

2 files changed

+73
-4
lines changed

2 files changed

+73
-4
lines changed

pkg/auth/auth.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ import (
1010
"path/filepath"
1111
"strings"
1212

13+
"github.com/containers/common/pkg/util"
1314
"github.com/containers/image/v5/docker"
1415
"github.com/containers/image/v5/docker/reference"
1516
"github.com/containers/image/v5/pkg/docker/config"
1617
"github.com/containers/image/v5/pkg/sysregistriesv2"
1718
"github.com/containers/image/v5/types"
1819
"github.com/sirupsen/logrus"
19-
terminal "golang.org/x/term"
2020
)
2121

2222
// GetDefaultAuthFile returns env value REGISTRY_AUTH_FILE as default
@@ -244,12 +244,12 @@ func getUserAndPass(opts *LoginOptions, password, userFromAuthFile string) (user
244244
}
245245
if password == "" {
246246
fmt.Fprint(opts.Stdout, "Password: ")
247-
pass, err := terminal.ReadPassword(int(os.Stdin.Fd()))
247+
pass, err := util.ReadPassword(int(os.Stdin.Fd()))
248248
if err != nil {
249249
return "", "", fmt.Errorf("reading password: %w", err)
250250
}
251-
password = string(pass)
252251
fmt.Fprintln(opts.Stdout)
252+
password = string(pass)
253253
}
254254
return strings.TrimSpace(username), password, err
255255
}

pkg/util/util.go

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
package util
22

3-
import "regexp"
3+
import (
4+
"fmt"
5+
"os"
6+
"os/signal"
7+
"regexp"
8+
"syscall"
9+
10+
"golang.org/x/term"
11+
)
412

513
// StringInSlice determines if a string is in a string slice, returns bool
614
func StringInSlice(s string, sl []string) bool {
@@ -22,3 +30,64 @@ func StringMatchRegexSlice(s string, re []string) bool {
2230
}
2331
return false
2432
}
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

Comments
 (0)