File: notify_v2_linux.go

package info (click to toggle)
runc 1.3.3%2Bds1-3
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 3,136 kB
  • sloc: sh: 2,298; ansic: 1,125; makefile: 229
file content (85 lines) | stat: -rw-r--r-- 2,405 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
package libcontainer

import (
	"fmt"
	"os"
	"path/filepath"
	"unsafe"

	"github.com/opencontainers/cgroups/fscommon"
	"github.com/sirupsen/logrus"
	"golang.org/x/sys/unix"
)

func registerMemoryEventV2(cgDir, evName, cgEvName string) (<-chan struct{}, error) {
	fd, err := unix.InotifyInit()
	if err != nil {
		return nil, fmt.Errorf("unable to init inotify: %w", err)
	}
	// watching oom kill
	evFd, err := unix.InotifyAddWatch(fd, filepath.Join(cgDir, evName), unix.IN_MODIFY)
	if err != nil {
		unix.Close(fd)
		return nil, fmt.Errorf("unable to add inotify watch: %w", err)
	}
	// Because no `unix.IN_DELETE|unix.IN_DELETE_SELF` event for cgroup file system, so watching all process exited
	cgFd, err := unix.InotifyAddWatch(fd, filepath.Join(cgDir, cgEvName), unix.IN_MODIFY)
	if err != nil {
		unix.Close(fd)
		return nil, fmt.Errorf("unable to add inotify watch: %w", err)
	}
	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 == unix.EINTR { //nolint:errorlint // unix errors are bare
				continue
			}
			if err != nil {
				err = os.NewSyscallError("read", err)
				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 + rawEvent.Len
				if rawEvent.Mask&unix.IN_MODIFY != unix.IN_MODIFY {
					continue
				}
				switch int(rawEvent.Wd) {
				case evFd:
					oom, err := fscommon.GetValueByKey(cgDir, evName, "oom_kill")
					if err != nil || oom > 0 {
						ch <- struct{}{}
					}
				case cgFd:
					pids, err := fscommon.GetValueByKey(cgDir, cgEvName, "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")
}