Add terminal support to do, exec and run commands

When a terminal is used, signals are now forwared to the foreground
process inside the sandbox. As a result, control commands like Ctrl+C
can be handled by the terminal in the guest.
This commit is contained in:
B. Blechschmidt
2023-02-27 01:21:16 +01:00
parent 34ff3ebe05
commit d5e1ed3cd9
7 changed files with 56 additions and 3 deletions
+7
View File
@@ -81,6 +81,9 @@ func StartSignalForwarding(handler func(linux.Signal)) func() {
//
// External real-time signals are not supported. We rely on the go-runtime
// for their handling.
//
// We do not forward some signals that are likely induced by the behavior
// of the forwarding process.
var sigchans []chan os.Signal
for sig := 1; sig <= numSignals+1; sig++ {
sigchan := make(chan os.Signal, 1)
@@ -94,6 +97,10 @@ func StartSignalForwarding(handler func(linux.Signal)) func() {
if sig == int(linux.SIGPIPE) {
continue
}
// SIGCHLD is received when a child of the forwarding process exits.
if sig == int(linux.SIGCHLD) {
continue
}
signal.Notify(sigchan, unix.Signal(sig))
}
// Start up our listener.
+3 -1
View File
@@ -33,6 +33,7 @@ import (
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/runsc/cmd/util"
"gvisor.dev/gvisor/runsc/config"
"gvisor.dev/gvisor/runsc/console"
"gvisor.dev/gvisor/runsc/container"
"gvisor.dev/gvisor/runsc/flag"
"gvisor.dev/gvisor/runsc/specutils"
@@ -167,6 +168,7 @@ func (c *Do) Execute(_ context.Context, f *flag.FlagSet, args ...any) subcommand
Args: f.Args(),
Env: os.Environ(),
Capabilities: specutils.AllCapabilities(),
Terminal: console.IsPty(os.Stdin.Fd()),
},
Hostname: hostname,
}
@@ -445,7 +447,7 @@ func startContainerAndWait(spec *specs.Spec, conf *config.Config, cid string, wa
//
// N.B. There is a still a window before this where a signal may kill
// this process, skipping cleanup.
stopForwarding := ct.ForwardSignals(0 /* pid */, false /* fgProcess */)
stopForwarding := ct.ForwardSignals(0 /* pid */, spec.Process.Terminal /* fgProcess */)
defer stopForwarding()
ws, err := ct.Wait()
+1 -1
View File
@@ -329,7 +329,7 @@ func (ex *Exec) argsFromCLI(argv []string, enableRaw bool) (*control.ExecArgs, e
KGID: ex.user.kgid,
ExtraKGIDs: extraKGIDs,
Capabilities: caps,
StdioIsPty: ex.consoleSocket != "",
StdioIsPty: ex.consoleSocket != "" || console.IsPty(os.Stdin.Fd()),
FilePayload: urpc.FilePayload{[]*os.File{os.Stdin, os.Stdout, os.Stderr}},
}, nil
}
+1
View File
@@ -6,6 +6,7 @@ go_library(
name = "console",
srcs = [
"console.go",
"pty_linux.go"
],
visibility = [
"//runsc:__subpackages__",
+26
View File
@@ -0,0 +1,26 @@
// Copyright 2023 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build linux
// +build linux
package console
import "golang.org/x/sys/unix"
// IsPty returns true if FD is a PTY.
func IsPty(fd uintptr) bool {
_, err := unix.IoctlGetTermios(int(fd), unix.TCGETS)
return err == nil
}
+14 -1
View File
@@ -521,6 +521,15 @@ func Run(conf *config.Config, args Args) (unix.WaitStatus, error) {
return 0, fmt.Errorf("starting container: %v", err)
}
}
// If we allocate a terminal, forward signals to the sandbox process.
// Otherwise, Ctrl+C will terminate this process and its children,
// including the terminal.
if c.Spec.Process.Terminal {
stopForwarding := c.ForwardSignals(0, true /* fgProcess */)
defer stopForwarding()
}
if args.Attached {
return c.Wait()
}
@@ -1059,7 +1068,11 @@ func (c *Container) createGoferProcess(spec *specs.Spec, conf *config.Config, bu
// Start with the general config flags.
cmd := exec.Command(specutils.ExePath, conf.ToFlags()...)
cmd.SysProcAttr = &unix.SysProcAttr{}
cmd.SysProcAttr = &unix.SysProcAttr{
// Detach from session. Otherwise, signals sent to the foreground process
// will also be forwarded by this process, resulting in duplicate signals.
Setsid: true,
}
// Set Args[0] to make easier to spot the gofer process. Otherwise it's
// shown as `exe`.
+4
View File
@@ -282,6 +282,10 @@ func MaybeRunAsRoot() error {
// Make sure child is killed when the parent terminates.
Pdeathsig: unix.SIGKILL,
// Detach from session. Otherwise, signals sent to the foreground process
// will also be forwarded by this process, resulting in duplicate signals.
Setsid: true,
}
cmd.Env = os.Environ()