mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
runsc: Terminal support for "docker exec -ti".
This CL adds terminal support for "docker exec". We previously only supported consoles for the container process, but not exec processes. The SYS_IOCTL syscall was added to the default seccomp filter list, but only for ioctls that get/set winsize and termios structs. We need to allow these ioctl for all containers because it's possible to run "exec -ti" on a container that was started without an attached console, after the filters have been installed. Note that control-character signals are still not properly supported. Tested with: $ docker run --runtime=runsc -it alpine In another terminial: $ docker exec -it <containerid> /bin/sh PiperOrigin-RevId: 210185456 Change-Id: I6d2401e53a7697bb988c120a8961505c335f96d9
This commit is contained in:
committed by
Shentubot
parent
c48708a041
commit
106de2182d
@@ -21,8 +21,12 @@ const (
|
||||
TCGETS = 0x00005401
|
||||
TCSETS = 0x00005402
|
||||
TCSETSW = 0x00005403
|
||||
TIOCINQ = 0x0000541b
|
||||
TIOCGPGRP = 0x0000540f
|
||||
TIOCSPGRP = 0x00005410
|
||||
TIOCOUTQ = 0x00005411
|
||||
TIOCGWINSZ = 0x00005413
|
||||
TIOCSWINSZ = 0x00005414
|
||||
TIOCINQ = 0x0000541b
|
||||
FIONREAD = TIOCINQ
|
||||
FIONBIO = 0x00005421
|
||||
TIOCGPTN = 0x80045430
|
||||
|
||||
@@ -26,6 +26,14 @@ const (
|
||||
disabledChar = 0
|
||||
)
|
||||
|
||||
// Winsize is struct winsize, defined in uapi/asm-generic/termios.h.
|
||||
type Winsize struct {
|
||||
Row uint16
|
||||
Col uint16
|
||||
Xpixel uint16
|
||||
Ypixel uint16
|
||||
}
|
||||
|
||||
// Termios is struct termios, defined in uapi/asm-generic/termbits.h.
|
||||
type Termios struct {
|
||||
InputFlags uint32
|
||||
|
||||
@@ -19,7 +19,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"syscall"
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
@@ -73,6 +72,10 @@ type ExecArgs struct {
|
||||
// Capabilities is the list of capabilities to give to the process.
|
||||
Capabilities *auth.TaskCapabilities
|
||||
|
||||
// StdioIsPty indicates that FDs 0, 1, and 2 are connected to a host
|
||||
// pty fd.
|
||||
StdioIsPty bool
|
||||
|
||||
// FilePayload determines the files to give to the new process.
|
||||
urpc.FilePayload
|
||||
}
|
||||
@@ -108,17 +111,11 @@ func (proc *Proc) Exec(args *ExecArgs, waitStatus *uint32) error {
|
||||
mounter := fs.FileOwnerFromContext(ctx)
|
||||
|
||||
for appFD, f := range args.FilePayload.Files {
|
||||
// Copy the underlying FD.
|
||||
newFD, err := syscall.Dup(int(f.Fd()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.Close()
|
||||
enableIoctl := args.StdioIsPty && appFD <= 2
|
||||
|
||||
// Install the given file as an FD.
|
||||
file, err := host.NewFile(ctx, newFD, mounter)
|
||||
// Import the given file FD. This dups the FD as well.
|
||||
file, err := host.ImportFile(ctx, int(f.Fd()), mounter, enableIoctl)
|
||||
if err != nil {
|
||||
syscall.Close(newFD)
|
||||
return err
|
||||
}
|
||||
defer file.DecRef()
|
||||
|
||||
@@ -48,7 +48,6 @@ go_library(
|
||||
"//pkg/unet",
|
||||
"//pkg/waiter",
|
||||
"//pkg/waiter/fdnotifier",
|
||||
"@org_golang_x_sys//unix:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.googlesource.com/gvisor/pkg/abi/linux"
|
||||
"gvisor.googlesource.com/gvisor/pkg/fd"
|
||||
"gvisor.googlesource.com/gvisor/pkg/log"
|
||||
@@ -296,7 +295,7 @@ func (f *fileOperations) Ioctl(ctx context.Context, io usermem.IO, args arch.Sys
|
||||
fd := f.iops.fileState.FD()
|
||||
ioctl := args[1].Uint64()
|
||||
switch ioctl {
|
||||
case unix.TCGETS:
|
||||
case linux.TCGETS:
|
||||
termios, err := ioctlGetTermios(fd)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
@@ -306,7 +305,7 @@ func (f *fileOperations) Ioctl(ctx context.Context, io usermem.IO, args arch.Sys
|
||||
})
|
||||
return 0, err
|
||||
|
||||
case unix.TCSETS, unix.TCSETSW:
|
||||
case linux.TCSETS, linux.TCSETSW:
|
||||
var termios linux.Termios
|
||||
if _, err := usermem.CopyObjectIn(ctx, io, args[2].Pointer(), &termios, usermem.IOOpts{
|
||||
AddressSpaceActive: true,
|
||||
@@ -316,7 +315,7 @@ func (f *fileOperations) Ioctl(ctx context.Context, io usermem.IO, args arch.Sys
|
||||
err := ioctlSetTermios(fd, ioctl, &termios)
|
||||
return 0, err
|
||||
|
||||
case unix.TIOCGPGRP:
|
||||
case linux.TIOCGPGRP:
|
||||
// Args: pid_t *argp
|
||||
// When successful, equivalent to *argp = tcgetpgrp(fd).
|
||||
// Get the process group ID of the foreground process group on
|
||||
@@ -332,7 +331,7 @@ func (f *fileOperations) Ioctl(ctx context.Context, io usermem.IO, args arch.Sys
|
||||
})
|
||||
return 0, err
|
||||
|
||||
case unix.TIOCSPGRP:
|
||||
case linux.TIOCSPGRP:
|
||||
// Args: const pid_t *argp
|
||||
// Equivalent to tcsetpgrp(fd, *argp).
|
||||
// Set the foreground process group ID of this terminal.
|
||||
@@ -343,10 +342,10 @@ func (f *fileOperations) Ioctl(ctx context.Context, io usermem.IO, args arch.Sys
|
||||
log.Warningf("Ignoring application ioctl(TIOCSPGRP) call")
|
||||
return 0, nil
|
||||
|
||||
case unix.TIOCGWINSZ:
|
||||
case linux.TIOCGWINSZ:
|
||||
// Args: struct winsize *argp
|
||||
// Get window size.
|
||||
winsize, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ)
|
||||
winsize, err := ioctlGetWinsize(fd)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -355,16 +354,16 @@ func (f *fileOperations) Ioctl(ctx context.Context, io usermem.IO, args arch.Sys
|
||||
})
|
||||
return 0, err
|
||||
|
||||
case unix.TIOCSWINSZ:
|
||||
case linux.TIOCSWINSZ:
|
||||
// Args: const struct winsize *argp
|
||||
// Set window size.
|
||||
var winsize unix.Winsize
|
||||
var winsize linux.Winsize
|
||||
if _, err := usermem.CopyObjectIn(ctx, io, args[2].Pointer(), &winsize, usermem.IOOpts{
|
||||
AddressSpaceActive: true,
|
||||
}); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
err := unix.IoctlSetWinsize(fd, unix.TIOCSWINSZ, &winsize)
|
||||
err := ioctlSetWinsize(fd, &winsize)
|
||||
return 0, err
|
||||
|
||||
default:
|
||||
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
|
||||
func ioctlGetTermios(fd int) (*linux.Termios, error) {
|
||||
var t linux.Termios
|
||||
_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), syscall.TCGETS, uintptr(unsafe.Pointer(&t)))
|
||||
_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), linux.TCGETS, uintptr(unsafe.Pointer(&t)))
|
||||
if errno != 0 {
|
||||
return nil, errno
|
||||
}
|
||||
@@ -37,3 +37,20 @@ func ioctlSetTermios(fd int, req uint64, t *linux.Termios) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ioctlGetWinsize(fd int) (*linux.Winsize, error) {
|
||||
var w linux.Winsize
|
||||
_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), linux.TIOCGWINSZ, uintptr(unsafe.Pointer(&w)))
|
||||
if errno != 0 {
|
||||
return nil, errno
|
||||
}
|
||||
return &w, nil
|
||||
}
|
||||
|
||||
func ioctlSetWinsize(fd int, w *linux.Winsize) error {
|
||||
_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), linux.TIOCSWINSZ, uintptr(unsafe.Pointer(w)))
|
||||
if errno != 0 {
|
||||
return errno
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -227,7 +227,7 @@ func (cm *containerManager) Start(args *StartArgs, _ *struct{}) error {
|
||||
|
||||
// Execute runs a command on a created or running sandbox.
|
||||
func (cm *containerManager) Execute(e *control.ExecArgs, waitStatus *uint32) error {
|
||||
log.Debugf("containerManager.Execute")
|
||||
log.Debugf("containerManager.Execute: %+v", *e)
|
||||
proc := control.Proc{Kernel: cm.l.k}
|
||||
if err := proc.Exec(e, waitStatus); err != nil {
|
||||
return fmt.Errorf("error executing: %+v: %v", e, err)
|
||||
|
||||
@@ -18,6 +18,7 @@ go_library(
|
||||
"//runsc/boot:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
"//pkg/abi/linux",
|
||||
"//pkg/log",
|
||||
"//pkg/seccomp",
|
||||
"//pkg/sentry/platform",
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.googlesource.com/gvisor/pkg/abi/linux"
|
||||
"gvisor.googlesource.com/gvisor/pkg/seccomp"
|
||||
)
|
||||
|
||||
@@ -78,15 +79,36 @@ var allowedSyscalls = seccomp.SyscallRules{
|
||||
syscall.SYS_TGKILL: {},
|
||||
syscall.SYS_WRITE: {},
|
||||
syscall.SYS_WRITEV: {},
|
||||
}
|
||||
|
||||
// TODO: Ioctl is needed in order to support tty consoles.
|
||||
// Once filters support argument-checking, we should only allow ioctl
|
||||
// with tty-related arguments.
|
||||
func consoleFilters() seccomp.SyscallRules {
|
||||
return seccomp.SyscallRules{
|
||||
syscall.SYS_IOCTL: {},
|
||||
}
|
||||
// SYS_IOCTL is needed for terminal support, but we only allow
|
||||
// setting/getting termios and winsize.
|
||||
syscall.SYS_IOCTL: []seccomp.Rule{
|
||||
{
|
||||
seccomp.AllowAny{}, /* fd */
|
||||
seccomp.AllowValue(linux.TCGETS),
|
||||
seccomp.AllowAny{}, /* termios struct */
|
||||
},
|
||||
{
|
||||
seccomp.AllowAny{}, /* fd */
|
||||
seccomp.AllowValue(linux.TCSETS),
|
||||
seccomp.AllowAny{}, /* termios struct */
|
||||
},
|
||||
{
|
||||
seccomp.AllowAny{}, /* fd */
|
||||
seccomp.AllowValue(linux.TCSETSW),
|
||||
seccomp.AllowAny{}, /* termios struct */
|
||||
},
|
||||
{
|
||||
seccomp.AllowAny{}, /* fd */
|
||||
seccomp.AllowValue(linux.TIOCSWINSZ),
|
||||
seccomp.AllowAny{}, /* winsize struct */
|
||||
},
|
||||
{
|
||||
seccomp.AllowAny{}, /* fd */
|
||||
seccomp.AllowValue(linux.TIOCGWINSZ),
|
||||
seccomp.AllowAny{}, /* winsize struct */
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// whitelistFSFilters returns syscalls made by whitelistFS. Using WhitelistFS
|
||||
|
||||
@@ -28,7 +28,7 @@ import (
|
||||
)
|
||||
|
||||
// Install installs seccomp filters for based on the given platform.
|
||||
func Install(p platform.Platform, whitelistFS, console, hostNetwork bool) error {
|
||||
func Install(p platform.Platform, whitelistFS, hostNetwork bool) error {
|
||||
s := allowedSyscalls
|
||||
|
||||
// Set of additional filters used by -race and -msan. Returns empty
|
||||
@@ -39,10 +39,6 @@ func Install(p platform.Platform, whitelistFS, console, hostNetwork bool) error
|
||||
Report("direct file access allows unrestricted file access!")
|
||||
s.Merge(whitelistFSFilters())
|
||||
}
|
||||
if console {
|
||||
Report("console is enabled: syscall filters less restrictive!")
|
||||
s.Merge(consoleFilters())
|
||||
}
|
||||
if hostNetwork {
|
||||
Report("host networking enabled: syscall filters less restrictive!")
|
||||
s.Merge(hostInetFilters())
|
||||
|
||||
@@ -340,7 +340,7 @@ func (l *Loader) run() error {
|
||||
} else {
|
||||
whitelistFS := l.conf.FileAccess == FileAccessDirect
|
||||
hostNet := l.conf.Network == NetworkHost
|
||||
if err := filter.Install(l.k.Platform, whitelistFS, l.console, hostNet); err != nil {
|
||||
if err := filter.Install(l.k.Platform, whitelistFS, hostNet); err != nil {
|
||||
return fmt.Errorf("Failed to install seccomp filters: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ go_library(
|
||||
"//pkg/sentry/kernel/auth",
|
||||
"//pkg/urpc",
|
||||
"//runsc/boot",
|
||||
"//runsc/console",
|
||||
"//runsc/container",
|
||||
"//runsc/fsgofer",
|
||||
"//runsc/specutils",
|
||||
|
||||
+36
-3
@@ -35,6 +35,7 @@ import (
|
||||
"gvisor.googlesource.com/gvisor/pkg/sentry/kernel/auth"
|
||||
"gvisor.googlesource.com/gvisor/pkg/urpc"
|
||||
"gvisor.googlesource.com/gvisor/runsc/boot"
|
||||
"gvisor.googlesource.com/gvisor/runsc/console"
|
||||
"gvisor.googlesource.com/gvisor/runsc/container"
|
||||
"gvisor.googlesource.com/gvisor/runsc/specutils"
|
||||
)
|
||||
@@ -50,6 +51,11 @@ type Exec struct {
|
||||
detach bool
|
||||
processPath string
|
||||
pidFile string
|
||||
|
||||
// consoleSocket is the path to an AF_UNIX socket which will receive a
|
||||
// file descriptor referencing the master end of the console's
|
||||
// pseudoterminal.
|
||||
consoleSocket string
|
||||
}
|
||||
|
||||
// Name implements subcommands.Command.Name.
|
||||
@@ -91,6 +97,7 @@ func (ex *Exec) SetFlags(f *flag.FlagSet) {
|
||||
f.BoolVar(&ex.detach, "detach", false, "detach from the container's process")
|
||||
f.StringVar(&ex.processPath, "process", "", "path to the process.json")
|
||||
f.StringVar(&ex.pidFile, "pid-file", "", "filename that the container pid will be written to")
|
||||
f.StringVar(&ex.consoleSocket, "console-socket", "", "path to an AF_UNIX socket which will receive a file descriptor referencing the master end of the console's pseudoterminal")
|
||||
}
|
||||
|
||||
// Execute implements subcommands.Command.Execute. It starts a process in an
|
||||
@@ -178,11 +185,35 @@ func (ex *Exec) execAndWait(waitStatus *syscall.WaitStatus) subcommands.ExitStat
|
||||
args = append(args, a)
|
||||
}
|
||||
}
|
||||
|
||||
cmd := exec.Command(binPath, args...)
|
||||
|
||||
// Exec stdio defaults to current process stdio.
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
// If the console control socket file is provided, then create a new
|
||||
// pty master/slave pair and set the tty on the sandbox process.
|
||||
if ex.consoleSocket != "" {
|
||||
// Create a new tty pair and send the master on the provided
|
||||
// socket.
|
||||
tty, err := console.NewWithSocket(ex.consoleSocket)
|
||||
if err != nil {
|
||||
Fatalf("error setting up console with socket %q: %v", ex.consoleSocket, err)
|
||||
}
|
||||
defer tty.Close()
|
||||
|
||||
// Set stdio to the new tty slave.
|
||||
cmd.Stdin = tty
|
||||
cmd.Stdout = tty
|
||||
cmd.Stderr = tty
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
Setsid: true,
|
||||
Setctty: true,
|
||||
Ctty: int(tty.Fd()),
|
||||
}
|
||||
}
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
Fatalf("failure to start child exec process, err: %v", err)
|
||||
}
|
||||
@@ -252,11 +283,12 @@ func (ex *Exec) argsFromCLI(argv []string) (*control.ExecArgs, error) {
|
||||
return &control.ExecArgs{
|
||||
Argv: argv,
|
||||
WorkingDirectory: ex.cwd,
|
||||
FilePayload: urpc.FilePayload{Files: []*os.File{os.Stdin, os.Stdout, os.Stderr}},
|
||||
KUID: ex.user.kuid,
|
||||
KGID: ex.user.kgid,
|
||||
ExtraKGIDs: extraKGIDs,
|
||||
Capabilities: caps,
|
||||
StdioIsPty: ex.consoleSocket != "",
|
||||
FilePayload: urpc.FilePayload{[]*os.File{os.Stdin, os.Stdout, os.Stderr}},
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -292,11 +324,12 @@ func argsFromProcess(p *specs.Process) (*control.ExecArgs, error) {
|
||||
Argv: p.Args,
|
||||
Envv: p.Env,
|
||||
WorkingDirectory: p.Cwd,
|
||||
FilePayload: urpc.FilePayload{Files: []*os.File{os.Stdin, os.Stdout, os.Stderr}},
|
||||
KUID: auth.KUID(p.User.UID),
|
||||
KGID: auth.KGID(p.User.GID),
|
||||
ExtraKGIDs: extraKGIDs,
|
||||
Capabilities: caps,
|
||||
StdioIsPty: p.Terminal,
|
||||
FilePayload: urpc.FilePayload{Files: []*os.File{os.Stdin, os.Stdout, os.Stderr}},
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package(licenses = ["notice"]) # Apache 2.0
|
||||
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "console",
|
||||
srcs = ["console.go"],
|
||||
importpath = "gvisor.googlesource.com/gvisor/runsc/console",
|
||||
visibility = [
|
||||
"//runsc:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
"@com_github_kr_pty//:go_default_library",
|
||||
"@org_golang_x_sys//unix:go_default_library",
|
||||
],
|
||||
)
|
||||
@@ -12,7 +12,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package sandbox
|
||||
// Package console contains utilities for working with pty consols in runsc.
|
||||
package console
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -23,9 +24,9 @@ import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// setupConsole creates pty master/slave pair, sends the master FD over the
|
||||
// given socket, and returns the slave.
|
||||
func setupConsole(socketPath string) (*os.File, error) {
|
||||
// NewWithSocket creates pty master/slave pair, sends the master FD over the given
|
||||
// socket, and returns the slave.
|
||||
func NewWithSocket(socketPath string) (*os.File, error) {
|
||||
// Create a new pty master and slave.
|
||||
ptyMaster, ptySlave, err := pty.Open()
|
||||
if err != nil {
|
||||
+1
-2
@@ -5,7 +5,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
go_library(
|
||||
name = "sandbox",
|
||||
srcs = [
|
||||
"console.go",
|
||||
"namespace.go",
|
||||
"network.go",
|
||||
"sandbox.go",
|
||||
@@ -21,9 +20,9 @@ go_library(
|
||||
"//pkg/sentry/control",
|
||||
"//pkg/urpc",
|
||||
"//runsc/boot",
|
||||
"//runsc/console",
|
||||
"//runsc/fsgofer",
|
||||
"//runsc/specutils",
|
||||
"@com_github_kr_pty//:go_default_library",
|
||||
"@com_github_opencontainers_runtime-spec//specs-go:go_default_library",
|
||||
"@com_github_vishvananda_netlink//:go_default_library",
|
||||
"@org_golang_x_sys//unix:go_default_library",
|
||||
|
||||
@@ -31,6 +31,7 @@ import (
|
||||
"gvisor.googlesource.com/gvisor/pkg/sentry/control"
|
||||
"gvisor.googlesource.com/gvisor/pkg/urpc"
|
||||
"gvisor.googlesource.com/gvisor/runsc/boot"
|
||||
"gvisor.googlesource.com/gvisor/runsc/console"
|
||||
"gvisor.googlesource.com/gvisor/runsc/fsgofer"
|
||||
"gvisor.googlesource.com/gvisor/runsc/specutils"
|
||||
)
|
||||
@@ -392,7 +393,7 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund
|
||||
"boot",
|
||||
"--bundle", bundleDir,
|
||||
"--controller-fd="+strconv.Itoa(nextFD),
|
||||
fmt.Sprintf("--console=%t", consoleEnabled))
|
||||
"--console="+strconv.FormatBool(consoleEnabled))
|
||||
nextFD++
|
||||
|
||||
controllerFile := os.NewFile(uintptr(fd), "control_server_socket")
|
||||
@@ -407,14 +408,19 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund
|
||||
nextFD++
|
||||
}
|
||||
|
||||
// Sandbox stdio defaults to current process stdio.
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
// If the console control socket file is provided, then create a new
|
||||
// pty master/slave pair and set the tty on the sandbox process.
|
||||
if consoleEnabled {
|
||||
// setupConsole will send the master on the socket, and return
|
||||
// the slave.
|
||||
tty, err := setupConsole(consoleSocket)
|
||||
// console.NewWithSocket will send the master on the socket,
|
||||
// and return the slave.
|
||||
tty, err := console.NewWithSocket(consoleSocket)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error setting up control socket %q: %v", consoleSocket, err)
|
||||
return fmt.Errorf("error setting up console with socket %q: %v", consoleSocket, err)
|
||||
}
|
||||
defer tty.Close()
|
||||
|
||||
@@ -423,10 +429,6 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund
|
||||
cmd.Stderr = tty
|
||||
cmd.SysProcAttr.Setctty = true
|
||||
cmd.SysProcAttr.Ctty = int(tty.Fd())
|
||||
} else {
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
}
|
||||
|
||||
// Detach from this session, otherwise cmd will get SIGHUP and SIGCONT
|
||||
|
||||
Reference in New Issue
Block a user