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 }