소스 검색

Merge pull request #1317 from calavera/login_signal

Exit from `docker login` on SIGTERM and SIGINT.
Michael Crosby 12 년 전
부모
커밋
d13c2ed24e
2개의 변경된 파일66개의 추가작업 그리고 84개의 파일을 삭제
  1. 36 79
      commands.go
  2. 30 5
      term/term.go

+ 36 - 79
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

+ 30 - 5
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)
+	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() {
-		_ = <-c
-		RestoreTerminal(fd, oldState)
+		_ = <-sigchan
+		RestoreTerminal(fd, state)
 		os.Exit(0)
 	}()
-	return oldState, err
 }