mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
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:
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ go_library(
|
||||
name = "console",
|
||||
srcs = [
|
||||
"console.go",
|
||||
"pty_linux.go"
|
||||
],
|
||||
visibility = [
|
||||
"//runsc:__subpackages__",
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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`.
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user