Browse code

Make tty term exec driver specific

lxc is special in that we cannot create the master outside of the
container without opening the slave because we have nothing to provide to the
cmd. We have to open both then do the crazy setup on command right now instead of
passing the console path to lxc and telling it to open up that console. we save a couple of
openfiles in the native driver because we can do this.
Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@docker.com> (github: crosbymichael)

Michael Crosby authored on 2014/07/17 07:37:10
Showing 3 changed files
... ...
@@ -3,6 +3,7 @@ package lxc
3 3
 import (
4 4
 	"encoding/json"
5 5
 	"fmt"
6
+	"io"
6 7
 	"io/ioutil"
7 8
 	"log"
8 9
 	"os"
... ...
@@ -19,7 +20,9 @@ import (
19 19
 	"github.com/docker/libcontainer/label"
20 20
 	"github.com/docker/libcontainer/mount/nodes"
21 21
 	"github.com/dotcloud/docker/daemon/execdriver"
22
+	"github.com/dotcloud/docker/pkg/term"
22 23
 	"github.com/dotcloud/docker/utils"
24
+	"github.com/kr/pty"
23 25
 )
24 26
 
25 27
 const DriverName = "lxc"
... ...
@@ -78,10 +81,20 @@ func (d *driver) Name() string {
78 78
 }
79 79
 
80 80
 func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
81
-	if err := execdriver.SetTerminal(c, pipes); err != nil {
82
-		return -1, err
81
+	var (
82
+		term execdriver.Terminal
83
+		err  error
84
+	)
85
+
86
+	if c.Tty {
87
+		term, err = NewTtyConsole(c, pipes)
88
+	} else {
89
+		term, err = execdriver.NewStdConsole(c, pipes)
83 90
 	}
91
+	c.Terminal = term
92
+
84 93
 	c.Mounts = append(c.Mounts, execdriver.Mount{d.initPath, c.InitPath, false, true})
94
+
85 95
 	if err := d.generateEnvConfig(c); err != nil {
86 96
 		return -1, err
87 97
 	}
... ...
@@ -462,3 +475,74 @@ func (d *driver) generateEnvConfig(c *execdriver.Command) error {
462 462
 
463 463
 	return ioutil.WriteFile(p, data, 0600)
464 464
 }
465
+
466
+type TtyConsole struct {
467
+	MasterPty *os.File
468
+	SlavePty  *os.File
469
+}
470
+
471
+func NewTtyConsole(command *execdriver.Command, pipes *execdriver.Pipes) (*TtyConsole, error) {
472
+	// lxc is special in that we cannot create the master outside of the container without
473
+	// opening the slave because we have nothing to provide to the cmd.  We have to open both then do
474
+	// the crazy setup on command right now instead of passing the console path to lxc and telling it
475
+	// to open up that console.  we save a couple of openfiles in the native driver because we can do
476
+	// this.
477
+	ptyMaster, ptySlave, err := pty.Open()
478
+	if err != nil {
479
+		return nil, err
480
+	}
481
+
482
+	tty := &TtyConsole{
483
+		MasterPty: ptyMaster,
484
+		SlavePty:  ptySlave,
485
+	}
486
+
487
+	if err := tty.AttachPipes(&command.Cmd, pipes); err != nil {
488
+		tty.Close()
489
+		return nil, err
490
+	}
491
+
492
+	command.Console = tty.SlavePty.Name()
493
+
494
+	return tty, nil
495
+}
496
+
497
+func (t *TtyConsole) Master() *os.File {
498
+	return t.MasterPty
499
+}
500
+
501
+func (t *TtyConsole) Resize(h, w int) error {
502
+	return term.SetWinsize(t.MasterPty.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)})
503
+}
504
+
505
+func (t *TtyConsole) AttachPipes(command *exec.Cmd, pipes *execdriver.Pipes) error {
506
+	command.Stdout = t.SlavePty
507
+	command.Stderr = t.SlavePty
508
+
509
+	go func() {
510
+		if wb, ok := pipes.Stdout.(interface {
511
+			CloseWriters() error
512
+		}); ok {
513
+			defer wb.CloseWriters()
514
+		}
515
+
516
+		io.Copy(pipes.Stdout, t.MasterPty)
517
+	}()
518
+
519
+	if pipes.Stdin != nil {
520
+		command.Stdin = t.SlavePty
521
+		command.SysProcAttr.Setctty = true
522
+
523
+		go func() {
524
+			io.Copy(t.MasterPty, pipes.Stdin)
525
+
526
+			pipes.Stdin.Close()
527
+		}()
528
+	}
529
+	return nil
530
+}
531
+
532
+func (t *TtyConsole) Close() error {
533
+	t.SlavePty.Close()
534
+	return t.MasterPty.Close()
535
+}
... ...
@@ -5,6 +5,7 @@ package native
5 5
 import (
6 6
 	"encoding/json"
7 7
 	"fmt"
8
+	"io"
8 9
 	"io/ioutil"
9 10
 	"os"
10 11
 	"os/exec"
... ...
@@ -21,6 +22,7 @@ import (
21 21
 	"github.com/docker/libcontainer/syncpipe"
22 22
 	"github.com/dotcloud/docker/daemon/execdriver"
23 23
 	"github.com/dotcloud/docker/pkg/system"
24
+	"github.com/dotcloud/docker/pkg/term"
24 25
 )
25 26
 
26 27
 const (
... ...
@@ -96,9 +98,14 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
96 96
 		return -1, err
97 97
 	}
98 98
 
99
-	if err := execdriver.SetTerminal(c, pipes); err != nil {
100
-		return -1, err
99
+	var term execdriver.Terminal
100
+
101
+	if c.Tty {
102
+		term, err = NewTtyConsole(c, pipes)
103
+	} else {
104
+		term, err = execdriver.NewStdConsole(c, pipes)
101 105
 	}
106
+	c.Terminal = term
102 107
 
103 108
 	d.Lock()
104 109
 	d.activeContainers[c.ID] = &activeContainer{
... ...
@@ -272,3 +279,61 @@ func getEnv(key string, env []string) string {
272 272
 	}
273 273
 	return ""
274 274
 }
275
+
276
+type TtyConsole struct {
277
+	MasterPty *os.File
278
+}
279
+
280
+func NewTtyConsole(command *execdriver.Command, pipes *execdriver.Pipes) (*TtyConsole, error) {
281
+	ptyMaster, console, err := system.CreateMasterAndConsole()
282
+	if err != nil {
283
+		return nil, err
284
+	}
285
+
286
+	tty := &TtyConsole{
287
+		MasterPty: ptyMaster,
288
+	}
289
+
290
+	if err := tty.AttachPipes(&command.Cmd, pipes); err != nil {
291
+		tty.Close()
292
+		return nil, err
293
+	}
294
+
295
+	command.Console = console
296
+
297
+	return tty, nil
298
+}
299
+
300
+func (t *TtyConsole) Master() *os.File {
301
+	return t.MasterPty
302
+}
303
+
304
+func (t *TtyConsole) Resize(h, w int) error {
305
+	return term.SetWinsize(t.MasterPty.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)})
306
+}
307
+
308
+func (t *TtyConsole) AttachPipes(command *exec.Cmd, pipes *execdriver.Pipes) error {
309
+	go func() {
310
+		if wb, ok := pipes.Stdout.(interface {
311
+			CloseWriters() error
312
+		}); ok {
313
+			defer wb.CloseWriters()
314
+		}
315
+
316
+		io.Copy(pipes.Stdout, t.MasterPty)
317
+	}()
318
+
319
+	if pipes.Stdin != nil {
320
+		go func() {
321
+			io.Copy(t.MasterPty, pipes.Stdin)
322
+
323
+			pipes.Stdin.Close()
324
+		}()
325
+	}
326
+
327
+	return nil
328
+}
329
+
330
+func (t *TtyConsole) Close() error {
331
+	return t.MasterPty.Close()
332
+}
... ...
@@ -2,89 +2,9 @@ package execdriver
2 2
 
