1
0
Fork 0
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:
David Calavera 2013-08-03 16:43:20 -07:00
parent 23dc52f528
commit 2357fecc92
2 changed files with 68 additions and 89 deletions

View file

@ -2,6 +2,7 @@ package docker
import ( import (
"archive/tar" "archive/tar"
"bufio"
"bytes" "bytes"
"encoding/json" "encoding/json"
"flag" "flag"
@ -25,7 +26,6 @@ import (
"syscall" "syscall"
"text/tabwriter" "text/tabwriter"
"time" "time"
"unicode"
) )
var ( var (
@ -252,75 +252,18 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
// 'docker login': login / register a user to registry service. // 'docker login': login / register a user to registry service.
func (cli *DockerCli) CmdLogin(args ...string) error { 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") cmd := Subcmd("login", "[OPTIONS]", "Register or Login to the docker registry server")
flUsername := cmd.String("u", "", "username")
flPassword := cmd.String("p", "", "password") username := *cmd.String("u", "", "username")
flEmail := cmd.String("e", "", "email") password := *cmd.String("p", "", "password")
email := *cmd.String("e", "", "email")
err := cmd.Parse(args) err := cmd.Parse(args)
if err != nil { if err != nil {
return nil return nil
} }
cli.LoadConfigFile() promptDefault := func(prompt string, configDefault string) {
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) {
if configDefault == "" { if configDefault == "" {
fmt.Fprintf(cli.out, "%s: ", prompt) fmt.Fprintf(cli.out, "%s: ", prompt)
} else { } 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()] authconfig, ok := cli.configFile.Configs[auth.IndexServerAddress()]
if !ok { if !ok {
authconfig = auth.AuthConfig{} authconfig = auth.AuthConfig{}
} }
if *flUsername == "" { if username == "" {
promptDefault("Username", authconfig.Username) promptDefault("Username", authconfig.Username)
username = readAndEchoString(cli.in, cli.out) username, _ = readInput(cli.in)
if username == "" { if username == "" {
username = authconfig.Username username = authconfig.Username
} }
} else {
username = *flUsername
} }
if username != authconfig.Username { if username != authconfig.Username {
if *flPassword == "" { if password == "" {
oldState, _ := term.SaveState(cli.terminalFd)
fmt.Fprintf(cli.out, "Password: ") 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 == "" { if password == "" {
return fmt.Errorf("Error : Password Required") return fmt.Errorf("Error : Password Required")
} }
} else {
password = *flPassword
} }
if *flEmail == "" { if email == "" {
promptDefault("Email", authconfig.Email) promptDefault("\nEmail", authconfig.Email)
email = readAndEchoString(cli.in, cli.out) email, _ = readInput(cli.in)
if email == "" { if email == "" {
email = authconfig.Email email = authconfig.Email
} }
} else {
email = *flEmail
} }
} else { } else {
password = authconfig.Password password = authconfig.Password
email = authconfig.Email email = authconfig.Email
} }
if oldState != nil {
term.RestoreTerminal(cli.terminalFd, oldState)
}
authconfig.Username = username authconfig.Username = username
authconfig.Password = password authconfig.Password = password
authconfig.Email = email 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") == "" { 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 { if err != nil {
return err return err
} }

View file

@ -1,6 +1,8 @@
package term package term
import ( import (
"fmt"
"io"
"os" "os"
"os/signal" "os/signal"
"syscall" "syscall"
@ -43,17 +45,43 @@ func RestoreTerminal(fd uintptr, state *State) error {
return err 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) oldState, err := MakeRaw(fd)
if err != nil { if err != nil {
return nil, err return nil, err
} }
c := make(chan os.Signal, 1) HandleInterrupt(fd, out, oldState)
signal.Notify(c, os.Interrupt)
go func() {
_ = <-c
RestoreTerminal(fd, oldState)
os.Exit(0)
}()
return oldState, err return oldState, err
} }