mirror of
https://github.com/opencontainers/runc.git
synced 2026-07-11 06:03:57 +08:00
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:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user