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