forked from google/cadvisor
/
inotify_watcher.go
135 lines (112 loc) · 3.46 KB
/
inotify_watcher.go
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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package common
import (
"sync"
"golang.org/x/exp/inotify"
)
// Watcher for container-related inotify events in the cgroup hierarchy.
//
// Implementation is thread-safe.
type InotifyWatcher struct {
// Underlying inotify watcher.
watcher *inotify.Watcher
// Map of containers being watched to cgroup paths watched for that container.
containersWatched map[string]map[string]bool
// Lock for all datastructure access.
lock sync.Mutex
}
func NewInotifyWatcher() (*InotifyWatcher, error) {
w, err := inotify.NewWatcher()
if err != nil {
return nil, err
}
return &InotifyWatcher{
watcher: w,
containersWatched: make(map[string]map[string]bool),
}, nil
}
// Add a watch to the specified directory. Returns if the container was already being watched.
func (iw *InotifyWatcher) AddWatch(containerName, dir string) (bool, error) {
iw.lock.Lock()
defer iw.lock.Unlock()
cgroupsWatched, alreadyWatched := iw.containersWatched[containerName]
// Register an inotify notification.
if !cgroupsWatched[dir] {
err := iw.watcher.AddWatch(dir, inotify.IN_CREATE|inotify.IN_DELETE|inotify.IN_MOVE)
if err != nil {
return alreadyWatched, err
}
if cgroupsWatched == nil {
cgroupsWatched = make(map[string]bool)
}
cgroupsWatched[dir] = true
}
// Record our watching of the container.
if !alreadyWatched {
iw.containersWatched[containerName] = cgroupsWatched
}
return alreadyWatched, nil
}
// Remove watch from the specified directory. Returns if this was the last watch on the specified container.
func (iw *InotifyWatcher) RemoveWatch(containerName, dir string) (bool, error) {
iw.lock.Lock()
defer iw.lock.Unlock()
// If we don't have a watch registed for this, just return.
cgroupsWatched, ok := iw.containersWatched[containerName]
if !ok {
return false, nil
}
// Remove the inotify watch if it exists.
if cgroupsWatched[dir] {
err := iw.watcher.RemoveWatch(dir)
if err != nil {
return false, nil
}
delete(cgroupsWatched, dir)
}
// Remove the record if this is the last watch.
if len(cgroupsWatched) == 0 {
delete(iw.containersWatched, containerName)
return true, nil
}
return false, nil
}
// Errors are returned on this channel.
func (iw *InotifyWatcher) Error() chan error {
return iw.watcher.Error
}
// Events are returned on this channel.
func (iw *InotifyWatcher) Event() chan *inotify.Event {
return iw.watcher.Event
}
// Closes the inotify watcher.
func (iw *InotifyWatcher) Close() error {
return iw.watcher.Close()
}
// Returns a map of containers to the cgroup paths being watched.
func (iw *InotifyWatcher) GetWatches() map[string][]string {
out := make(map[string][]string, len(iw.containersWatched))
for k, v := range iw.containersWatched {
out[k] = mapToSlice(v)
}
return out
}
func mapToSlice(m map[string]bool) []string {
out := make([]string, 0, len(m))
for k := range m {
out = append(out, k)
}
return out
}