...

Source file src/github.com/opencontainers/runc/signals.go

Documentation: github.com/opencontainers/runc

     1  package main
     2  
     3  import (
     4  	"os"
     5  	"os/signal"
     6  
     7  	"github.com/opencontainers/runc/libcontainer"
     8  	"github.com/opencontainers/runc/libcontainer/system"
     9  	"github.com/opencontainers/runc/libcontainer/utils"
    10  
    11  	"github.com/sirupsen/logrus"
    12  	"golang.org/x/sys/unix"
    13  )
    14  
    15  const signalBufferSize = 2048
    16  
    17  // newSignalHandler returns a signal handler for processing SIGCHLD and SIGWINCH signals
    18  // while still forwarding all other signals to the process.
    19  // If notifySocket is present, use it to read systemd notifications from the container and
    20  // forward them to notifySocketHost.
    21  func newSignalHandler(enableSubreaper bool, notifySocket *notifySocket) *signalHandler {
    22  	if enableSubreaper {
    23  		// set us as the subreaper before registering the signal handler for the container
    24  		if err := system.SetSubreaper(1); err != nil {
    25  			logrus.Warn(err)
    26  		}
    27  	}
    28  	// ensure that we have a large buffer size so that we do not miss any signals
    29  	// in case we are not processing them fast enough.
    30  	s := make(chan os.Signal, signalBufferSize)
    31  	// handle all signals for the process.
    32  	signal.Notify(s)
    33  	return &signalHandler{
    34  		signals:      s,
    35  		notifySocket: notifySocket,
    36  	}
    37  }
    38  
    39  // exit models a process exit status with the pid and
    40  // exit status.
    41  type exit struct {
    42  	pid    int
    43  	status int
    44  }
    45  
    46  type signalHandler struct {
    47  	signals      chan os.Signal
    48  	notifySocket *notifySocket
    49  }
    50  
    51  // forward handles the main signal event loop forwarding, resizing, or reaping depending
    52  // on the signal received.
    53  func (h *signalHandler) forward(process *libcontainer.Process, tty *tty, detach bool) (int, error) {
    54  	// make sure we know the pid of our main process so that we can return
    55  	// after it dies.
    56  	if detach && h.notifySocket == nil {
    57  		return 0, nil
    58  	}
    59  
    60  	pid1, err := process.Pid()
    61  	if err != nil {
    62  		return -1, err
    63  	}
    64  
    65  	if h.notifySocket != nil {
    66  		if detach {
    67  			_ = h.notifySocket.run(pid1)
    68  			return 0, nil
    69  		}
    70  		_ = h.notifySocket.run(os.Getpid())
    71  		go func() { _ = h.notifySocket.run(0) }()
    72  	}
    73  
    74  	// Perform the initial tty resize. Always ignore errors resizing because
    75  	// stdout might have disappeared (due to races with when SIGHUP is sent).
    76  	_ = tty.resize()
    77  	// Handle and forward signals.
    78  	for s := range h.signals {
    79  		switch s {
    80  		case unix.SIGWINCH:
    81  			// Ignore errors resizing, as above.
    82  			_ = tty.resize()
    83  		case unix.SIGCHLD:
    84  			exits, err := h.reap()
    85  			if err != nil {
    86  				logrus.Error(err)
    87  			}
    88  			for _, e := range exits {
    89  				logrus.WithFields(logrus.Fields{
    90  					"pid":    e.pid,
    91  					"status": e.status,
    92  				}).Debug("process exited")
    93  				if e.pid == pid1 {
    94  					// call Wait() on the process even though we already have the exit
    95  					// status because we must ensure that any of the go specific process
    96  					// fun such as flushing pipes are complete before we return.
    97  					_, _ = process.Wait()
    98  					return e.status, nil
    99  				}
   100  			}
   101  		default:
   102  			logrus.Debugf("sending signal to process %s", s)
   103  			if err := unix.Kill(pid1, s.(unix.Signal)); err != nil {
   104  				logrus.Error(err)
   105  			}
   106  		}
   107  	}
   108  	return -1, nil
   109  }
   110  
   111  // reap runs wait4 in a loop until we have finished processing any existing exits
   112  // then returns all exits to the main event loop for further processing.
   113  func (h *signalHandler) reap() (exits []exit, err error) {
   114  	var (
   115  		ws  unix.WaitStatus
   116  		rus unix.Rusage
   117  	)
   118  	for {
   119  		pid, err := unix.Wait4(-1, &ws, unix.WNOHANG, &rus)
   120  		if err != nil {
   121  			if err == unix.ECHILD { //nolint:errorlint // unix errors are bare
   122  				return exits, nil
   123  			}
   124  			return nil, err
   125  		}
   126  		if pid <= 0 {
   127  			return exits, nil
   128  		}
   129  		exits = append(exits, exit{
   130  			pid:    pid,
   131  			status: utils.ExitStatus(ws),
   132  		})
   133  	}
   134  }
   135  

View as plain text