3 3
 import (
4 4
 	"io"
5
-	"os"
6 5
 	"os/exec"
7
-
8
-	"github.com/dotcloud/docker/pkg/system"
9
-	"github.com/dotcloud/docker/pkg/term"
10 6
 )
11 7
 
12
-func SetTerminal(command *Command, pipes *Pipes) error {
13
-	var (
14
-		term Terminal
15
-		err  error
16
-	)
17
-
18
-	if command.Tty {
19
-		term, err = NewTtyConsole(command, pipes)
20
-	} else {
21
-		term, err = NewStdConsole(command, pipes)
22
-	}
23
-
24
-	if err != nil {
25
-		return err
26
-	}
27
-
28
-	command.Terminal = term
29
-
30
-	return nil
31
-}
32
-
33
-type TtyConsole struct {
34
-	MasterPty *os.File
35
-}
36
-
37
-func NewTtyConsole(command *Command, pipes *Pipes) (*TtyConsole, error) {
38
-	ptyMaster, console, err := system.CreateMasterAndConsole()
39
-	if err != nil {
40
-		return nil, err
41
-	}
42
-
43
-	tty := &TtyConsole{
44
-		MasterPty: ptyMaster,
45
-	}
46
-
47
-	if err := tty.AttachPipes(&command.Cmd, pipes); err != nil {
48
-		tty.Close()
49
-		return nil, err
50
-	}
51
-	command.Console = console
52
-
53
-	return tty, nil
54
-}
55
-
56
-func (t *TtyConsole) Master() *os.File {
57
-	return t.MasterPty
58
-}
59
-
60
-func (t *TtyConsole) Resize(h, w int) error {
61
-	return term.SetWinsize(t.MasterPty.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)})
62
-}
63
-
64
-func (t *TtyConsole) AttachPipes(command *exec.Cmd, pipes *Pipes) error {
65
-	go func() {
66
-		if wb, ok := pipes.Stdout.(interface {
67
-			CloseWriters() error
68
-		}); ok {
69
-			defer wb.CloseWriters()
70
-		}
71
-		io.Copy(pipes.Stdout, t.MasterPty)
72
-	}()
73
-
74
-	if pipes.Stdin != nil {
75
-		go func() {
76
-			defer pipes.Stdin.Close()
77
-			io.Copy(t.MasterPty, pipes.Stdin)
78
-		}()
79
-	}
80
-
81
-	return nil
82
-}
83
-
84
-func (t *TtyConsole) Close() error {
85
-	return t.MasterPty.Close()
86
-}
87
-
88 8
 type StdConsole struct {
89 9
 }
90 10