Skip to content

Commit b2c848f

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: phoenix <[email protected]>
1 parent 6585c6e commit b2c848f

File tree

3 files changed

+58
-2
lines changed

3 files changed

+58
-2
lines changed

pkg/auth/auth.go

Lines changed: 2 additions & 2 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
// ErrNewCredentialsInvalid means that the new user-provided credentials are
@@ -269,7 +269,7 @@ func getUserAndPass(opts *LoginOptions, password, userFromAuthFile string) (user
269269
}
270270
if password == "" {
271271
fmt.Fprint(opts.Stdout, "Password: ")
272-
pass, err := terminal.ReadPassword(int(os.Stdin.Fd()))
272+
pass, err := util.ReadPassword(int(os.Stdin.Fd()))
273273
if err != nil {
274274
return "", "", fmt.Errorf("reading password: %w", err)
275275
}

pkg/util/util_supported.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@ import (
77
"errors"
88
"fmt"
99
"os"
10+
"os/signal"
1011
"path/filepath"
1112
"sync"
1213
"syscall"
1314

1415
"github.com/containers/storage/pkg/homedir"
1516
"github.com/containers/storage/pkg/unshare"
1617
"github.com/sirupsen/logrus"
18+
terminal "golang.org/x/term"
1719
)
1820

1921
var (
@@ -89,3 +91,45 @@ func GetRuntimeDir() (string, error) {
8991
}
9092
return rootlessRuntimeDir, nil
9193
}
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: fmt.Errorf("interrupted")}
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+
}

pkg/util/util_windows.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,21 @@ package util
55

66
import (
77
"errors"
8+
9+
terminal "golang.org/x/term"
810
)
911

1012
// getRuntimeDir returns the runtime directory
1113
func GetRuntimeDir() (string, error) {
1214
return "", errors.New("this function is not implemented for windows")
1315
}
16+
17+
// ReadPassword reads a password from the terminal.
18+
func ReadPassword(fd int) ([]byte, error) {
19+
oldState, _ := terminal.GetState(fd)
20+
buf, err := terminal.ReadPassword(fd)
21+
if oldState != nil {
22+
_ = terminal.Restore(fd, oldState)
23+
}
24+
return buf, err
25+
}

0 commit comments

Comments
 (0)