github.com/google/cadvisor@v0.49.1/container/common/inotify_watcher.go (about) 1 // Copyright 2015 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package common 16 17 import ( 18 "sync" 19 20 inotify "k8s.io/utils/inotify" 21 ) 22 23 // Watcher for container-related inotify events in the cgroup hierarchy. 24 // 25 // Implementation is thread-safe. 26 type InotifyWatcher struct { 27 // Underlying inotify watcher. 28 watcher *inotify.Watcher 29 30 // Map of containers being watched to cgroup paths watched for that container. 31 containersWatched map[string]map[string]bool 32 33 // Lock for all datastructure access. 34 lock sync.Mutex 35 } 36 37 func NewInotifyWatcher() (*InotifyWatcher, error) { 38 w, err := inotify.NewWatcher() 39 if err != nil { 40 return nil, err 41 } 42 43 return &InotifyWatcher{ 44 watcher: w, 45 containersWatched: make(map[string]map[string]bool), 46 }, nil 47 } 48 49 // Add a watch to the specified directory. Returns if the container was already being watched. 50 func (iw *InotifyWatcher) AddWatch(containerName, dir string) (bool, error) { 51 iw.lock.Lock() 52 defer iw.lock.Unlock() 53 54 cgroupsWatched, alreadyWatched := iw.containersWatched[containerName] 55 56 // Register an inotify notification. 57 if !cgroupsWatched[dir] { 58 err := iw.watcher.AddWatch(dir, inotify.InCreate|inotify.InDelete|inotify.InMove) 59 if err != nil { 60 return alreadyWatched, err 61 } 62 63 if cgroupsWatched == nil { 64 cgroupsWatched = make(map[string]bool) 65 } 66 cgroupsWatched[dir] = true 67 } 68 69 // Record our watching of the container. 70 if !alreadyWatched { 71 iw.containersWatched[containerName] = cgroupsWatched 72 } 73 return alreadyWatched, nil 74 } 75 76 // Remove watch from the specified directory. Returns if this was the last watch on the specified container. 77 func (iw *InotifyWatcher) RemoveWatch(containerName, dir string) (bool, error) { 78 iw.lock.Lock() 79 defer iw.lock.Unlock() 80 81 // If we don't have a watch registered for this, just return. 82 cgroupsWatched, ok := iw.containersWatched[containerName] 83 if !ok { 84 return false, nil 85 } 86 87 // Remove the inotify watch if it exists. 88 if cgroupsWatched[dir] { 89 err := iw.watcher.RemoveWatch(dir) 90 if err != nil { 91 return false, nil 92 } 93 delete(cgroupsWatched, dir) 94 } 95 96 // Remove the record if this is the last watch. 97 if len(cgroupsWatched) == 0 { 98 delete(iw.containersWatched, containerName) 99 return true, nil 100 } 101 102 return false, nil 103 } 104 105 // Errors are returned on this channel. 106 func (iw *InotifyWatcher) Error() chan error { 107 return iw.watcher.Error 108 } 109 110 // Events are returned on this channel. 111 func (iw *InotifyWatcher) Event() chan *inotify.Event { 112 return iw.watcher.Event 113 } 114 115 // Closes the inotify watcher. 116 func (iw *InotifyWatcher) Close() error { 117 return iw.watcher.Close() 118 } 119 120 // Returns a map of containers to the cgroup paths being watched. 121 func (iw *InotifyWatcher) GetWatches() map[string][]string { 122 out := make(map[string][]string, len(iw.containersWatched)) 123 for k, v := range iw.containersWatched { 124 out[k] = mapToSlice(v) 125 } 126 return out 127 } 128 129 func mapToSlice(m map[string]bool) []string { 130 out := make([]string, 0, len(m)) 131 for k := range m { 132 out = append(out, k) 133 } 134 return out 135 }