summaryrefslogtreecommitdiffhomepage
path: root/pkg
diff options
context:
space:
mode:
authorNicolas Lacasse <nlacasse@google.com>2018-10-17 12:27:58 -0700
committerShentubot <shentubot@google.com>2018-10-17 12:29:05 -0700
commit4e6f0892c96c374b1abcf5c39b75ba52d98c97f8 (patch)
treecb21538ad26a50ff61086d55c1bef36d5026e4c0 /pkg
parent578fe5a50dcf8e104b6bce3802987b0f8c069ade (diff)
runsc: Support job control signals for the root container.
Now containers run with "docker run -it" support control characters like ^C and ^Z. This required refactoring our signal handling a bit. Signals delivered to the "runsc boot" process are turned into loader.Signal calls with the appropriate delivery mode. Previously they were always sent directly to PID 1. PiperOrigin-RevId: 217566770 Change-Id: I5b7220d9a0f2b591a56335479454a200c6de8732
Diffstat (limited to 'pkg')
-rw-r--r--pkg/sentry/kernel/kernel.go27
-rw-r--r--pkg/sentry/sighandling/BUILD6
-rw-r--r--pkg/sentry/sighandling/sighandling.go25
3 files changed, 36 insertions, 22 deletions
diff --git a/pkg/sentry/kernel/kernel.go b/pkg/sentry/kernel/kernel.go
index cc664deec..84afdb530 100644
--- a/pkg/sentry/kernel/kernel.go
+++ b/pkg/sentry/kernel/kernel.go
@@ -839,17 +839,40 @@ func (k *Kernel) SendContainerSignal(cid string, info *arch.SignalInfo) error {
k.tasks.mu.RLock()
defer k.tasks.mu.RUnlock()
+ var lastErr error
for t := range k.tasks.Root.tids {
if t == t.tg.leader && t.ContainerID() == cid {
t.tg.signalHandlers.mu.Lock()
defer t.tg.signalHandlers.mu.Unlock()
infoCopy := *info
if err := t.sendSignalLocked(&infoCopy, true /*group*/); err != nil {
- return err
+ lastErr = err
}
}
}
- return nil
+ return lastErr
+}
+
+// SendProcessGroupSignal sends a signal to all processes inside the process
+// group. It is analagous to kernel/signal.c:kill_pgrp.
+func (k *Kernel) SendProcessGroupSignal(pg *ProcessGroup, info *arch.SignalInfo) error {
+ k.extMu.Lock()
+ defer k.extMu.Unlock()
+ k.tasks.mu.RLock()
+ defer k.tasks.mu.RUnlock()
+
+ var lastErr error
+ for t := range k.tasks.Root.tids {
+ if t == t.tg.leader && t.tg.ProcessGroup() == pg {
+ t.tg.signalHandlers.mu.Lock()
+ defer t.tg.signalHandlers.mu.Unlock()
+ infoCopy := *info
+ if err := t.sendSignalLocked(&infoCopy, true /*group*/); err != nil {
+ lastErr = err
+ }
+ }
+ }
+ return lastErr
}
// FeatureSet returns the FeatureSet.
diff --git a/pkg/sentry/sighandling/BUILD b/pkg/sentry/sighandling/BUILD
index f480f0735..751176747 100644
--- a/pkg/sentry/sighandling/BUILD
+++ b/pkg/sentry/sighandling/BUILD
@@ -10,9 +10,5 @@ go_library(
],
importpath = "gvisor.googlesource.com/gvisor/pkg/sentry/sighandling",
visibility = ["//pkg/sentry:internal"],
- deps = [
- "//pkg/abi/linux",
- "//pkg/sentry/arch",
- "//pkg/sentry/kernel",
- ],
+ deps = ["//pkg/abi/linux"],
)
diff --git a/pkg/sentry/sighandling/sighandling.go b/pkg/sentry/sighandling/sighandling.go
index 0946ab075..29bcf55ab 100644
--- a/pkg/sentry/sighandling/sighandling.go
+++ b/pkg/sentry/sighandling/sighandling.go
@@ -23,18 +23,17 @@ import (
"syscall"
"gvisor.googlesource.com/gvisor/pkg/abi/linux"
- "gvisor.googlesource.com/gvisor/pkg/sentry/arch"
- "gvisor.googlesource.com/gvisor/pkg/sentry/kernel"
)
// numSignals is the number of normal (non-realtime) signals on Linux.
const numSignals = 32
-// forwardSignals listens for incoming signals and delivers them to k.
+// handleSignals listens for incoming signals and calls the given handler
+// function.
//
// It starts when the start channel is closed, stops when the stop channel
// is closed, and closes done once it will no longer deliver signals to k.
-func forwardSignals(k *kernel.Kernel, sigchans []chan os.Signal, start, stop, done chan struct{}) {
+func handleSignals(sigchans []chan os.Signal, handler func(linux.Signal), start, stop, done chan struct{}) {
// Build a select case.
sc := []reflect.SelectCase{{Dir: reflect.SelectRecv, Chan: reflect.ValueOf(start)}}
for _, sigchan := range sigchans {
@@ -98,18 +97,19 @@ func forwardSignals(k *kernel.Kernel, sigchans []chan os.Signal, start, stop, do
}
}
- k.SendExternalSignal(&arch.SignalInfo{Signo: int32(signal)}, "sentry")
+ // Pass the signal to the handler.
+ handler(signal)
}
}
-// PrepareForwarding ensures that synchronous signals are forwarded to k and
-// returns a callback that starts signal delivery, which itself returns a
-// callback that stops signal forwarding.
+// PrepareHandler ensures that synchronous signals are passed to the given
+// handler function and returns a callback that starts signal delivery, which
+// itself returns a callback that stops signal handling.
//
// Note that this function permanently takes over signal handling. After the
// stop callback, signals revert to the default Go runtime behavior, which
// cannot be overridden with external calls to signal.Notify.
-func PrepareForwarding(k *kernel.Kernel, skipSignal syscall.Signal) func() func() {
+func PrepareHandler(handler func(linux.Signal)) func() func() {
start := make(chan struct{})
stop := make(chan struct{})
done := make(chan struct{})
@@ -125,15 +125,10 @@ func PrepareForwarding(k *kernel.Kernel, skipSignal syscall.Signal) func() func(
for sig := 1; sig <= numSignals+1; sig++ {
sigchan := make(chan os.Signal, 1)
sigchans = append(sigchans, sigchan)
-
- if syscall.Signal(sig) == skipSignal {
- continue
- }
-
signal.Notify(sigchan, syscall.Signal(sig))
}
// Start up our listener.
- go forwardSignals(k, sigchans, start, stop, done) // S/R-SAFE: synchronized by Kernel.extMu.
+ go handleSignals(sigchans, handler, start, stop, done) // S/R-SAFE: synchronized by Kernel.extMu.
return func() func() {
close(start)