diff --git a/libcontainer/container_linux.go b/libcontainer/container_linux.go index e8e46e56e24..2e6c83dbea4 100644 --- a/libcontainer/container_linux.go +++ b/libcontainer/container_linux.go @@ -522,6 +522,8 @@ func (c *linuxContainer) newInitConfig(process *Process) *initConfig { cfg.Rlimits = process.Rlimits } cfg.CreateConsole = process.ConsoleSocket != nil + cfg.ConsoleWidth = process.ConsoleWidth + cfg.ConsoleHeight = process.ConsoleHeight return cfg } diff --git a/libcontainer/init_linux.go b/libcontainer/init_linux.go index 59c4e372873..344d239603c 100644 --- a/libcontainer/init_linux.go +++ b/libcontainer/init_linux.go @@ -62,6 +62,8 @@ type initConfig struct { ContainerId string `json:"containerid"` Rlimits []configs.Rlimit `json:"rlimits"` CreateConsole bool `json:"create_console"` + ConsoleWidth uint16 `json:"console_width"` + ConsoleHeight uint16 `json:"console_height"` Rootless bool `json:"rootless"` } @@ -171,12 +173,25 @@ func setupConsole(socket *os.File, config *initConfig, mount bool) error { // however, that setupUser (specifically fixStdioPermissions) *will* change // the UID owner of the console to be the user the process will run as (so // they can actually control their console). - console, slavePath, err := console.NewPty() + + pty, slavePath, err := console.NewPty() if err != nil { return err } + + if config.ConsoleHeight != 0 && config.ConsoleWidth != 0 { + err = pty.Resize(console.WinSize{ + Height: config.ConsoleHeight, + Width: config.ConsoleWidth, + }) + + if err != nil { + return err + } + } + // After we return from here, we don't need the console anymore. - defer console.Close() + defer pty.Close() // Mount the console inside our rootfs. if mount { @@ -185,7 +200,7 @@ func setupConsole(socket *os.File, config *initConfig, mount bool) error { } } // While we can access console.master, using the API is a good idea. - if err := utils.SendFd(socket, console.Name(), console.Fd()); err != nil { + if err := utils.SendFd(socket, pty.Name(), pty.Fd()); err != nil { return err } // Now, dup over all the things. diff --git a/libcontainer/process.go b/libcontainer/process.go index f1ad0814912..86bf7387f8c 100644 --- a/libcontainer/process.go +++ b/libcontainer/process.go @@ -47,6 +47,10 @@ type Process struct { // ExtraFiles specifies additional open files to be inherited by the container ExtraFiles []*os.File + // Initial sizings for the console + ConsoleWidth uint16 + ConsoleHeight uint16 + // Capabilities specify the capabilities to keep when executing the process inside the container // All capabilities not specified will be dropped from the processes capability mask Capabilities *configs.Capabilities diff --git a/tests/integration/tty.bats b/tests/integration/tty.bats index a4a0f190923..baad58d7aa9 100644 --- a/tests/integration/tty.bats +++ b/tests/integration/tty.bats @@ -116,3 +116,60 @@ function teardown() { [[ ${lines[0]} =~ 1000 ]] [[ ${lines[1]} =~ 5 ]] } + +@test "runc exec [tty consolesize]" { + # allow writing to filesystem + sed -i 's/"readonly": true/"readonly": false/' config.json + + # run busybox detached + runc run -d --console-socket $CONSOLE_SOCKET test_busybox + [ "$status" -eq 0 ] + + # make sure we're running + testcontainer test_busybox running + + tty_info_with_consize_size=$( cat < /tmp/tty-info" + ], + "cwd": "/" +} +EOF + ) + + # run the exec + runc exec --pid-file pid.txt -d --console-socket $CONSOLE_SOCKET -p <( echo $tty_info_with_consize_size ) test_busybox + [ "$status" -eq 0 ] + + # check the pid was generated + [ -e pid.txt ] + + #wait user process to finish + timeout 1 tail --pid=$(head -n 1 pid.txt) -f /dev/null + + tty_info=$( cat <