notify_v2_linux.go 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. package libcontainer
  2. import (
  3. "fmt"
  4. "path/filepath"
  5. "unsafe"
  6. "github.com/opencontainers/runc/libcontainer/cgroups/fscommon"
  7. "github.com/sirupsen/logrus"
  8. "golang.org/x/sys/unix"
  9. )
  10. func registerMemoryEventV2(cgDir, evName, cgEvName string) (<-chan struct{}, error) {
  11. fd, err := unix.InotifyInit()
  12. if err != nil {
  13. return nil, fmt.Errorf("unable to init inotify: %w", err)
  14. }
  15. // watching oom kill
  16. evFd, err := unix.InotifyAddWatch(fd, filepath.Join(cgDir, evName), unix.IN_MODIFY)
  17. if err != nil {
  18. unix.Close(fd)
  19. return nil, fmt.Errorf("unable to add inotify watch: %w", err)
  20. }
  21. // Because no `unix.IN_DELETE|unix.IN_DELETE_SELF` event for cgroup file system, so watching all process exited
  22. cgFd, err := unix.InotifyAddWatch(fd, filepath.Join(cgDir, cgEvName), unix.IN_MODIFY)
  23. if err != nil {
  24. unix.Close(fd)
  25. return nil, fmt.Errorf("unable to add inotify watch: %w", err)
  26. }
  27. ch := make(chan struct{})
  28. go func() {
  29. var (
  30. buffer [unix.SizeofInotifyEvent + unix.PathMax + 1]byte
  31. offset uint32
  32. )
  33. defer func() {
  34. unix.Close(fd)
  35. close(ch)
  36. }()
  37. for {
  38. n, err := unix.Read(fd, buffer[:])
  39. if err != nil {
  40. logrus.Warnf("unable to read event data from inotify, got error: %v", err)
  41. return
  42. }
  43. if n < unix.SizeofInotifyEvent {
  44. logrus.Warnf("we should read at least %d bytes from inotify, but got %d bytes.", unix.SizeofInotifyEvent, n)
  45. return
  46. }
  47. offset = 0
  48. for offset <= uint32(n-unix.SizeofInotifyEvent) {
  49. rawEvent := (*unix.InotifyEvent)(unsafe.Pointer(&buffer[offset]))
  50. offset += unix.SizeofInotifyEvent + rawEvent.Len
  51. if rawEvent.Mask&unix.IN_MODIFY != unix.IN_MODIFY {
  52. continue
  53. }
  54. switch int(rawEvent.Wd) {
  55. case evFd:
  56. oom, err := fscommon.GetValueByKey(cgDir, evName, "oom_kill")
  57. if err != nil || oom > 0 {
  58. ch <- struct{}{}
  59. }
  60. case cgFd:
  61. pids, err := fscommon.GetValueByKey(cgDir, cgEvName, "populated")
  62. if err != nil || pids == 0 {
  63. return
  64. }
  65. }
  66. }
  67. }
  68. }()
  69. return ch, nil
  70. }
  71. // notifyOnOOMV2 returns channel on which you can expect event about OOM,
  72. // if process died without OOM this channel will be closed.
  73. func notifyOnOOMV2(path string) (<-chan struct{}, error) {
  74. return registerMemoryEventV2(path, "memory.events", "cgroup.events")
  75. }