...

Source file src/github.com/opencontainers/runc/libcontainer/notify_v2_linux.go

Documentation: github.com/opencontainers/runc/libcontainer

     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  }
    81  

View as plain text