github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/exp/inotify/inotify_linux.go (about) 1 // Copyright 2010 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 /* 6 Package inotify implements a wrapper for the Linux inotify system. 7 8 Example: 9 watcher, err := inotify.NewWatcher() 10 if err != nil { 11 log.Fatal(err) 12 } 13 err = watcher.Watch("/tmp") 14 if err != nil { 15 log.Fatal(err) 16 } 17 for { 18 select { 19 case ev := <-watcher.Event: 20 log.Println("event:", ev) 21 case err := <-watcher.Error: 22 log.Println("error:", err) 23 } 24 } 25 26 */ 27 package inotify // import "golang.org/x/exp/inotify" 28 29 import ( 30 "errors" 31 "fmt" 32 "os" 33 "strings" 34 "sync" 35 "syscall" 36 "unsafe" 37 ) 38 39 type Event struct { 40 Mask uint32 // Mask of events 41 Cookie uint32 // Unique cookie associating related events (for rename(2)) 42 Name string // File name (optional) 43 } 44 45 type watch struct { 46 wd uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall) 47 flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags) 48 } 49 50 type Watcher struct { 51 mu sync.Mutex 52 fd int // File descriptor (as returned by the inotify_init() syscall) 53 watches map[string]*watch // Map of inotify watches (key: path) 54 paths map[int]string // Map of watched paths (key: watch descriptor) 55 Error chan error // Errors are sent on this channel 56 Event chan *Event // Events are returned on this channel 57 done chan bool // Channel for sending a "quit message" to the reader goroutine 58 isClosed bool // Set to true when Close() is first called 59 } 60 61 // NewWatcher creates and returns a new inotify instance using inotify_init(2) 62 func NewWatcher() (*Watcher, error) { 63 fd, errno := syscall.InotifyInit() 64 if fd == -1 { 65 return nil, os.NewSyscallError("inotify_init", errno) 66 } 67 w := &Watcher{ 68 fd: fd, 69 watches: make(map[string]*watch), 70 paths: make(map[int]string), 71 Event: make(chan *Event), 72 Error: make(chan error), 73 done: make(chan bool, 1), 74 } 75 76 go w.readEvents() 77 return w, nil 78 } 79 80 // Close closes an inotify watcher instance 81 // It sends a message to the reader goroutine to quit and removes all watches 82 // associated with the inotify instance 83 func (w *Watcher) Close() error { 84 if w.isClosed { 85 return nil 86 } 87 w.isClosed = true 88 89 // Send "quit" message to the reader goroutine 90 w.done <- true 91 for path := range w.watches { 92 w.RemoveWatch(path) 93 } 94 95 return nil 96 } 97 98 // AddWatch adds path to the watched file set. 99 // The flags are interpreted as described in inotify_add_watch(2). 100 func (w *Watcher) AddWatch(path string, flags uint32) error { 101 if w.isClosed { 102 return errors.New("inotify instance already closed") 103 } 104 105 watchEntry, found := w.watches[path] 106 if found { 107 watchEntry.flags |= flags 108 flags |= syscall.IN_MASK_ADD 109 } 110 111 w.mu.Lock() // synchronize with readEvents goroutine 112 113 wd, err := syscall.InotifyAddWatch(w.fd, path, flags) 114 if err != nil { 115 w.mu.Unlock() 116 return &os.PathError{ 117 Op: "inotify_add_watch", 118 Path: path, 119 Err: err, 120 } 121 } 122 123 if !found { 124 w.watches[path] = &watch{wd: uint32(wd), flags: flags} 125 w.paths[wd] = path 126 } 127 w.mu.Unlock() 128 return nil 129 } 130 131 // Watch adds path to the watched file set, watching all events. 132 func (w *Watcher) Watch(path string) error { 133 return w.AddWatch(path, IN_ALL_EVENTS) 134 } 135 136 // RemoveWatch removes path from the watched file set. 137 func (w *Watcher) RemoveWatch(path string) error { 138 watch, ok := w.watches[path] 139 if !ok { 140 return errors.New(fmt.Sprintf("can't remove non-existent inotify watch for: %s", path)) 141 } 142 success, errno := syscall.InotifyRmWatch(w.fd, watch.wd) 143 if success == -1 { 144 return os.NewSyscallError("inotify_rm_watch", errno) 145 } 146 delete(w.watches, path) 147 return nil 148 } 149 150 // readEvents reads from the inotify file descriptor, converts the 151 // received events into Event objects and sends them via the Event channel 152 func (w *Watcher) readEvents() { 153 var buf [syscall.SizeofInotifyEvent * 4096]byte 154 155 for { 156 n, err := syscall.Read(w.fd, buf[:]) 157 // See if there is a message on the "done" channel 158 var done bool 159 select { 160 case done = <-w.done: 161 default: 162 } 163 164 // If EOF or a "done" message is received 165 if n == 0 || done { 166 // The syscall.Close can be slow. Close 167 // w.Event first. 168 close(w.Event) 169 err := syscall.Close(w.fd) 170 if err != nil { 171 w.Error <- os.NewSyscallError("close", err) 172 } 173 close(w.Error) 174 return 175 } 176 if n < 0 { 177 w.Error <- os.NewSyscallError("read", err) 178 continue 179 } 180 if n < syscall.SizeofInotifyEvent { 181 w.Error <- errors.New("inotify: short read in readEvents()") 182 continue 183 } 184 185 var offset uint32 = 0 186 // We don't know how many events we just read into the buffer 187 // While the offset points to at least one whole event... 188 for offset <= uint32(n-syscall.SizeofInotifyEvent) { 189 // Point "raw" to the event in the buffer 190 raw := (*syscall.InotifyEvent)(unsafe.Pointer(&buf[offset])) 191 event := new(Event) 192 event.Mask = uint32(raw.Mask) 193 event.Cookie = uint32(raw.Cookie) 194 nameLen := uint32(raw.Len) 195 // If the event happened to the watched directory or the watched file, the kernel 196 // doesn't append the filename to the event, but we would like to always fill the 197 // the "Name" field with a valid filename. We retrieve the path of the watch from 198 // the "paths" map. 199 w.mu.Lock() 200 event.Name = w.paths[int(raw.Wd)] 201 w.mu.Unlock() 202 if nameLen > 0 { 203 // Point "bytes" at the first byte of the filename 204 bytes := (*[syscall.PathMax]byte)(unsafe.Pointer(&buf[offset+syscall.SizeofInotifyEvent])) 205 // The filename is padded with NUL bytes. TrimRight() gets rid of those. 206 event.Name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000") 207 } 208 // Send the event on the events channel 209 w.Event <- event 210 211 // Move to the next event in the buffer 212 offset += syscall.SizeofInotifyEvent + nameLen 213 } 214 } 215 } 216 217 // String formats the event e in the form 218 // "filename: 0xEventMask = IN_ACCESS|IN_ATTRIB_|..." 219 func (e *Event) String() string { 220 var events string = "" 221 222 m := e.Mask 223 for _, b := range eventBits { 224 if m&b.Value == b.Value { 225 m &^= b.Value 226 events += "|" + b.Name 227 } 228 } 229 230 if m != 0 { 231 events += fmt.Sprintf("|%#x", m) 232 } 233 if len(events) > 0 { 234 events = " == " + events[1:] 235 } 236 237 return fmt.Sprintf("%q: %#x%s", e.Name, e.Mask, events) 238 } 239 240 const ( 241 // Options for inotify_init() are not exported 242 // IN_CLOEXEC uint32 = syscall.IN_CLOEXEC 243 // IN_NONBLOCK uint32 = syscall.IN_NONBLOCK 244 245 // Options for AddWatch 246 IN_DONT_FOLLOW uint32 = syscall.IN_DONT_FOLLOW 247 IN_ONESHOT uint32 = syscall.IN_ONESHOT 248 IN_ONLYDIR uint32 = syscall.IN_ONLYDIR 249 250 // The "IN_MASK_ADD" option is not exported, as AddWatch 251 // adds it automatically, if there is already a watch for the given path 252 // IN_MASK_ADD uint32 = syscall.IN_MASK_ADD 253 254 // Events 255 IN_ACCESS uint32 = syscall.IN_ACCESS 256 IN_ALL_EVENTS uint32 = syscall.IN_ALL_EVENTS 257 IN_ATTRIB uint32 = syscall.IN_ATTRIB 258 IN_CLOSE uint32 = syscall.IN_CLOSE 259 IN_CLOSE_NOWRITE uint32 = syscall.IN_CLOSE_NOWRITE 260 IN_CLOSE_WRITE uint32 = syscall.IN_CLOSE_WRITE 261 IN_CREATE uint32 = syscall.IN_CREATE 262 IN_DELETE uint32 = syscall.IN_DELETE 263 IN_DELETE_SELF uint32 = syscall.IN_DELETE_SELF 264 IN_MODIFY uint32 = syscall.IN_MODIFY 265 IN_MOVE uint32 = syscall.IN_MOVE 266 IN_MOVED_FROM uint32 = syscall.IN_MOVED_FROM 267 IN_MOVED_TO uint32 = syscall.IN_MOVED_TO 268 IN_MOVE_SELF uint32 = syscall.IN_MOVE_SELF 269 IN_OPEN uint32 = syscall.IN_OPEN 270 271 // Special events 272 IN_ISDIR uint32 = syscall.IN_ISDIR 273 IN_IGNORED uint32 = syscall.IN_IGNORED 274 IN_Q_OVERFLOW uint32 = syscall.IN_Q_OVERFLOW 275 IN_UNMOUNT uint32 = syscall.IN_UNMOUNT 276 ) 277 278 var eventBits = []struct { 279 Value uint32 280 Name string 281 }{ 282 {IN_ACCESS, "IN_ACCESS"}, 283 {IN_ATTRIB, "IN_ATTRIB"}, 284 {IN_CLOSE, "IN_CLOSE"}, 285 {IN_CLOSE_NOWRITE, "IN_CLOSE_NOWRITE"}, 286 {IN_CLOSE_WRITE, "IN_CLOSE_WRITE"}, 287 {IN_CREATE, "IN_CREATE"}, 288 {IN_DELETE, "IN_DELETE"}, 289 {IN_DELETE_SELF, "IN_DELETE_SELF"}, 290 {IN_MODIFY, "IN_MODIFY"}, 291 {IN_MOVE, "IN_MOVE"}, 292 {IN_MOVED_FROM, "IN_MOVED_FROM"}, 293 {IN_MOVED_TO, "IN_MOVED_TO"}, 294 {IN_MOVE_SELF, "IN_MOVE_SELF"}, 295 {IN_OPEN, "IN_OPEN"}, 296 {IN_ISDIR, "IN_ISDIR"}, 297 {IN_IGNORED, "IN_IGNORED"}, 298 {IN_Q_OVERFLOW, "IN_Q_OVERFLOW"}, 299 {IN_UNMOUNT, "IN_UNMOUNT"}, 300 }