func (cli *HyperClient) hijack(method, path string, setRawTerminal bool, in io.ReadCloser, stdout, stderr io.Writer, started chan io.Closer, data interface{}, hostname string) error { defer func() { if started != nil { close(started) } }() params, err := cli.encodeData(data) if err != nil { return err } req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", utils.APIVERSION, path), params) if err != nil { return err } req.Header.Set("User-Agent", "Hyper-Client/"+utils.VERSION) req.Header.Set("Content-Type", "text/plain") req.Header.Set("Connection", "Upgrade") req.Header.Set("Upgrade", "tcp") req.Host = cli.addr dial, err := cli.dial() if err != nil { return err } // When we set up a TCP connection for hijack, there could be long periods // of inactivity (a long running command with no output) that in certain // network setups may cause ECONNTIMEOUT, leaving the client in an unknown // state. Setting TCP KeepAlive on the socket connection will prohibit // ECONNTIMEOUT unless the socket connection truly is broken if tcpConn, ok := dial.(*net.TCPConn); ok { tcpConn.SetKeepAlive(true) tcpConn.SetKeepAlivePeriod(30 * time.Second) } if err != nil { if strings.Contains(err.Error(), "connection refused") { return fmt.Errorf("Cannot connect to the Hyper daemon. Is 'hyperd' running on this host?") } return err } clientconn := httputil.NewClientConn(dial, nil) defer clientconn.Close() // Server hijacks the connection, error 'connection closed' expected _, err = clientconn.Do(req) if err != nil { fmt.Printf("Client DO: %s\n", err.Error()) } rwc, br := clientconn.Hijack() defer rwc.Close() if started != nil { started <- rwc } var ( receiveStdout chan error oldState *term.State ) if in != nil && setRawTerminal { // fmt.Printf("In the Raw Terminal!!!\n") oldState, err = term.SetRawTerminal(cli.inFd) if err != nil { return err } defer term.RestoreTerminal(cli.inFd, oldState) } if stdout != nil || stderr != nil { receiveStdout = promise.Go(func() (err error) { defer func() { if in != nil { if setRawTerminal { term.RestoreTerminal(cli.inFd, oldState) } } }() _, err = io.Copy(stdout, br) // fmt.Printf("[hijack] End of stdout\n") return err }) } sendStdin := promise.Go(func() error { if in != nil { io.Copy(rwc, in) // fmt.Printf("[hijack] End of stdin\n") } if conn, ok := rwc.(interface { CloseWrite() error }); ok { if err := conn.CloseWrite(); err != nil { fmt.Printf("Couldn't send EOF: %s", err.Error()) } } // Discard errors due to pipe interruption return nil }) if stdout != nil || stderr != nil { if err := <-receiveStdout; err != nil { fmt.Printf("Error receiveStdout: %s\n", err.Error()) return err } sendStdin <- nil } if in != nil { if err := <-sendStdin; err != nil { fmt.Printf("Error sendStdin: %s\n", err.Error()) return err } } return nil }
// CmdLogin logs in or registers a user to a Docker registry service. // // If no server is specified, the user will be logged into or registered to the registry's index server. // // Usage: docker login SERVER func (cli *HyperClient) HyperCmdLogin(args ...string) error { var opts struct { Email string `short:"e" long:"email" default:"" value-name:"\"\"" description:"Email"` Username string `short:"u" long:"username" default:"" value-name:"\"\"" description:"Username"` Password string `short:"p" long:"password" default:"" value-name:"\"\"" description:"Password"` } var parser = gflag.NewParser(&opts, gflag.Default) parser.Usage = "login [SERVER]\n\nRegister or log in to a Docker registry server, if no server is\nspecified \"" + registry.IndexServerAddress() + "\" is the default." args, err := parser.Parse() if err != nil { if !strings.Contains(err.Error(), "Usage") { return err } else { return nil } } username := opts.Username password := opts.Password email := opts.Email serverAddress := registry.IndexServerAddress() if len(args) > 1 { serverAddress = args[1] } promptDefault := func(prompt string, configDefault string) { if configDefault == "" { fmt.Fprintf(cli.out, "%s: ", prompt) } else { fmt.Fprintf(cli.out, "%s (%s): ", prompt, configDefault) } } 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) } authconfig, ok := cli.configFile.AuthConfigs[serverAddress] if !ok { authconfig = cliconfig.AuthConfig{} } if username == "" { promptDefault("Username", authconfig.Username) username = readInput(cli.in, cli.out) username = strings.Trim(username, " ") if username == "" { username = authconfig.Username } } // Assume that a different username means they may not want to use // the password or email from the config file, so prompt them if username != authconfig.Username { if password == "" { oldState, err := term.SaveState(cli.inFd) if err != nil { return err } fmt.Fprintf(cli.out, "Password: "******"\n") term.RestoreTerminal(cli.inFd, oldState) if password == "" { return fmt.Errorf("Error : Password Required") } } if email == "" { promptDefault("Email", authconfig.Email) email = readInput(cli.in, cli.out) if email == "" { email = authconfig.Email } } } else { // However, if they don't override the username use the // password or email from the cmd line if specified. IOW, allow // then to change/override them. And if not specified, just // use what's in the config file if password == "" { password = authconfig.Password } if email == "" { email = authconfig.Email } } authconfig.Username = username authconfig.Password = password authconfig.Email = email authconfig.ServerAddress = serverAddress cli.configFile.AuthConfigs[serverAddress] = authconfig stream, statusCode, err := cli.call("POST", "/auth", cli.configFile.AuthConfigs[serverAddress], nil) if statusCode == 401 { delete(cli.configFile.AuthConfigs, serverAddress) if err2 := cli.configFile.Save(); err2 != nil { fmt.Fprintf(cli.out, "WARNING: could not save config file: %v\n", err2) } return err } if err != nil { return err } var response types.AuthResponse if err := json.NewDecoder(stream).Decode(&response); err != nil { // Upon error, remove entry delete(cli.configFile.AuthConfigs, serverAddress) return err } if err := cli.configFile.Save(); err != nil { return fmt.Errorf("Error saving config file: %v", err) } fmt.Fprintf(cli.out, "WARNING: login credentials saved in %s\n", cli.configFile.Filename()) if response.Status != "" { fmt.Fprintf(cli.out, "%s\n", response.Status) } return nil }