github.com/zhuohuang-hust/src-cbuild@v0.0.0-20230105071821-c7aab3e7c840/mergeCode/runc/signals.go (about)

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