func runCommand(args []string) error { var oldState *term.State var err error if term.IsTerminal(int(os.Stdin.Fd())) && os.Getenv("NORAW") == "" { oldState, err = term.MakeRaw(int(os.Stdin.Fd())) if err != nil { return err } defer term.Restore(int(os.Stdin.Fd()), oldState) c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt) go func() { for _ = range c { term.Restore(int(os.Stdin.Fd()), oldState) log.Printf("\nSIGINT received\n") os.Exit(0) } }() } // FIXME: we want to use unix sockets here, but net.UnixConn doesn't expose // CloseWrite(), which we need to cleanly signal that stdin is closed without // closing the connection. // See http://code.google.com/p/go/issues/detail?id=3345 if conn, err := rcli.Call("tcp", "127.0.0.1:4242", args...); err == nil { receiveStdout := docker.Go(func() error { _, err := io.Copy(os.Stdout, conn) return err }) sendStdin := docker.Go(func() error { _, err := io.Copy(conn, os.Stdin) if err := conn.CloseWrite(); err != nil { log.Printf("Couldn't send EOF: " + err.Error()) } return err }) if err := <-receiveStdout; err != nil { return err } if !term.IsTerminal(int(os.Stdin.Fd())) { if err := <-sendStdin; err != nil { return err } } } else { service, err := docker.NewServer() if err != nil { return err } if err := rcli.LocalCall(service, os.Stdin, os.Stdout, args...); err != nil { return err } } if oldState != nil { term.Restore(int(os.Stdin.Fd()), oldState) } return nil }
func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in *os.File, out io.Writer) error { req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", APIVERSION, path), nil) if err != nil { return err } req.Header.Set("User-Agent", "Docker-Client/"+VERSION) req.Header.Set("Content-Type", "plain/text") dial, err := net.Dial(cli.proto, cli.addr) if err != nil { return err } clientconn := httputil.NewClientConn(dial, nil) defer clientconn.Close() // Server hijacks the connection, error 'connection closed' expected clientconn.Do(req) rwc, br := clientconn.Hijack() defer rwc.Close() receiveStdout := utils.Go(func() error { _, err := io.Copy(out, br) return err }) if in != nil && setRawTerminal && term.IsTerminal(in.Fd()) && os.Getenv("NORAW") == "" { oldState, err := term.SetRawTerminal() if err != nil { return err } defer term.RestoreTerminal(oldState) } sendStdin := utils.Go(func() error { io.Copy(rwc, in) if err := rwc.(*net.TCPConn).CloseWrite(); err != nil { utils.Debugf("Couldn't send EOF: %s\n", err) } // Discard errors due to pipe interruption return nil }) if err := <-receiveStdout; err != nil { utils.Debugf("Error receiveStdout: %s", err) return err } if !term.IsTerminal(in.Fd()) { if err := <-sendStdin; err != nil { utils.Debugf("Error sendStdin: %s", err) return err } } return nil }
func (cli *DockerCli) hijack(method, path string, setRawTerminal bool) error { req, err := http.NewRequest(method, path, nil) if err != nil { return err } req.Header.Set("Content-Type", "plain/text") dial, err := net.Dial("tcp", fmt.Sprintf("%s:%d", cli.host, cli.port)) if err != nil { return err } clientconn := httputil.NewClientConn(dial, nil) clientconn.Do(req) defer clientconn.Close() rwc, br := clientconn.Hijack() defer rwc.Close() receiveStdout := utils.Go(func() error { _, err := io.Copy(os.Stdout, br) return err }) if setRawTerminal && term.IsTerminal(int(os.Stdin.Fd())) && os.Getenv("NORAW") == "" { if oldState, err := term.SetRawTerminal(); err != nil { return err } else { defer term.RestoreTerminal(oldState) } } sendStdin := utils.Go(func() error { _, err := io.Copy(rwc, os.Stdin) if err := rwc.(*net.TCPConn).CloseWrite(); err != nil { fmt.Fprintf(os.Stderr, "Couldn't send EOF: %s\n", err) } return err }) if err := <-receiveStdout; err != nil { return err } if !term.IsTerminal(int(os.Stdin.Fd())) { if err := <-sendStdin; err != nil { return err } } return nil }
func runCommand(args []string) error { // FIXME: we want to use unix sockets here, but net.UnixConn doesn't expose // CloseWrite(), which we need to cleanly signal that stdin is closed without // closing the connection. // See http://code.google.com/p/go/issues/detail?id=3345 if conn, err := rcli.Call("tcp", "127.0.0.1:4242", args...); err == nil { options := conn.GetOptions() if options.RawTerminal && term.IsTerminal(int(os.Stdin.Fd())) && os.Getenv("NORAW") == "" { if oldState, err := rcli.SetRawTerminal(); err != nil { return err } else { defer rcli.RestoreTerminal(oldState) } } receiveStdout := docker.Go(func() error { _, err := io.Copy(os.Stdout, conn) return err }) sendStdin := docker.Go(func() error { _, err := io.Copy(conn, os.Stdin) if err := conn.CloseWrite(); err != nil { log.Printf("Couldn't send EOF: " + err.Error()) } return err }) if err := <-receiveStdout; err != nil { return err } if !term.IsTerminal(int(os.Stdin.Fd())) { if err := <-sendStdin; err != nil { return err } } } else { service, err := docker.NewServer() if err != nil { return err } dockerConn := rcli.NewDockerLocalConn(os.Stdout) defer dockerConn.Close() if err := rcli.LocalCall(service, os.Stdin, dockerConn, args...); err != nil { return err } } return nil }
func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string) *DockerCli { var ( isTerminal = false terminalFd uintptr ) if in != nil { if file, ok := in.(*os.File); ok { terminalFd = file.Fd() isTerminal = term.IsTerminal(terminalFd) } } if err == nil { err = out } authConfig, _ := auth.LoadConfig(os.Getenv("HOME")) return &DockerCli{ proto: proto, addr: addr, authConfig: authConfig, in: in, out: out, err: err, isTerminal: isTerminal, terminalFd: terminalFd, } }
func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string) *DockerCli { var ( isTerminal = false terminalFd uintptr ) if in != nil { if file, ok := in.(*os.File); ok { terminalFd = file.Fd() isTerminal = term.IsTerminal(terminalFd) } } if err == nil { err = out } configFile, e := auth.LoadConfig(os.Getenv("HOME")) if e != nil { fmt.Fprintf(err, "WARNING: %s\n", e) } return &DockerCli{ proto: proto, addr: addr, configFile: configFile, in: in, out: out, err: err, isTerminal: isTerminal, terminalFd: terminalFd, } }
func (client *Client) Attach(in io.ReadCloser, out io.WriteCloser) { client.Connect() client.in = in if file, ok := client.in.(*os.File); ok { client.terminalFd = file.Fd() client.isTerminal = term.IsTerminal(client.terminalFd) } if !client.isTerminal { panic(fmt.Errorf("siphon: cannot attach, no tty")) } fmt.Fprintf(log.client, "attaching to tty\r\n") rawOldState, err := term.SetRawTerminal(client.terminalFd) if err != nil { panic(err) } defer term.RestoreTerminal(client.terminalFd, rawOldState) client.monitorTtySize() var track sync.WaitGroup track.Add(1) go func() { defer track.Done() io.Copy(out, client.stdout) fmt.Fprintf(log.client, "client output closed\r\n") }() // track.Add(1) // io.Copy will block indefinitely on 'in' regardless of if client.stdin has been closed, so we can't actually wait for this. go func() { io.Copy(client.stdin, in) fmt.Fprintf(log.client, "client input closed\r\n") }() track.Wait() }
func (c *Client) hijack(method, path string, setRawTerminal bool, in *os.File, errStream io.Writer, out io.Writer) error { req, err := http.NewRequest(method, c.getURL(path), nil) if err != nil { return err } req.Header.Set("Content-Type", "plain/text") dial, err := net.Dial("tcp", c.endpointURL.Host) if err != nil { return err } clientconn := httputil.NewClientConn(dial, nil) clientconn.Do(req) defer clientconn.Close() rwc, br := clientconn.Hijack() defer rwc.Close() errStdout := make(chan error, 1) go func() { _, err := io.Copy(out, br) errStdout <- err }() if in != nil && setRawTerminal && term.IsTerminal(in.Fd()) && os.Getenv("NORAW") == "" { oldState, err := term.SetRawTerminal(in.Fd()) if err != nil { return err } defer term.RestoreTerminal(in.Fd(), oldState) } go func() { io.Copy(rwc, in) if err := rwc.(*net.TCPConn).CloseWrite(); err != nil { fmt.Fprintf(errStream, "Couldn't send EOF: %s\n", err) } }() if err := <-errStdout; err != nil { return err } return nil }