Merge pull request #2535 from kolyshkin/no-term-on-stdin

Fix run/exec with terminal: true in case stdin is not a terminal
This commit is contained in:
Michael Crosby
2020-08-24 21:18:38 -04:00
committed by GitHub
3 changed files with 69 additions and 19 deletions
+20
View File
@@ -11,6 +11,14 @@ function teardown() {
teardown_busybox
}
@test "runc run [stdin not a tty]" {
# stty size fails without a tty
update_config '(.. | select(.[]? == "sh")) += ["-c", "stty size"]'
# note that stdout/stderr are already redirected by bats' run
runc run test_busybox < /dev/null
[ "$status" -eq 0 ]
}
@test "runc run [tty ptsname]" {
# Replace sh script with readlink.
# shellcheck disable=SC2016
@@ -61,6 +69,18 @@ function teardown() {
[[ ${lines[1]} =~ 5 ]]
}
@test "runc exec [stdin not a tty]" {
runc run -d --console-socket "$CONSOLE_SOCKET" test_busybox
[ "$status" -eq 0 ]
# make sure we're running
testcontainer test_busybox running
# note that stdout/stderr are already redirected by bats' run
runc exec -t test_busybox sh -c "stty size" < /dev/null
[ "$status" -eq 0 ]
}
@test "runc exec [tty ptsname]" {
# run busybox detached
runc run -d --console-socket "$CONSOLE_SOCKET" test_busybox
+46 -19
View File
@@ -12,16 +12,17 @@ import (
"github.com/containerd/console"
"github.com/opencontainers/runc/libcontainer"
"github.com/opencontainers/runc/libcontainer/utils"
"github.com/pkg/errors"
)
type tty struct {
epoller *console.Epoller
console *console.EpollConsole
stdin console.Console
closers []io.Closer
postStart []io.Closer
wg sync.WaitGroup
consoleC chan error
epoller *console.Epoller
console *console.EpollConsole
hostConsole console.Console
closers []io.Closer
postStart []io.Closer
wg sync.WaitGroup
consoleC chan error
}
func (t *tty) copyIO(w io.Writer, r io.ReadCloser) {
@@ -71,6 +72,37 @@ func inheritStdio(process *libcontainer.Process) error {
return nil
}
func (t *tty) initHostConsole() error {
// Usually all three (stdin, stdout, and stderr) streams are open to
// the terminal, but they might be redirected, so try them all.
for _, s := range []*os.File{os.Stderr, os.Stdout, os.Stdin} {
c, err := console.ConsoleFromFile(s)
switch err {
case nil:
t.hostConsole = c
return nil
case console.ErrNotAConsole:
continue
default:
// should not happen
return errors.Wrap(err, "unable to get console")
}
}
// If all streams are redirected, but we still have a controlling
// terminal, it can be obtained by opening /dev/tty.
tty, err := os.Open("/dev/tty")
if err != nil {
return err
}
c, err := console.ConsoleFromFile(tty)
if err != nil {
return errors.Wrap(err, "unable to get console")
}
t.hostConsole = c
return nil
}
func (t *tty) recvtty(process *libcontainer.Process, socket *os.File) (Err error) {
f, err := utils.RecvFd(socket)
if err != nil {
@@ -99,18 +131,13 @@ func (t *tty) recvtty(process *libcontainer.Process, socket *os.File) (Err error
t.wg.Add(1)
go t.copyIO(os.Stdout, epollConsole)
// set raw mode to stdin and also handle interrupt
stdin, err := console.ConsoleFromFile(os.Stdin)
if err != nil {
return err
}
if err := stdin.SetRaw(); err != nil {
// Set raw mode for the controlling terminal.
if err := t.hostConsole.SetRaw(); err != nil {
return fmt.Errorf("failed to set the terminal from the stdin: %v", err)
}
go handleInterrupt(stdin)
go handleInterrupt(t.hostConsole)
t.epoller = epoller
t.stdin = stdin
t.console = epollConsole
t.closers = []io.Closer{epollConsole}
return nil
@@ -156,15 +183,15 @@ func (t *tty) Close() error {
for _, c := range t.closers {
c.Close()
}
if t.stdin != nil {
t.stdin.Reset()
if t.hostConsole != nil {
t.hostConsole.Reset()
}
return nil
}
func (t *tty) resize() error {
if t.console == nil {
if t.console == nil || t.hostConsole == nil {
return nil
}
return t.console.ResizeFrom(console.Current())
return t.console.ResizeFrom(t.hostConsole)
}
+3
View File
@@ -157,6 +157,9 @@ func setupIO(process *libcontainer.Process, rootuid, rootgid int, createTTY, det
process.Stderr = nil
t := &tty{}
if !detach {
if err := t.initHostConsole(); err != nil {
return nil, err
}
parent, child, err := utils.NewSockPair("console")
if err != nil {
return nil, err