summaryrefslogtreecommitdiff
path: root/vendor/github.com/opencontainers/runc/libcontainer/notify_linux_v2.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/opencontainers/runc/libcontainer/notify_linux_v2.go')
-rw-r--r--vendor/github.com/opencontainers/runc/libcontainer/notify_linux_v2.go102
1 files changed, 102 insertions, 0 deletions
diff --git a/vendor/github.com/opencontainers/runc/libcontainer/notify_linux_v2.go b/vendor/github.com/opencontainers/runc/libcontainer/notify_linux_v2.go
new file mode 100644
index 000000000..cdab10ed6
--- /dev/null
+++ b/vendor/github.com/opencontainers/runc/libcontainer/notify_linux_v2.go
@@ -0,0 +1,102 @@
+// +build linux
+
+package libcontainer
+
+import (
+ "io/ioutil"
+ "path/filepath"
+ "strconv"
+ "strings"
+ "unsafe"
+
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+ "golang.org/x/sys/unix"
+)
+
+func getValueFromCgroup(path, key string) (int, error) {
+ content, err := ioutil.ReadFile(path)
+ if err != nil {
+ return 0, err
+ }
+
+ lines := strings.Split(string(content), "\n")
+ for _, line := range lines {
+ arr := strings.Split(line, " ")
+ if len(arr) == 2 && arr[0] == key {
+ return strconv.Atoi(arr[1])
+ }
+ }
+ return 0, nil
+}
+
+func registerMemoryEventV2(cgDir, evName, cgEvName string) (<-chan struct{}, error) {
+ eventControlPath := filepath.Join(cgDir, evName)
+ cgEvPath := filepath.Join(cgDir, cgEvName)
+ fd, err := unix.InotifyInit()
+ if err != nil {
+ return nil, errors.Wrap(err, "unable to init inotify")
+ }
+ // watching oom kill
+ evFd, err := unix.InotifyAddWatch(fd, eventControlPath, unix.IN_MODIFY)
+ if err != nil {
+ unix.Close(fd)
+ return nil, errors.Wrap(err, "unable to add inotify watch")
+ }
+ // Because no `unix.IN_DELETE|unix.IN_DELETE_SELF` event for cgroup file system, so watching all process exited
+ cgFd, err := unix.InotifyAddWatch(fd, cgEvPath, unix.IN_MODIFY)
+ if err != nil {
+ unix.Close(fd)
+ return nil, errors.Wrap(err, "unable to add inotify watch")
+ }
+ ch := make(chan struct{})
+ go func() {
+ var (
+ buffer [unix.SizeofInotifyEvent + unix.PathMax + 1]byte
+ offset uint32
+ )
+ defer func() {
+ unix.Close(fd)
+ close(ch)
+ }()
+
+ for {
+ n, err := unix.Read(fd, buffer[:])
+ if err != nil {
+ logrus.Warnf("unable to read event data from inotify, got error: %v", err)
+ return
+ }
+ if n < unix.SizeofInotifyEvent {
+ logrus.Warnf("we should read at least %d bytes from inotify, but got %d bytes.", unix.SizeofInotifyEvent, n)
+ return
+ }
+ offset = 0
+ for offset <= uint32(n-unix.SizeofInotifyEvent) {
+ rawEvent := (*unix.InotifyEvent)(unsafe.Pointer(&buffer[offset]))
+ offset += unix.SizeofInotifyEvent + uint32(rawEvent.Len)
+ if rawEvent.Mask&unix.IN_MODIFY != unix.IN_MODIFY {
+ continue
+ }
+ switch int(rawEvent.Wd) {
+ case evFd:
+ oom, err := getValueFromCgroup(eventControlPath, "oom_kill")
+ if err != nil || oom > 0 {
+ ch <- struct{}{}
+ }
+ case cgFd:
+ pids, err := getValueFromCgroup(cgEvPath, "populated")
+ if err != nil || pids == 0 {
+ return
+ }
+ }
+ }
+ }
+ }()
+ return ch, nil
+}
+
+// notifyOnOOMV2 returns channel on which you can expect event about OOM,
+// if process died without OOM this channel will be closed.
+func notifyOnOOMV2(path string) (<-chan struct{}, error) {
+ return registerMemoryEventV2(path, "memory.events", "cgroup.events")
+}