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.
21 // Watcher watches a set of files, delivering events to a channel.
25 mu sync.Mutex // Map access
26 cv *sync.Cond // sync removing on rm_watch with IN_IGNORE
29 watches map[string]*watch // Map of inotify watches (key: path)
30 paths map[int]string // Map of watched paths (key: watch descriptor)
31 done chan struct{} // Channel for sending a "quit message" to the reader goroutine
32 doneResp chan struct{} // Channel to respond to Close
35 // NewWatcher establishes a new watcher with the underlying OS and begins waiting for events.
36 func NewWatcher() (*Watcher, error) {
38 fd, errno := syscall.InotifyInit()
43 poller, err := newFdPoller(fd)
51 watches: make(map[string]*watch),
52 paths: make(map[int]string),
53 Events: make(chan Event),
54 Errors: make(chan error),
55 done: make(chan struct{}),
56 doneResp: make(chan struct{}),
58 w.cv = sync.NewCond(&w.mu)
64 func (w *Watcher) isClosed() bool {
73 // Close removes all watches and closes the events channel.
74 func (w *Watcher) Close() error {
79 // Send 'close' signal to goroutine, and set the Watcher to closed.
85 // Wait for goroutine to close
91 // Add starts watching the named file or directory (non-recursively).
92 func (w *Watcher) Add(name string) error {
93 name = filepath.Clean(name)
95 return errors.New("inotify instance already closed")
98 const agnosticEvents = syscall.IN_MOVED_TO | syscall.IN_MOVED_FROM |
99 syscall.IN_CREATE | syscall.IN_ATTRIB | syscall.IN_MODIFY |
100 syscall.IN_MOVE_SELF | syscall.IN_DELETE | syscall.IN_DELETE_SELF
102 var flags uint32 = agnosticEvents
105 watchEntry, found := w.watches[name]
108 watchEntry.flags |= flags
109 flags |= syscall.IN_MASK_ADD
111 wd, errno := syscall.InotifyAddWatch(w.fd, name, flags)
117 w.watches[name] = &watch{wd: uint32(wd), flags: flags}
124 // Remove stops watching the named file or directory (non-recursively).
125 func (w *Watcher) Remove(name string) error {
126 name = filepath.Clean(name)
131 watch, ok := w.watches[name]
133 // Remove it from inotify.
135 return fmt.Errorf("can't remove non-existent inotify watch for: %s", name)
137 // inotify_rm_watch will return EINVAL if the file has been deleted;
138 // the inotify will already have been removed.
139 // watches and pathes are deleted in ignoreLinux() implicitly and asynchronously
140 // by calling inotify_rm_watch() below. e.g. readEvents() goroutine receives IN_IGNORE
141 // so that EINVAL means that the wd is being rm_watch()ed or its file removed
142 // by another thread and we have not received IN_IGNORE event.
143 success, errno := syscall.InotifyRmWatch(w.fd, watch.wd)
145 // TODO: Perhaps it's not helpful to return an error here in every case.
146 // the only two possible errors are:
147 // EBADF, which happens when w.fd is not a valid file descriptor of any kind.
148 // EINVAL, which is when fd is not an inotify descriptor or wd is not a valid watch descriptor.
149 // Watch descriptors are invalidated when they are removed explicitly or implicitly;
150 // explicitly by inotify_rm_watch, implicitly when the file they are watching is deleted.
154 // wait until ignoreLinux() deleting maps
158 _, exists = w.watches[name]
165 wd uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall)
166 flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags)
169 // readEvents reads from the inotify file descriptor, converts the
170 // received events into Event objects and sends them via the Events channel
171 func (w *Watcher) readEvents() {
173 buf [syscall.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events
174 n int // Number of bytes read with read()
175 errno error // Syscall errno
176 ok bool // For poller.wait
179 defer close(w.doneResp)
180 defer close(w.Errors)
181 defer close(w.Events)
182 defer syscall.Close(w.fd)
183 defer w.poller.close()
186 // See if we have been closed.
191 ok, errno = w.poller.wait()
194 case w.Errors <- errno:
205 n, errno = syscall.Read(w.fd, buf[:])
206 // If a signal interrupted execution, see if we've been asked to close, and try again.
207 // http://man7.org/linux/man-pages/man7/signal.7.html :
208 // "Before Linux 3.8, reads from an inotify(7) file descriptor were not restartable"
209 if errno == syscall.EINTR {
213 // syscall.Read might have been woken up by Close. If so, we're done.
218 if n < syscall.SizeofInotifyEvent {
221 // If EOF is received. This should really never happen.
224 // If an error occurred while reading.
227 // Read was too short.
228 err = errors.New("notify: short read in readEvents()")
231 case w.Errors <- err:
239 // We don't know how many events we just read into the buffer
240 // While the offset points to at least one whole event...
241 for offset <= uint32(n-syscall.SizeofInotifyEvent) {
242 // Point "raw" to the event in the buffer
243 raw := (*syscall.InotifyEvent)(unsafe.Pointer(&buf[offset]))
245 mask := uint32(raw.Mask)
246 nameLen := uint32(raw.Len)
247 // If the event happened to the watched directory or the watched file, the kernel
248 // doesn't append the filename to the event, but we would like to always fill the
249 // the "Name" field with a valid filename. We retrieve the path of the watch from
252 name := w.paths[int(raw.Wd)]
255 // Point "bytes" at the first byte of the filename
256 bytes := (*[syscall.PathMax]byte)(unsafe.Pointer(&buf[offset+syscall.SizeofInotifyEvent]))
257 // The filename is padded with NULL bytes. TrimRight() gets rid of those.
258 name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000")
261 event := newEvent(name, mask)
263 // Send the events that are not ignored on the events channel
264 if !event.ignoreLinux(w, raw.Wd, mask) {
266 case w.Events <- event:
272 // Move to the next event in the buffer
273 offset += syscall.SizeofInotifyEvent + nameLen
278 // Certain types of events can be "ignored" and not sent over the Events
279 // channel. Such as events marked ignore by the kernel, or MODIFY events
280 // against files that do not exist.
281 func (e *Event) ignoreLinux(w *Watcher, wd int32, mask uint32) bool {
282 // Ignore anything the inotify API says to ignore
283 if mask&syscall.IN_IGNORED == syscall.IN_IGNORED {
286 name := w.paths[int(wd)]
287 delete(w.paths, int(wd))
288 delete(w.watches, name)
293 // If the event is not a DELETE or RENAME, the file must exist.
294 // Otherwise the event is ignored.
295 // *Note*: this was put in place because it was seen that a MODIFY
296 // event was sent after the DELETE. This ignores that MODIFY and
297 // assumes a DELETE will come or has come if the file doesn't exist.
298 if !(e.Op&Remove == Remove || e.Op&Rename == Rename) {
299 _, statErr := os.Lstat(e.Name)
300 return os.IsNotExist(statErr)
305 // newEvent returns an platform-independent Event based on an inotify mask.
306 func newEvent(name string, mask uint32) Event {
307 e := Event{Name: name}
308 if mask&syscall.IN_CREATE == syscall.IN_CREATE || mask&syscall.IN_MOVED_TO == syscall.IN_MOVED_TO {
311 if mask&syscall.IN_DELETE_SELF == syscall.IN_DELETE_SELF || mask&syscall.IN_DELETE == syscall.IN_DELETE {
314 if mask&syscall.IN_MODIFY == syscall.IN_MODIFY {
317 if mask&syscall.IN_MOVE_SELF == syscall.IN_MOVE_SELF || mask&syscall.IN_MOVED_FROM == syscall.IN_MOVED_FROM {
320 if mask&syscall.IN_ATTRIB == syscall.IN_ATTRIB {