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// +build freebsd openbsd netbsd dragonfly darwin
6
7package fsnotify
8
9import (
10	"errors"
11	"fmt"
12	"io/ioutil"
13	"os"
14	"path/filepath"
15	"sync"
16	"time"
17
18	"golang.org/x/sys/unix"
19)
20
21// Watcher watches a set of files, delivering events to a channel.
22type Watcher struct {
23	Events chan Event
24	Errors chan error
25	done   chan struct{} // Channel for sending a "quit message" to the reader goroutine
26
27	kq int // File descriptor (as returned by the kqueue() syscall).
28
29	mu              sync.Mutex        // Protects access to watcher data
30	watches         map[string]int    // Map of watched file descriptors (key: path).
31	externalWatches map[string]bool   // Map of watches added by user of the library.
32	dirFlags        map[string]uint32 // Map of watched directories to fflags used in kqueue.
33	paths           map[int]pathInfo  // Map file descriptors to path names for processing kqueue events.
34	fileExists      map[string]bool   // Keep track of if we know this file exists (to stop duplicate create events).
35	isClosed        bool              // Set to true when Close() is first called
36}
37
38type pathInfo struct {
39	name  string
40	isDir bool
41}
42
43// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events.
44func NewWatcher() (*Watcher, error) {
45	kq, err := kqueue()
46	if err != nil {
47		return nil, err
48	}
49
50	w := &Watcher{
51		kq:              kq,
52		watches:         make(map[string]int),
53		dirFlags:        make(map[string]uint32),
54		paths:           make(map[int]pathInfo),
55		fileExists:      make(map[string]bool),
56		externalWatches: make(map[string]bool),
57		Events:          make(chan Event),
58		Errors:          make(chan error),
59		done:            make(chan struct{}),
60	}
61
62	go w.readEvents()
63	return w, nil
64}
65
66// Close removes all watches and closes the events channel.
67func (w *Watcher) Close() error {
68	w.mu.Lock()
69	if w.isClosed {
70		w.mu.Unlock()
71		return nil
72	}
73	w.isClosed = true
74
75	// copy paths to remove while locked
76	var pathsToRemove = make([]string, 0, len(w.watches))
77	for name := range w.watches {
78		pathsToRemove = append(pathsToRemove, name)
79	}
80	w.mu.Unlock()
81	// unlock before calling Remove, which also locks
82
83	for _, name := range pathsToRemove {
84		w.Remove(name)
85	}
86
87	// send a "quit" message to the reader goroutine
88	close(w.done)
89
90	return nil
91}
92
93// Add starts watching the named file or directory (non-recursively).
94func (w *Watcher) Add(name string) error {
95	w.mu.Lock()
96	w.externalWatches[name] = true
97	w.mu.Unlock()
98	_, err := w.addWatch(name, noteAllEvents)
99	return err
100}
101
102// Remove stops watching the the named file or directory (non-recursively).
103func (w *Watcher) Remove(name string) error {
104	name = filepath.Clean(name)
105	w.mu.Lock()
106	watchfd, ok := w.watches[name]
107	w.mu.Unlock()
108	if !ok {
109		return fmt.Errorf("can't remove non-existent kevent watch for: %s", name)
110	}
111
112	const registerRemove = unix.EV_DELETE
113	if err := register(w.kq, []int{watchfd}, registerRemove, 0); err != nil {
114		return err
115	}
116
117	unix.Close(watchfd)
118
119	w.mu.Lock()
120	isDir := w.paths[watchfd].isDir
121	delete(w.watches, name)
122	delete(w.paths, watchfd)
123	delete(w.dirFlags, name)
124	w.mu.Unlock()
125
126	// Find all watched paths that are in this directory that are not external.
127	if isDir {
128		var pathsToRemove []string
129		w.mu.Lock()
130		for _, path := range w.paths {
131			wdir, _ := filepath.Split(path.name)
132			if filepath.Clean(wdir) == name {
133				if !w.externalWatches[path.name] {
134					pathsToRemove = append(pathsToRemove, path.name)
135				}
136			}
137		}
138		w.mu.Unlock()
139		for _, name := range pathsToRemove {
140			// Since these are internal, not much sense in propagating error
141			// to the user, as that will just confuse them with an error about
142			// a path they did not explicitly watch themselves.
143			w.Remove(name)
144		}
145	}
146
147	return nil
148}
149
150// Watch all events (except NOTE_EXTEND, NOTE_LINK, NOTE_REVOKE)
151const noteAllEvents = unix.NOTE_DELETE | unix.NOTE_WRITE | unix.NOTE_ATTRIB | unix.NOTE_RENAME
152
153// keventWaitTime to block on each read from kevent
154var keventWaitTime = durationToTimespec(100 * time.Millisecond)
155
156// addWatch adds name to the watched file set.
157// The flags are interpreted as described in kevent(2).
158// Returns the real path to the file which was added, if any, which may be different from the one passed in the case of symlinks.
159func (w *Watcher) addWatch(name string, flags uint32) (string, error) {
160	var isDir bool
161	// Make ./name and name equivalent
162	name = filepath.Clean(name)
163
164	w.mu.Lock()
165	if w.isClosed {
166		w.mu.Unlock()
167		return "", errors.New("kevent instance already closed")
168	}
169	watchfd, alreadyWatching := w.watches[name]
170	// We already have a watch, but we can still override flags.
171	if alreadyWatching {
172		isDir = w.paths[watchfd].isDir
173	}
174	w.mu.Unlock()
175
176	if !alreadyWatching {
177		fi, err := os.Lstat(name)
178		if err != nil {
179			return "", err
180		}
181
182		// Don't watch sockets.
183		if fi.Mode()&os.ModeSocket == os.ModeSocket {
184			return "", nil
185		}
186
187		// Don't watch named pipes.
188		if fi.Mode()&os.ModeNamedPipe == os.ModeNamedPipe {
189			return "", nil
190		}
191
192		// Follow Symlinks
193		// Unfortunately, Linux can add bogus symlinks to watch list without
194		// issue, and Windows can't do symlinks period (AFAIK). To  maintain
195		// consistency, we will act like everything is fine. There will simply
196		// be no file events for broken symlinks.
197		// Hence the returns of nil on errors.
198		if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
199			name, err = filepath.EvalSymlinks(name)
200			if err != nil {
201				return "", nil
202			}
203
204			w.mu.Lock()
205			_, alreadyWatching = w.watches[name]
206			w.mu.Unlock()
207
208			if alreadyWatching {
209				return name, nil
210			}
211
212			fi, err = os.Lstat(name)
213			if err != nil {
214				return "", nil
215			}
216		}
217
218		watchfd, err = unix.Open(name, openMode, 0700)
219		if watchfd == -1 {
220			return "", err
221		}
222
223		isDir = fi.IsDir()
224	}
225
226	const registerAdd = unix.EV_ADD | unix.EV_CLEAR | unix.EV_ENABLE
227	if err := register(w.kq, []int{watchfd}, registerAdd, flags); err != nil {
228		unix.Close(watchfd)
229		return "", err
230	}
231
232	if !alreadyWatching {
233		w.mu.Lock()
234		w.watches[name] = watchfd
235		w.paths[watchfd] = pathInfo{name: name, isDir: isDir}
236		w.mu.Unlock()
237	}
238
239	if isDir {
240		// Watch the directory if it has not been watched before,
241		// or if it was watched before, but perhaps only a NOTE_DELETE (watchDirectoryFiles)
242		w.mu.Lock()
243
244		watchDir := (flags&unix.NOTE_WRITE) == unix.NOTE_WRITE &&
245			(!alreadyWatching || (w.dirFlags[name]&unix.NOTE_WRITE) != unix.NOTE_WRITE)
246		// Store flags so this watch can be updated later
247		w.dirFlags[name] = flags
248		w.mu.Unlock()
249
250		if watchDir {
251			if err := w.watchDirectoryFiles(name); err != nil {
252				return "", err
253			}
254		}
255	}
256	return name, nil
257}
258
259// readEvents reads from kqueue and converts the received kevents into
260// Event values that it sends down the Events channel.
261func (w *Watcher) readEvents() {
262	eventBuffer := make([]unix.Kevent_t, 10)
263
264loop:
265	for {
266		// See if there is a message on the "done" channel
267		select {
268		case <-w.done:
269			break loop
270		default:
271		}
272
273		// Get new events
274		kevents, err := read(w.kq, eventBuffer, &keventWaitTime)
275		// EINTR is okay, the syscall was interrupted before timeout expired.
276		if err != nil && err != unix.EINTR {
277			select {
278			case w.Errors <- err:
279			case <-w.done:
280				break loop
281			}
282			continue
283		}
284
285		// Flush the events we received to the Events channel
286		for len(kevents) > 0 {
287			kevent := &kevents[0]
288			watchfd := int(kevent.Ident)
289			mask := uint32(kevent.Fflags)
290			w.mu.Lock()
291			path := w.paths[watchfd]
292			w.mu.Unlock()
293			event := newEvent(path.name, mask)
294
295			if path.isDir && !(event.Op&Remove == Remove) {
296				// Double check to make sure the directory exists. This can happen when
297				// we do a rm -fr on a recursively watched folders and we receive a
298				// modification event first but the folder has been deleted and later
299				// receive the delete event
300				if _, err := os.Lstat(event.Name); os.IsNotExist(err) {
301					// mark is as delete event
302					event.Op |= Remove
303				}
304			}
305
306			if event.Op&Rename == Rename || event.Op&Remove == Remove {
307				w.Remove(event.Name)
308				w.mu.Lock()
309				delete(w.fileExists, event.Name)
310				w.mu.Unlock()
311			}
312
313			if path.isDir && event.Op&Write == Write && !(event.Op&Remove == Remove) {
314				w.sendDirectoryChangeEvents(event.Name)
315			} else {
316				// Send the event on the Events channel.
317				select {
318				case w.Events <- event:
319				case <-w.done:
320					break loop
321				}
322			}
323
324			if event.Op&Remove == Remove {
325				// Look for a file that may have overwritten this.
326				// For example, mv f1 f2 will delete f2, then create f2.
327				if path.isDir {
328					fileDir := filepath.Clean(event.Name)
329					w.mu.Lock()
330					_, found := w.watches[fileDir]
331					w.mu.Unlock()
332					if found {
333						// make sure the directory exists before we watch for changes. When we
334						// do a recursive watch and perform rm -fr, the parent directory might
335						// have gone missing, ignore the missing directory and let the
336						// upcoming delete event remove the watch from the parent directory.
337						if _, err := os.Lstat(fileDir); err == nil {
338							w.sendDirectoryChangeEvents(fileDir)
339						}
340					}
341				} else {
342					filePath := filepath.Clean(event.Name)
343					if fileInfo, err := os.Lstat(filePath); err == nil {
344						w.sendFileCreatedEventIfNew(filePath, fileInfo)
345					}
346				}
347			}
348
349			// Move to next event
350			kevents = kevents[1:]
351		}
352	}
353
354	// cleanup
355	err := unix.Close(w.kq)
356	if err != nil {
357		// only way the previous loop breaks is if w.done was closed so we need to async send to w.Errors.
358		select {
359		case w.Errors <- err:
360		default:
361		}
362	}
363	close(w.Events)
364	close(w.Errors)
365}
366
367// newEvent returns an platform-independent Event based on kqueue Fflags.
368func newEvent(name string, mask uint32) Event {
369	e := Event{Name: name}
370	if mask&unix.NOTE_DELETE == unix.NOTE_DELETE {
371		e.Op |= Remove
372	}
373	if mask&unix.NOTE_WRITE == unix.NOTE_WRITE {
374		e.Op |= Write
375	}
376	if mask&unix.NOTE_RENAME == unix.NOTE_RENAME {
377		e.Op |= Rename
378	}
379	if mask&unix.NOTE_ATTRIB == unix.NOTE_ATTRIB {
380		e.Op |= Chmod
381	}
382	return e
383}
384
385func newCreateEvent(name string) Event {
386	return Event{Name: name, Op: Create}
387}
388
389// watchDirectoryFiles to mimic inotify when adding a watch on a directory
390func (w *Watcher) watchDirectoryFiles(dirPath string) error {
391	// Get all files
392	files, err := ioutil.ReadDir(dirPath)
393	if err != nil {
394		return err
395	}
396
397	for _, fileInfo := range files {
398		filePath := filepath.Join(dirPath, fileInfo.Name())
399		filePath, err = w.internalWatch(filePath, fileInfo)
400		if err != nil {
401			return err
402		}
403
404		w.mu.Lock()
405		w.fileExists[filePath] = true
406		w.mu.Unlock()
407	}
408
409	return nil
410}
411
412// sendDirectoryEvents searches the directory for newly created files
413// and sends them over the event channel. This functionality is to have
414// the BSD version of fsnotify match Linux inotify which provides a
415// create event for files created in a watched directory.
416func (w *Watcher) sendDirectoryChangeEvents(dirPath string) {
417	// Get all files
418	files, err := ioutil.ReadDir(dirPath)
419	if err != nil {
420		select {
421		case w.Errors <- err:
422		case <-w.done:
423			return
424		}
425	}
426
427	// Search for new files
428	for _, fileInfo := range files {
429		filePath := filepath.Join(dirPath, fileInfo.Name())
430		err := w.sendFileCreatedEventIfNew(filePath, fileInfo)
431
432		if err != nil {
433			return
434		}
435	}
436}
437
438// sendFileCreatedEvent sends a create event if the file isn't already being tracked.
439func (w *Watcher) sendFileCreatedEventIfNew(filePath string, fileInfo os.FileInfo) (err error) {
440	w.mu.Lock()
441	_, doesExist := w.fileExists[filePath]
442	w.mu.Unlock()
443	if !doesExist {
444		// Send create event
445		select {
446		case w.Events <- newCreateEvent(filePath):
447		case <-w.done:
448			return
449		}
450	}
451
452	// like watchDirectoryFiles (but without doing another ReadDir)
453	filePath, err = w.internalWatch(filePath, fileInfo)
454	if err != nil {
455		return err
456	}
457
458	w.mu.Lock()
459	w.fileExists[filePath] = true
460	w.mu.Unlock()
461
462	return nil
463}
464
465func (w *Watcher) internalWatch(name string, fileInfo os.FileInfo) (string, error) {
466	if fileInfo.IsDir() {
467		// mimic Linux providing delete events for subdirectories
468		// but preserve the flags used if currently watching subdirectory
469		w.mu.Lock()
470		flags := w.dirFlags[name]
471		w.mu.Unlock()
472
473		flags |= unix.NOTE_DELETE | unix.NOTE_RENAME
474		return w.addWatch(name, flags)
475	}
476
477	// watch file to mimic Linux inotify
478	return w.addWatch(name, noteAllEvents)
479}
480
481// kqueue creates a new kernel event queue and returns a descriptor.
482func kqueue() (kq int, err error) {
483	kq, err = unix.Kqueue()
484	if kq == -1 {
485		return kq, err
486	}
487	return kq, nil
488}
489
490// register events with the queue
491func register(kq int, fds []int, flags int, fflags uint32) error {
492	changes := make([]unix.Kevent_t, len(fds))
493
494	for i, fd := range fds {
495		// SetKevent converts int to the platform-specific types:
496		unix.SetKevent(&changes[i], fd, unix.EVFILT_VNODE, flags)
497		changes[i].Fflags = fflags
498	}
499
500	// register the events
501	success, err := unix.Kevent(kq, changes, nil, nil)
502	if success == -1 {
503		return err
504	}
505	return nil
506}
507
508// read retrieves pending events, or waits until an event occurs.
509// A timeout of nil blocks indefinitely, while 0 polls the queue.
510func read(kq int, events []unix.Kevent_t, timeout *unix.Timespec) ([]unix.Kevent_t, error) {
511	n, err := unix.Kevent(kq, nil, events, timeout)
512	if err != nil {
513		return nil, err
514	}
515	return events[0:n], nil
516}
517
518// durationToTimespec prepares a timeout value
519func durationToTimespec(d time.Duration) unix.Timespec {
520	return unix.NsecToTimespec(d.Nanoseconds())
521}
522