mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Stop making a raw terminal to ask for registry login credentials.
It only disables echo asking for the password and lets the terminal to handle everything else. It fixes #1392 since blank spaces are not discarded as they did before. It also cleans the login code a little bit to improve readability.
This commit is contained in:
parent
23dc52f528
commit
2357fecc92
2 changed files with 68 additions and 89 deletions
113
commands.go
113
commands.go
|
@ -2,6 +2,7 @@ package docker
|
|||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
|
@ -25,7 +26,6 @@ import (
|
|||
"syscall"
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -252,75 +252,18 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
|||
|
||||
// 'docker login': login / register a user to registry service.
|
||||
func (cli *DockerCli) CmdLogin(args ...string) error {
|
||||
var readStringOnRawTerminal = func(stdin io.Reader, stdout io.Writer, echo bool) string {
|
||||
char := make([]byte, 1)
|
||||
buffer := make([]byte, 64)
|
||||
var i = 0
|
||||
for i < len(buffer) {
|
||||
n, err := stdin.Read(char)
|
||||
if n > 0 {
|
||||
if char[0] == '\r' || char[0] == '\n' {
|
||||
stdout.Write([]byte{'\r', '\n'})
|
||||
break
|
||||
} else if char[0] == 127 || char[0] == '\b' {
|
||||
if i > 0 {
|
||||
if echo {
|
||||
stdout.Write([]byte{'\b', ' ', '\b'})
|
||||
}
|
||||
i--
|
||||
}
|
||||
} else if !unicode.IsSpace(rune(char[0])) &&
|
||||
!unicode.IsControl(rune(char[0])) {
|
||||
if echo {
|
||||
stdout.Write(char)
|
||||
}
|
||||
buffer[i] = char[0]
|
||||
i++
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
fmt.Fprintf(stdout, "Read error: %v\r\n", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return string(buffer[:i])
|
||||
}
|
||||
var readAndEchoString = func(stdin io.Reader, stdout io.Writer) string {
|
||||
return readStringOnRawTerminal(stdin, stdout, true)
|
||||
}
|
||||
var readString = func(stdin io.Reader, stdout io.Writer) string {
|
||||
return readStringOnRawTerminal(stdin, stdout, false)
|
||||
}
|
||||
|
||||
cmd := Subcmd("login", "[OPTIONS]", "Register or Login to the docker registry server")
|
||||
flUsername := cmd.String("u", "", "username")
|
||||
flPassword := cmd.String("p", "", "password")
|
||||
flEmail := cmd.String("e", "", "email")
|
||||
|
||||
username := *cmd.String("u", "", "username")
|
||||
password := *cmd.String("p", "", "password")
|
||||
email := *cmd.String("e", "", "email")
|
||||
err := cmd.Parse(args)
|
||||
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
cli.LoadConfigFile()
|
||||
|
||||
var oldState *term.State
|
||||
if *flUsername == "" || *flPassword == "" || *flEmail == "" {
|
||||
oldState, err = term.SetRawTerminal(cli.terminalFd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer term.RestoreTerminal(cli.terminalFd, oldState)
|
||||
}
|
||||
|
||||
var (
|
||||
username string
|
||||
password string
|
||||
email string
|
||||
)
|
||||
|
||||
var promptDefault = func(prompt string, configDefault string) {
|
||||
promptDefault := func(prompt string, configDefault string) {
|
||||
if configDefault == "" {
|
||||
fmt.Fprintf(cli.out, "%s: ", prompt)
|
||||
} else {
|
||||
|
@ -328,47 +271,55 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
|
|||
}
|
||||
}
|
||||
|
||||
readInput := func(in io.Reader) (string, error) {
|
||||
reader := bufio.NewReader(in)
|
||||
line, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return line, nil
|
||||
}
|
||||
|
||||
authconfig, ok := cli.configFile.Configs[auth.IndexServerAddress()]
|
||||
if !ok {
|
||||
authconfig = auth.AuthConfig{}
|
||||
}
|
||||
|
||||
if *flUsername == "" {
|
||||
if username == "" {
|
||||
promptDefault("Username", authconfig.Username)
|
||||
username = readAndEchoString(cli.in, cli.out)
|
||||
username, _ = readInput(cli.in)
|
||||
if username == "" {
|
||||
username = authconfig.Username
|
||||
}
|
||||
} else {
|
||||
username = *flUsername
|
||||
}
|
||||
|
||||
if username != authconfig.Username {
|
||||
if *flPassword == "" {
|
||||
if password == "" {
|
||||
oldState, _ := term.SaveState(cli.terminalFd)
|
||||
fmt.Fprintf(cli.out, "Password: ")
|
||||
password = readString(cli.in, cli.out)
|
||||
|
||||
term.DisableEcho(cli.terminalFd, cli.out, oldState)
|
||||
password, _ = readInput(cli.in)
|
||||
|
||||
term.RestoreTerminal(cli.terminalFd, oldState)
|
||||
|
||||
if password == "" {
|
||||
return fmt.Errorf("Error : Password Required")
|
||||
}
|
||||
} else {
|
||||
password = *flPassword
|
||||
}
|
||||
|
||||
if *flEmail == "" {
|
||||
promptDefault("Email", authconfig.Email)
|
||||
email = readAndEchoString(cli.in, cli.out)
|
||||
if email == "" {
|
||||
promptDefault("\nEmail", authconfig.Email)
|
||||
email, _ = readInput(cli.in)
|
||||
if email == "" {
|
||||
email = authconfig.Email
|
||||
}
|
||||
} else {
|
||||
email = *flEmail
|
||||
}
|
||||
} else {
|
||||
password = authconfig.Password
|
||||
email = authconfig.Email
|
||||
}
|
||||
if oldState != nil {
|
||||
term.RestoreTerminal(cli.terminalFd, oldState)
|
||||
}
|
||||
|
||||
authconfig.Username = username
|
||||
authconfig.Password = password
|
||||
authconfig.Email = email
|
||||
|
@ -1694,7 +1645,7 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea
|
|||
}
|
||||
|
||||
if in != nil && setRawTerminal && cli.isTerminal && os.Getenv("NORAW") == "" {
|
||||
oldState, err := term.SetRawTerminal(cli.terminalFd)
|
||||
oldState, err := term.SetRawTerminal(cli.terminalFd, cli.out)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
44
term/term.go
44
term/term.go
|
@ -1,6 +1,8 @@
|
|||
package term
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
@ -43,17 +45,43 @@ func RestoreTerminal(fd uintptr, state *State) error {
|
|||
return err
|
||||
}
|
||||
|
||||
func SetRawTerminal(fd uintptr) (*State, error) {
|
||||
func SaveState(fd uintptr) (*State, error) {
|
||||
var oldState State
|
||||
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, getTermios, uintptr(unsafe.Pointer(&oldState.termios))); err != 0 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &oldState, nil
|
||||
}
|
||||
|
||||
func DisableEcho(fd uintptr, out io.Writer, state *State) error {
|
||||
newState := state.termios
|
||||
newState.Lflag &^= syscall.ECHO
|
||||
|
||||
HandleInterrupt(fd, out, state)
|
||||
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, setTermios, uintptr(unsafe.Pointer(&newState))); err != 0 {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func HandleInterrupt(fd uintptr, out io.Writer, state *State) {
|
||||
sigchan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigchan, os.Interrupt)
|
||||
|
||||
go func() {
|
||||
_ = <-sigchan
|
||||
fmt.Fprintf(out, "\n")
|
||||
RestoreTerminal(fd, state)
|
||||
os.Exit(0)
|
||||
}()
|
||||
}
|
||||
|
||||
func SetRawTerminal(fd uintptr, out io.Writer) (*State, error) {
|
||||
oldState, err := MakeRaw(fd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt)
|
||||
go func() {
|
||||
_ = <-c
|
||||
RestoreTerminal(fd, oldState)
|
||||
os.Exit(0)
|
||||
}()
|
||||
HandleInterrupt(fd, out, oldState)
|
||||
return oldState, err
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue