1// +build linux
2
3package main
4
5import (
6	"fmt"
7	"io"
8	"os"
9	"sync"
10
11	"github.com/docker/docker/pkg/term"
12	"github.com/opencontainers/runc/libcontainer"
13)
14
15// setup standard pipes so that the TTY of the calling runc process
16// is not inherited by the container.
17func createStdioPipes(p *libcontainer.Process, rootuid int) (*tty, error) {
18	i, err := p.InitializeIO(rootuid)
19	if err != nil {
20		return nil, err
21	}
22	t := &tty{
23		closers: []io.Closer{
24			i.Stdin,
25			i.Stdout,
26			i.Stderr,
27		},
28	}
29	// add the process's io to the post start closers if they support close
30	for _, cc := range []interface{}{
31		p.Stdin,
32		p.Stdout,
33		p.Stderr,
34	} {
35		if c, ok := cc.(io.Closer); ok {
36			t.postStart = append(t.postStart, c)
37		}
38	}
39	go func() {
40		io.Copy(i.Stdin, os.Stdin)
41		i.Stdin.Close()
42	}()
43	t.wg.Add(2)
44	go t.copyIO(os.Stdout, i.Stdout)
45	go t.copyIO(os.Stderr, i.Stderr)
46	return t, nil
47}
48
49func (t *tty) copyIO(w io.Writer, r io.ReadCloser) {
50	defer t.wg.Done()
51	io.Copy(w, r)
52	r.Close()
53}
54
55func createTty(p *libcontainer.Process, rootuid int, consolePath string) (*tty, error) {
56	if consolePath != "" {
57		if err := p.ConsoleFromPath(consolePath); err != nil {
58			return nil, err
59		}
60		return &tty{}, nil
61	}
62	console, err := p.NewConsole(rootuid)
63	if err != nil {
64		return nil, err
65	}
66	go io.Copy(console, os.Stdin)
67	go io.Copy(os.Stdout, console)
68
69	state, err := term.SetRawTerminal(os.Stdin.Fd())
70	if err != nil {
71		return nil, fmt.Errorf("failed to set the terminal from the stdin: %v", err)
72	}
73	return &tty{
74		console: console,
75		state:   state,
76		closers: []io.Closer{
77			console,
78		},
79	}, nil
80}
81
82type tty struct {
83	console   libcontainer.Console
84	state     *term.State
85	closers   []io.Closer
86	postStart []io.Closer
87	wg        sync.WaitGroup
88}
89
90// ClosePostStart closes any fds that are provided to the container and dup2'd
91// so that we no longer have copy in our process.
92func (t *tty) ClosePostStart() error {
93	for _, c := range t.postStart {
94		c.Close()
95	}
96	return nil
97}
98
99// Close closes all open fds for the tty and/or restores the orignal
100// stdin state to what it was prior to the container execution
101func (t *tty) Close() error {
102	// ensure that our side of the fds are always closed
103	for _, c := range t.postStart {
104		c.Close()
105	}
106	// wait for the copy routines to finish before closing the fds
107	t.wg.Wait()
108	for _, c := range t.closers {
109		c.Close()
110	}
111	if t.state != nil {
112		term.RestoreTerminal(os.Stdin.Fd(), t.state)
113	}
114	return nil
115}
116
117func (t *tty) resize() error {
118	if t.console == nil {
119		return nil
120	}
121	ws, err := term.GetWinsize(os.Stdin.Fd())
122	if err != nil {
123		return err
124	}
125	return term.SetWinsize(t.console.Fd(), ws)
126}
127