*: console rewrite

This implements {createTTY, detach} and all of the combinations and
negations of the two that were previously implemented. There are some
valid questions about out-of-OCI-scope topics like !createTTY and how
things should be handled (why do we dup the current stdio to the
process, and how is that not a security issue). However, these will be
dealt with in a separate patchset.

In order to allow for late console setup, split setupRootfs into the
"preparation" section where all of the mounts are created and the
"finalize" section where we pivot_root and set things as ro. In between
the two we can set up all of the console mountpoints and symlinks we
need.

We use two-stage synchronisation to ensures that when the syscalls are
reordered in a suboptimal way, an out-of-place read() on the parentPipe
will not gobble the ancilliary information.

This patch is part of the console rewrite patchset.

Signed-off-by: Aleksa Sarai <asarai@suse.de>
This commit is contained in:
Aleksa Sarai
2016-06-04 01:29:34 +10:00
parent 4776b4326a
commit 244c9fc426
23 changed files with 325 additions and 220 deletions
+48 -35
View File
@@ -7,11 +7,26 @@ import (
"io"
"os"
"sync"
"syscall"
"github.com/docker/docker/pkg/term"
"github.com/opencontainers/runc/libcontainer"
)
type tty struct {
console libcontainer.Console
state *term.State
closers []io.Closer
postStart []io.Closer
wg sync.WaitGroup
}
func (t *tty) copyIO(w io.Writer, r io.ReadCloser) {
defer t.wg.Done()
io.Copy(w, r)
r.Close()
}
// setup standard pipes so that the TTY of the calling runc process
// is not inherited by the container.
func createStdioPipes(p *libcontainer.Process, rootuid, rootgid int) (*tty, error) {
@@ -46,45 +61,43 @@ func createStdioPipes(p *libcontainer.Process, rootuid, rootgid int) (*tty, erro
return t, nil
}
func (t *tty) copyIO(w io.Writer, r io.ReadCloser) {
defer t.wg.Done()
io.Copy(w, r)
r.Close()
}
func createTty(p *libcontainer.Process, rootuid, rootgid int, consolePath string) (*tty, error) {
if consolePath != "" {
if err := p.ConsoleFromPath(consolePath); err != nil {
return nil, err
func dupStdio(process *libcontainer.Process, rootuid, rootgid int) error {
process.Stdin = os.Stdin
process.Stdout = os.Stdout
process.Stderr = os.Stderr
for _, fd := range []uintptr{
os.Stdin.Fd(),
os.Stdout.Fd(),
os.Stderr.Fd(),
} {
if err := syscall.Fchown(int(fd), rootuid, rootgid); err != nil {
return err
}
return &tty{}, nil
}
console, err := p.NewConsole(rootuid, rootgid)
if err != nil {
return nil, err
}
go io.Copy(console, os.Stdin)
go io.Copy(os.Stdout, console)
state, err := term.SetRawTerminal(os.Stdin.Fd())
if err != nil {
return nil, fmt.Errorf("failed to set the terminal from the stdin: %v", err)
}
return &tty{
console: console,
state: state,
closers: []io.Closer{
console,
},
}, nil
return nil
}
type tty struct {
console libcontainer.Console
state *term.State
closers []io.Closer
postStart []io.Closer
wg sync.WaitGroup
func (t *tty) recvtty(process *libcontainer.Process, detach bool) error {
console, err := process.GetConsole()
if err != nil {
return err
}
if !detach {
go io.Copy(console, os.Stdin)
t.wg.Add(1)
go t.copyIO(os.Stdout, console)
state, err := term.SetRawTerminal(os.Stdin.Fd())
if err != nil {
return fmt.Errorf("failed to set the terminal from the stdin: %v", err)
}
t.state = state
}
t.console = console
t.closers = []io.Closer{console}
return nil
}
// ClosePostStart closes any fds that are provided to the container and dup2'd