123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117 |
- // +build linux
- package runc
- import (
- "os"
- "os/signal"
- "syscall"
- "github.com/Sirupsen/logrus"
- "github.com/opencontainers/runc/libcontainer"
- "github.com/opencontainers/runc/libcontainer/system"
- "github.com/opencontainers/runc/libcontainer/utils"
- )
- const signalBufferSize = 2048
- // newSignalHandler returns a signal handler for processing SIGCHLD and SIGWINCH signals
- // while still forwarding all other signals to the process.
- func newSignalHandler(tty *tty, enableSubreaper bool) *signalHandler {
- if enableSubreaper {
- // set us as the subreaper before registering the signal handler for the container
- if err := system.SetSubreaper(1); err != nil {
- logrus.Warn(err)
- }
- }
- // ensure that we have a large buffer size so that we do not miss any signals
- // incase we are not processing them fast enough.
- s := make(chan os.Signal, signalBufferSize)
- // handle all signals for the process.
- signal.Notify(s)
- return &signalHandler{
- tty: tty,
- signals: s,
- }
- }
- // exit models a process exit status with the pid and
- // exit status.
- type exit struct {
- pid int
- status int
- }
- type signalHandler struct {
- signals chan os.Signal
- tty *tty
- }
- // forward handles the main signal event loop forwarding, resizing, or reaping depending
- // on the signal received.
- func (h *signalHandler) forward(process *libcontainer.Process) (int, error) {
- // make sure we know the pid of our main process so that we can return
- // after it dies.
- pid1, err := process.Pid()
- if err != nil {
- return -1, err
- }
- // perform the initial tty resize.
- h.tty.resize()
- for s := range h.signals {
- switch s {
- case syscall.SIGWINCH:
- h.tty.resize()
- case syscall.SIGCHLD:
- exits, err := h.reap()
- if err != nil {
- logrus.Error(err)
- }
- for _, e := range exits {
- logrus.WithFields(logrus.Fields{
- "pid": e.pid,
- "status": e.status,
- }).Debug("process exited")
- if e.pid == pid1 {
- // call Wait() on the process even though we already have the exit
- // status because we must ensure that any of the go specific process
- // fun such as flushing pipes are complete before we return.
- process.Wait()
- return e.status, nil
- }
- }
- default:
- logrus.Debugf("sending signal to process %s", s)
- if err := syscall.Kill(pid1, s.(syscall.Signal)); err != nil {
- logrus.Error(err)
- }
- }
- }
- return -1, nil
- }
- // reap runs wait4 in a loop until we have finished processing any existing exits
- // then returns all exits to the main event loop for further processing.
- func (h *signalHandler) reap() (exits []exit, err error) {
- var (
- ws syscall.WaitStatus
- rus syscall.Rusage
- )
- for {
- pid, err := syscall.Wait4(-1, &ws, syscall.WNOHANG, &rus)
- if err != nil {
- if err == syscall.ECHILD {
- return exits, nil
- }
- return nil, err
- }
- if pid <= 0 {
- return exits, nil
- }
- exits = append(exits, exit{
- pid: pid,
- status: utils.ExitStatus(ws),
- })
- }
- }
|