Skip to content

Commit

Permalink
support forwarding signals
Browse files Browse the repository at this point in the history
  • Loading branch information
AkihiroSuda committed Mar 15, 2020
1 parent 05d72a2 commit bbccf1e
Show file tree
Hide file tree
Showing 7 changed files with 295 additions and 1 deletion.
13 changes: 12 additions & 1 deletion pkg/child/child.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package child

import (
"context"
"io/ioutil"
"os"
"os/exec"
Expand All @@ -18,6 +19,8 @@ import (
"github.com/rootless-containers/rootlesskit/pkg/msgutil"
"github.com/rootless-containers/rootlesskit/pkg/network"
"github.com/rootless-containers/rootlesskit/pkg/port"
"github.com/rootless-containers/rootlesskit/pkg/sigproxy"
sigproxysignal "github.com/rootless-containers/rootlesskit/pkg/sigproxy/signal"
)

var propagationStates = map[string]uintptr{
Expand Down Expand Up @@ -256,7 +259,12 @@ func Child(opt Opt) error {
return errors.Wrapf(err, "command %v exited", opt.TargetCmd)
}
} else {
if err := cmd.Run(); err != nil {
if err := cmd.Start(); err != nil {
return errors.Wrapf(err, "command %v exited", opt.TargetCmd)
}
sigc := sigproxy.ForwardAllSignals(context.TODO(), cmd.Process.Pid)
defer sigproxysignal.StopCatch(sigc)
if err := cmd.Wait(); err != nil {
return errors.Wrapf(err, "command %v exited", opt.TargetCmd)
}
}
Expand All @@ -283,6 +291,9 @@ func runAndReap(cmd *exec.Cmd) error {
if err := cmd.Start(); err != nil {
return err
}
sigc := sigproxy.ForwardAllSignals(context.TODO(), cmd.Process.Pid)
defer sigproxysignal.StopCatch(sigc)

result := make(chan error)
go func() {
defer close(result)
Expand Down
5 changes: 5 additions & 0 deletions pkg/parent/parent.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import (
"github.com/rootless-containers/rootlesskit/pkg/network"
"github.com/rootless-containers/rootlesskit/pkg/parent/idtools"
"github.com/rootless-containers/rootlesskit/pkg/port"
"github.com/rootless-containers/rootlesskit/pkg/sigproxy"
"github.com/rootless-containers/rootlesskit/pkg/sigproxy/signal"
)

type Opt struct {
Expand Down Expand Up @@ -131,6 +133,9 @@ func Parent(opt Opt) error {
if err := setupUIDGIDMap(cmd.Process.Pid); err != nil {
return errors.Wrap(err, "failed to setup UID/GID map")
}
sigc := sigproxy.ForwardAllSignals(context.TODO(), cmd.Process.Pid)
defer signal.StopCatch(sigc)

// send message 0
msg := common.Message{
Stage: 0,
Expand Down
56 changes: 56 additions & 0 deletions pkg/sigproxy/signal/signal.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Package signal provides helper functions for dealing with signals across
// various operating systems.
//
// Forked from /~https://github.com/moby/moby/tree/37defbfd9b968f38e8e15dfa5f06d9f878bd65ba/pkg/signal
package signal

import (
"fmt"
"os"
"os/signal"
"strconv"
"strings"
"syscall"
)

// CatchAll catches all signals and relays them to the specified channel.
func CatchAll(sigc chan os.Signal) {
var handledSigs []os.Signal
for _, s := range SignalMap {
handledSigs = append(handledSigs, s)
}
signal.Notify(sigc, handledSigs...)
}

// StopCatch stops catching the signals and closes the specified channel.
func StopCatch(sigc chan os.Signal) {
signal.Stop(sigc)
close(sigc)
}

// ParseSignal translates a string to a valid syscall signal.
// It returns an error if the signal map doesn't include the given signal.
func ParseSignal(rawSignal string) (syscall.Signal, error) {
s, err := strconv.Atoi(rawSignal)
if err == nil {
if s == 0 {
return -1, fmt.Errorf("Invalid signal: %s", rawSignal)
}
return syscall.Signal(s), nil
}
signal, ok := SignalMap[strings.TrimPrefix(strings.ToUpper(rawSignal), "SIG")]
if !ok {
return -1, fmt.Errorf("Invalid signal: %s", rawSignal)
}
return signal, nil
}

// ValidSignalForPlatform returns true if a signal is valid on the platform
func ValidSignalForPlatform(sig syscall.Signal) bool {
for _, v := range SignalMap {
if v == sig {
return true
}
}
return false
}
83 changes: 83 additions & 0 deletions pkg/sigproxy/signal/signal_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// +build !mips,!mipsle,!mips64,!mips64le

package signal

import (
"syscall"

"golang.org/x/sys/unix"
)

const (
sigrtmin = 34
sigrtmax = 64
)

// SignalMap is a map of Linux signals.
var SignalMap = map[string]syscall.Signal{
"ABRT": unix.SIGABRT,
"ALRM": unix.SIGALRM,
"BUS": unix.SIGBUS,
"CHLD": unix.SIGCHLD,
"CLD": unix.SIGCLD,
"CONT": unix.SIGCONT,
"FPE": unix.SIGFPE,
"HUP": unix.SIGHUP,
"ILL": unix.SIGILL,
"INT": unix.SIGINT,
"IO": unix.SIGIO,
"IOT": unix.SIGIOT,
"KILL": unix.SIGKILL,
"PIPE": unix.SIGPIPE,
"POLL": unix.SIGPOLL,
"PROF": unix.SIGPROF,
"PWR": unix.SIGPWR,
"QUIT": unix.SIGQUIT,
"SEGV": unix.SIGSEGV,
"STKFLT": unix.SIGSTKFLT,
"STOP": unix.SIGSTOP,
"SYS": unix.SIGSYS,
"TERM": unix.SIGTERM,
"TRAP": unix.SIGTRAP,
"TSTP": unix.SIGTSTP,
"TTIN": unix.SIGTTIN,
"TTOU": unix.SIGTTOU,
"URG": unix.SIGURG,
"USR1": unix.SIGUSR1,
"USR2": unix.SIGUSR2,
"VTALRM": unix.SIGVTALRM,
"WINCH": unix.SIGWINCH,
"XCPU": unix.SIGXCPU,
"XFSZ": unix.SIGXFSZ,
"RTMIN": sigrtmin,
"RTMIN+1": sigrtmin + 1,
"RTMIN+2": sigrtmin + 2,
"RTMIN+3": sigrtmin + 3,
"RTMIN+4": sigrtmin + 4,
"RTMIN+5": sigrtmin + 5,
"RTMIN+6": sigrtmin + 6,
"RTMIN+7": sigrtmin + 7,
"RTMIN+8": sigrtmin + 8,
"RTMIN+9": sigrtmin + 9,
"RTMIN+10": sigrtmin + 10,
"RTMIN+11": sigrtmin + 11,
"RTMIN+12": sigrtmin + 12,
"RTMIN+13": sigrtmin + 13,
"RTMIN+14": sigrtmin + 14,
"RTMIN+15": sigrtmin + 15,
"RTMAX-14": sigrtmax - 14,
"RTMAX-13": sigrtmax - 13,
"RTMAX-12": sigrtmax - 12,
"RTMAX-11": sigrtmax - 11,
"RTMAX-10": sigrtmax - 10,
"RTMAX-9": sigrtmax - 9,
"RTMAX-8": sigrtmax - 8,
"RTMAX-7": sigrtmax - 7,
"RTMAX-6": sigrtmax - 6,
"RTMAX-5": sigrtmax - 5,
"RTMAX-4": sigrtmax - 4,
"RTMAX-3": sigrtmax - 3,
"RTMAX-2": sigrtmax - 2,
"RTMAX-1": sigrtmax - 1,
"RTMAX": sigrtmax,
}
84 changes: 84 additions & 0 deletions pkg/sigproxy/signal/signal_linux_mipsx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// +build linux
// +build mips mipsle mips64 mips64le

package signal

import (
"syscall"

"golang.org/x/sys/unix"
)

const (
sigrtmin = 34
sigrtmax = 127
)

// SignalMap is a map of Linux signals.
var SignalMap = map[string]syscall.Signal{
"ABRT": unix.SIGABRT,
"ALRM": unix.SIGALRM,
"BUS": unix.SIGBUS,
"CHLD": unix.SIGCHLD,
"CLD": unix.SIGCLD,
"CONT": unix.SIGCONT,
"FPE": unix.SIGFPE,
"HUP": unix.SIGHUP,
"ILL": unix.SIGILL,
"INT": unix.SIGINT,
"IO": unix.SIGIO,
"IOT": unix.SIGIOT,
"KILL": unix.SIGKILL,
"PIPE": unix.SIGPIPE,
"POLL": unix.SIGPOLL,
"PROF": unix.SIGPROF,
"PWR": unix.SIGPWR,
"QUIT": unix.SIGQUIT,
"SEGV": unix.SIGSEGV,
"EMT": unix.SIGEMT,
"STOP": unix.SIGSTOP,
"SYS": unix.SIGSYS,
"TERM": unix.SIGTERM,
"TRAP": unix.SIGTRAP,
"TSTP": unix.SIGTSTP,
"TTIN": unix.SIGTTIN,
"TTOU": unix.SIGTTOU,
"URG": unix.SIGURG,
"USR1": unix.SIGUSR1,
"USR2": unix.SIGUSR2,
"VTALRM": unix.SIGVTALRM,
"WINCH": unix.SIGWINCH,
"XCPU": unix.SIGXCPU,
"XFSZ": unix.SIGXFSZ,
"RTMIN": sigrtmin,
"RTMIN+1": sigrtmin + 1,
"RTMIN+2": sigrtmin + 2,
"RTMIN+3": sigrtmin + 3,
"RTMIN+4": sigrtmin + 4,
"RTMIN+5": sigrtmin + 5,
"RTMIN+6": sigrtmin + 6,
"RTMIN+7": sigrtmin + 7,
"RTMIN+8": sigrtmin + 8,
"RTMIN+9": sigrtmin + 9,
"RTMIN+10": sigrtmin + 10,
"RTMIN+11": sigrtmin + 11,
"RTMIN+12": sigrtmin + 12,
"RTMIN+13": sigrtmin + 13,
"RTMIN+14": sigrtmin + 14,
"RTMIN+15": sigrtmin + 15,
"RTMAX-14": sigrtmax - 14,
"RTMAX-13": sigrtmax - 13,
"RTMAX-12": sigrtmax - 12,
"RTMAX-11": sigrtmax - 11,
"RTMAX-10": sigrtmax - 10,
"RTMAX-9": sigrtmax - 9,
"RTMAX-8": sigrtmax - 8,
"RTMAX-7": sigrtmax - 7,
"RTMAX-6": sigrtmax - 6,
"RTMAX-5": sigrtmax - 5,
"RTMAX-4": sigrtmax - 4,
"RTMAX-3": sigrtmax - 3,
"RTMAX-2": sigrtmax - 2,
"RTMAX-1": sigrtmax - 1,
"RTMAX": sigrtmax,
}
21 changes: 21 additions & 0 deletions pkg/sigproxy/signal/signal_unix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// +build !windows

package signal

import (
"syscall"
)

// Signals used in cli/command (no windows equivalent, use
// invalid signals so they don't get handled)

const (
// SIGCHLD is a signal sent to a process when a child process terminates, is interrupted, or resumes after being interrupted.
SIGCHLD = syscall.SIGCHLD
// SIGWINCH is a signal sent to a process when its controlling terminal changes its size
SIGWINCH = syscall.SIGWINCH
// SIGPIPE is a signal sent to a process when a pipe is written to before the other end is open for reading
SIGPIPE = syscall.SIGPIPE
// DefaultStopSignal is the syscall signal used to stop a container in unix systems.
DefaultStopSignal = "SIGTERM"
)
34 changes: 34 additions & 0 deletions pkg/sigproxy/sigproxy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package sigproxy

import (
"context"
"os"

"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"

"github.com/rootless-containers/rootlesskit/pkg/sigproxy/signal"
)

// ForwardAllSignals forwards signals.
// Based on /~https://github.com/docker/cli/blob/ef2f64abbd37edfa148f745fa0013731b5074d1b/cli/command/container/tty.go#L99-L126
func ForwardAllSignals(ctx context.Context, pid int) chan os.Signal {
sigc := make(chan os.Signal, 128)
signal.CatchAll(sigc)
go func() {
for s := range sigc {
if s == unix.SIGCHLD || s == unix.SIGPIPE || s == unix.SIGURG {
continue
}
us, ok := s.(unix.Signal)
if !ok {
logrus.Warnf("Unsupported signal %v", s)
continue
}
if err := unix.Kill(pid, us); err != nil {
logrus.WithError(err).Debugf("Error sending signal %v", s)
}
}
}()
return sigc
}

0 comments on commit bbccf1e

Please sign in to comment.