Browse code

working tty

Victor Vieux authored on 2013/04/29 22:12:18
Showing 3 changed files
... ...
@@ -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
+}