working tty

This commit is contained in:
Victor Vieux 2013-04-29 15:12:18 +02:00
parent 22f5e35579
commit e5104a4cb4
3 changed files with 60 additions and 12 deletions

38
api.go
View File

@ -451,14 +451,14 @@ func ListenAndServe(addr string, rtime *Runtime) error {
fmt.Fprintln(file, "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n")
if rtime.graph.LookupRemoteImage(name, rtime.authConfig) {
if err := rtime.graph.PullImage(file, name, rtime.authConfig); err != nil {
fmt.Fprintln(file, "Error: "+err.Error())
}
return
}
if err := rtime.graph.PullRepository(file, name, "", rtime.repositories, rtime.authConfig); err != nil {
fmt.Fprintln(file, "Error: "+err.Error())
}
if err := rtime.graph.PullImage(file, name, rtime.authConfig); err != nil {
fmt.Fprintln(file, "Error: "+err.Error())
}
return
}
if err := rtime.graph.PullRepository(file, name, "", rtime.repositories, rtime.authConfig); err != nil {
fmt.Fprintln(file, "Error: "+err.Error())
}
})
r.Path("/containers").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@ -482,6 +482,18 @@ func ListenAndServe(addr string, rtime *Runtime) error {
}
defer file.Close()
if config.Tty {
oldState, err := SetRawTerminal()
if err != nil {
if os.Getenv("DEBUG") != "" {
log.Printf("Can't set the terminal in raw mode: %s", err)
}
} else {
defer RestoreTerminal(oldState)
}
}
fmt.Fprintln(file, "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n")
container, err := rtime.Create(&config)
if err != nil {
@ -493,7 +505,7 @@ func ListenAndServe(addr string, rtime *Runtime) error {
fmt.Fprintln(file, "Error: "+err.Error())
return
}
} else if err := rtime.graph.PullRepository(file, config.Image, "", rtime.repositories, rtime.authConfig); err != nil {
} else if err := rtime.graph.PullRepository(file, config.Image, "", rtime.repositories, rtime.authConfig); err != nil {
fmt.Fprintln(file, "Error: "+err.Error())
return
}
@ -523,7 +535,7 @@ func ListenAndServe(addr string, rtime *Runtime) error {
cStdout = file
}
if config.AttachStderr {
cStderr = file // FIXME: rcli can't differentiate stdout from stderr
cStderr = file // FIXME: api can't differentiate stdout from stderr
}
attachErr := container.Attach(cStdin, file, cStdout, cStderr)
@ -538,6 +550,12 @@ func ListenAndServe(addr string, rtime *Runtime) error {
Debugf("Waiting for attach to return\n")
<-attachErr
// Expecting I/O pipe error, discarding
// If we are in stdinonce mode, wait for the process to end
// otherwise, simply return
if config.StdinOnce && !config.Tty {
container.Wait()
}
})
r.Path("/containers/{name:.*}/attach").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

View File

@ -5,6 +5,7 @@ import (
"encoding/json"
"flag"
"fmt"
"github.com/dotcloud/docker/term"
"io"
"io/ioutil"
"net"
@ -967,7 +968,7 @@ func call(method, path string) ([]byte, error) {
}
func callStream(method, path string, data interface{}, isTerminal bool) error {
func callStream(method, path string, data interface{}, setRawTerminal bool) error {
var body io.Reader
if data != nil {
buf, err := json.Marshal(data)
@ -996,6 +997,14 @@ func callStream(method, path string, data interface{}, isTerminal bool) error {
rwc, _ := clientconn.Hijack()
defer rwc.Close()
if setRawTerminal && term.IsTerminal(int(os.Stdin.Fd())) && os.Getenv("NORAW") == "" {
if oldState, err := SetRawTerminal(); err != nil {
return err
} else {
defer RestoreTerminal(oldState)
}
}
receiveStdout := Go(func() error {
_, err := io.Copy(os.Stdout, rwc)
return err
@ -1009,7 +1018,7 @@ func callStream(method, path string, data interface{}, isTerminal bool) error {
if err := <-receiveStdout; err != nil {
return err
}
if isTerminal {
if !term.IsTerminal(int(os.Stdin.Fd())) {
if err := <-sendStdin; err != nil {
return err
}

View File

@ -5,12 +5,14 @@ import (
"errors"
"fmt"
"github.com/dotcloud/docker/rcli"
"github.com/dotcloud/docker/term"
"index/suffixarray"
"io"
"io/ioutil"
"net/http"
"os"
"os/exec"
"os/signal"
"path/filepath"
"runtime"
"strings"
@ -384,3 +386,22 @@ func CopyEscapable(dst io.Writer, src io.ReadCloser) (written int64, err error)
}
return written, err
}
func SetRawTerminal() (*term.State, error) {
oldState, err := term.MakeRaw(int(os.Stdin.Fd()))
if err != nil {
return nil, err
}
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
go func() {
_ = <-c
term.Restore(int(os.Stdin.Fd()), oldState)
os.Exit(0)
}()
return oldState, err
}
func RestoreTerminal(state *term.State) {
term.Restore(int(os.Stdin.Fd()), state)
}