diff --git a/commands.go b/commands.go index ffa94973bf..b2a4d3db13 100644 --- a/commands.go +++ b/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 ( @@ -254,75 +254,20 @@ 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") + + var username, password, email string + + cmd.StringVar(&username, "u", "", "username") + cmd.StringVar(&password, "p", "", "password") + cmd.StringVar(&email, "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 { @@ -330,47 +275,59 @@ func (cli *DockerCli) CmdLogin(args ...string) error { } } + readInput := func(in io.Reader, out io.Writer) string { + reader := bufio.NewReader(in) + line, _, err := reader.ReadLine() + if err != nil { + fmt.Fprintln(out, err.Error()) + os.Exit(1) + } + return string(line) + } + + cli.LoadConfigFile() 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, cli.out) 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, oldState) + + password = readInput(cli.in, cli.out) + fmt.Fprint(cli.out, "\n") + + term.RestoreTerminal(cli.terminalFd, oldState) + if password == "" { return fmt.Errorf("Error : Password Required") } - } else { - password = *flPassword } - if *flEmail == "" { + if email == "" { promptDefault("Email", authconfig.Email) - email = readAndEchoString(cli.in, cli.out) + email = readInput(cli.in, cli.out) 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 diff --git a/term/term.go b/term/term.go index f4d66a71d6..5929c2caa1 100644 --- a/term/term.go +++ b/term/term.go @@ -43,17 +43,42 @@ func RestoreTerminal(fd uintptr, state *State) error { return err } +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, state *State) error { + newState := state.termios + newState.Lflag &^= syscall.ECHO + + if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, setTermios, uintptr(unsafe.Pointer(&newState))); err != 0 { + return err + } + handleInterrupt(fd, state) + return nil +} + func SetRawTerminal(fd uintptr) (*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, oldState) return oldState, err } + +func handleInterrupt(fd uintptr, state *State) { + sigchan := make(chan os.Signal, 1) + signal.Notify(sigchan, os.Interrupt) + + go func() { + _ = <-sigchan + RestoreTerminal(fd, state) + os.Exit(0) + }() +}