signals.go 2.9 KB

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