... | ... |
@@ -451,14 +451,14 @@ func ListenAndServe(addr string, rtime *Runtime) error { |
451 | 451 |
|
452 | 452 |
fmt.Fprintln(file, "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n") |
453 | 453 |
if rtime.graph.LookupRemoteImage(name, rtime.authConfig) { |
454 |
- if err := rtime.graph.PullImage(file, name, rtime.authConfig); err != nil { |
|
455 |
- fmt.Fprintln(file, "Error: "+err.Error()) |
|
456 |
- } |
|
457 |
- return |
|
458 |
- } |
|
459 |
- if err := rtime.graph.PullRepository(file, name, "", rtime.repositories, rtime.authConfig); err != nil { |
|
460 |
- fmt.Fprintln(file, "Error: "+err.Error()) |
|
461 |
- } |
|
454 |
+ if err := rtime.graph.PullImage(file, name, rtime.authConfig); err != nil { |
|
455 |
+ fmt.Fprintln(file, "Error: "+err.Error()) |
|
456 |
+ } |
|
457 |
+ return |
|
458 |
+ } |
|
459 |
+ if err := rtime.graph.PullRepository(file, name, "", rtime.repositories, rtime.authConfig); err != nil { |
|
460 |
+ fmt.Fprintln(file, "Error: "+err.Error()) |
|
461 |
+ } |
|
462 | 462 |
}) |
463 | 463 |
|
464 | 464 |
r.Path("/containers").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
... | ... |
@@ -482,6 +482,18 @@ func ListenAndServe(addr string, rtime *Runtime) error { |
482 | 482 |
} |
483 | 483 |
defer file.Close() |
484 | 484 |
|
485 |
+ if config.Tty { |
|
486 |
+ oldState, err := SetRawTerminal() |
|
487 |
+ if err != nil { |
|
488 |
+ if os.Getenv("DEBUG") != "" { |
|
489 |
+ log.Printf("Can't set the terminal in raw mode: %s", err) |
|
490 |
+ } |
|
491 |
+ } else { |
|
492 |
+ defer RestoreTerminal(oldState) |
|
493 |
+ } |
|
494 |
+ |
|
495 |
+ } |
|
496 |
+ |
|
485 | 497 |
fmt.Fprintln(file, "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n") |
486 | 498 |
container, err := rtime.Create(&config) |
487 | 499 |
if err != nil { |
... | ... |
@@ -493,7 +505,7 @@ func ListenAndServe(addr string, rtime *Runtime) error { |
493 | 493 |
fmt.Fprintln(file, "Error: "+err.Error()) |
494 | 494 |
return |
495 | 495 |
} |
496 |
- } else if err := rtime.graph.PullRepository(file, config.Image, "", rtime.repositories, rtime.authConfig); err != nil { |
|
496 |
+ } else if err := rtime.graph.PullRepository(file, config.Image, "", rtime.repositories, rtime.authConfig); err != nil { |
|
497 | 497 |
fmt.Fprintln(file, "Error: "+err.Error()) |
498 | 498 |
return |
499 | 499 |
} |
... | ... |
@@ -523,7 +535,7 @@ func ListenAndServe(addr string, rtime *Runtime) error { |
523 | 523 |
cStdout = file |
524 | 524 |
} |
525 | 525 |
if config.AttachStderr { |
526 |
- cStderr = file // FIXME: rcli can't differentiate stdout from stderr |
|
526 |
+ cStderr = file // FIXME: api can't differentiate stdout from stderr |
|
527 | 527 |
} |
528 | 528 |
|
529 | 529 |
attachErr := container.Attach(cStdin, file, cStdout, cStderr) |
... | ... |
@@ -538,6 +550,12 @@ func ListenAndServe(addr string, rtime *Runtime) error { |
538 | 538 |
Debugf("Waiting for attach to return\n") |
539 | 539 |
<-attachErr |
540 | 540 |
// Expecting I/O pipe error, discarding |
541 |
+ |
|
542 |
+ // If we are in stdinonce mode, wait for the process to end |
|
543 |
+ // otherwise, simply return |
|
544 |
+ if config.StdinOnce && !config.Tty { |
|
545 |
+ container.Wait() |
|
546 |
+ } |
|
541 | 547 |
}) |
542 | 548 |
|
543 | 549 |
r.Path("/containers/{name:.*}/attach").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
... | ... |
@@ -5,6 +5,7 @@ import ( |
5 | 5 |
"encoding/json" |
6 | 6 |
"flag" |
7 | 7 |
"fmt" |
8 |
+ "github.com/dotcloud/docker/term" |
|
8 | 9 |
"io" |
9 | 10 |
"io/ioutil" |
10 | 11 |
"net" |
... | ... |
@@ -967,7 +968,7 @@ func call(method, path string) ([]byte, error) { |
967 | 967 |
|
968 | 968 |
} |
969 | 969 |
|
970 |
-func callStream(method, path string, data interface{}, isTerminal bool) error { |
|
970 |
+func callStream(method, path string, data interface{}, setRawTerminal bool) error { |
|
971 | 971 |
var body io.Reader |
972 | 972 |
if data != nil { |
973 | 973 |
buf, err := json.Marshal(data) |
... | ... |
@@ -996,6 +997,14 @@ func callStream(method, path string, data interface{}, isTerminal bool) error { |
996 | 996 |
rwc, _ := clientconn.Hijack() |
997 | 997 |
defer rwc.Close() |
998 | 998 |
|
999 |
+ if setRawTerminal && term.IsTerminal(int(os.Stdin.Fd())) && os.Getenv("NORAW") == "" { |
|
1000 |
+ if oldState, err := SetRawTerminal(); err != nil { |
|
1001 |
+ return err |
|
1002 |
+ } else { |
|
1003 |
+ defer RestoreTerminal(oldState) |
|
1004 |
+ } |
|
1005 |
+ } |
|
1006 |
+ |
|
999 | 1007 |
receiveStdout := Go(func() error { |
1000 | 1008 |
_, err := io.Copy(os.Stdout, rwc) |
1001 | 1009 |
return err |
... | ... |
@@ -1009,7 +1018,7 @@ func callStream(method, path string, data interface{}, isTerminal bool) error { |
1009 | 1009 |
if err := <-receiveStdout; err != nil { |
1010 | 1010 |
return err |
1011 | 1011 |
} |
1012 |
- if isTerminal { |
|
1012 |
+ if !term.IsTerminal(int(os.Stdin.Fd())) { |
|
1013 | 1013 |
if err := <-sendStdin; err != nil { |
1014 | 1014 |
return err |
1015 | 1015 |
} |
... | ... |
@@ -5,12 +5,14 @@ import ( |
5 | 5 |
"errors" |
6 | 6 |
"fmt" |
7 | 7 |
"github.com/dotcloud/docker/rcli" |
8 |
+ "github.com/dotcloud/docker/term" |
|
8 | 9 |
"index/suffixarray" |
9 | 10 |
"io" |
10 | 11 |
"io/ioutil" |
11 | 12 |
"net/http" |
12 | 13 |
"os" |
13 | 14 |
"os/exec" |
15 |
+ "os/signal" |
|
14 | 16 |
"path/filepath" |
15 | 17 |
"runtime" |
16 | 18 |
"strings" |
... | ... |
@@ -384,3 +386,22 @@ func CopyEscapable(dst io.Writer, src io.ReadCloser) (written int64, err error) |
384 | 384 |
} |
385 | 385 |
return written, err |
386 | 386 |
} |
387 |
+ |
|
388 |
+func SetRawTerminal() (*term.State, error) { |
|
389 |
+ oldState, err := term.MakeRaw(int(os.Stdin.Fd())) |
|
390 |
+ if err != nil { |
|
391 |
+ return nil, err |
|
392 |
+ } |
|
393 |
+ c := make(chan os.Signal, 1) |
|
394 |
+ signal.Notify(c, os.Interrupt) |
|
395 |
+ go func() { |
|
396 |
+ _ = <-c |
|
397 |
+ term.Restore(int(os.Stdin.Fd()), oldState) |
|
398 |
+ os.Exit(0) |
|
399 |
+ }() |
|
400 |
+ return oldState, err |
|
401 |
+} |
|
402 |
+ |
|
403 |
+func RestoreTerminal(state *term.State) { |
|
404 |
+ term.Restore(int(os.Stdin.Fd()), state) |
|
405 |
+} |