import "fmt"
-const (
- FSN_CREATE = 1
- FSN_MODIFY = 2
- FSN_DELETE = 4
- FSN_RENAME = 8
-
- FSN_ALL = FSN_MODIFY | FSN_DELETE | FSN_RENAME | FSN_CREATE
-)
-
-// Purge events from interal chan to external chan if passes filter
-func (w *Watcher) purgeEvents() {
- for ev := range w.internalEvent {
- sendEvent := false
- w.fsnmut.Lock()
- fsnFlags := w.fsnFlags[ev.Name]
- w.fsnmut.Unlock()
-
- if (fsnFlags&FSN_CREATE == FSN_CREATE) && ev.IsCreate() {
- sendEvent = true
- }
-
- if (fsnFlags&FSN_MODIFY == FSN_MODIFY) && ev.IsModify() {
- sendEvent = true
- }
-
- if (fsnFlags&FSN_DELETE == FSN_DELETE) && ev.IsDelete() {
- sendEvent = true
- }
-
- if (fsnFlags&FSN_RENAME == FSN_RENAME) && ev.IsRename() {
- sendEvent = true
- }
-
- if sendEvent {
- w.Event <- ev
- }
-
- // If there's no file, then no more events for user
- // BSD must keep watch for internal use (watches DELETEs to keep track
- // what files exist for create events)
- if ev.IsDelete() {
- w.fsnmut.Lock()
- delete(w.fsnFlags, ev.Name)
- w.fsnmut.Unlock()
- }
- }
-
- close(w.Event)
-}
-
// Watch a given file path
func (w *Watcher) Watch(path string) error {
- w.fsnmut.Lock()
- w.fsnFlags[path] = FSN_ALL
- w.fsnmut.Unlock()
- return w.watch(path)
-}
-
-// Watch a given file path for a particular set of notifications (FSN_MODIFY etc.)
-func (w *Watcher) WatchFlags(path string, flags uint32) error {
- w.fsnmut.Lock()
- w.fsnFlags[path] = flags
- w.fsnmut.Unlock()
return w.watch(path)
}
// Remove a watch on a file
func (w *Watcher) RemoveWatch(path string) error {
- w.fsnmut.Lock()
- delete(w.fsnFlags, path)
- w.fsnmut.Unlock()
return w.removeWatch(path)
}
kq int // File descriptor (as returned by the kqueue() syscall)
watches map[string]int // Map of watched file descriptors (key: path)
wmut sync.Mutex // Protects access to watches.
- fsnFlags map[string]uint32 // Map of watched files to flags used for filter
- fsnmut sync.Mutex // Protects access to fsnFlags.
enFlags map[string]uint32 // Map of watched files to evfilt note flags used in kqueue
enmut sync.Mutex // Protects access to enFlags.
paths map[int]string // Map of watched paths (key: watch descriptor)
externalWatches map[string]bool // Map of watches added by user of the library.
ewmut sync.Mutex // Protects access to externalWatches.
Error chan error // Errors are sent on this channel
- internalEvent chan *FileEvent // Events are queued on this channel
Event chan *FileEvent // Events are returned on this channel
done chan bool // Channel for sending a "quit message" to the reader goroutine
isClosed bool // Set to true when Close() is first called
w := &Watcher{
kq: fd,
watches: make(map[string]int),
- fsnFlags: make(map[string]uint32),
enFlags: make(map[string]uint32),
paths: make(map[int]string),
finfo: make(map[int]os.FileInfo),
fileExists: make(map[string]bool),
externalWatches: make(map[string]bool),
- internalEvent: make(chan *FileEvent),
Event: make(chan *FileEvent),
Error: make(chan error),
done: make(chan bool, 1),
}
go w.readEvents()
- go w.purgeEvents()
return w, nil
}
if errno != nil {
w.Error <- os.NewSyscallError("close", errno)
}
- close(w.internalEvent)
+ close(w.Event)
close(w.Error)
return
}
w.sendDirectoryChangeEvents(fileEvent.Name)
} else {
// Send the event on the events channel
- w.internalEvent <- fileEvent
+ w.Event <- fileEvent
}
// Move to next event
for _, fileInfo := range files {
filePath := filepath.Join(dirPath, fileInfo.Name())
- // Inherit fsnFlags from parent directory
- w.fsnmut.Lock()
- if flags, found := w.fsnFlags[dirPath]; found {
- w.fsnFlags[filePath] = flags
- } else {
- w.fsnFlags[filePath] = FSN_ALL
- }
- w.fsnmut.Unlock()
-
if fileInfo.IsDir() == false {
// Watch file to mimic linux fsnotify
e := w.addWatch(filePath, sys_NOTE_ALLEVENTS)
_, doesExist := w.fileExists[filePath]
w.femut.Unlock()
if !doesExist {
- // Inherit fsnFlags from parent directory
- w.fsnmut.Lock()
- if flags, found := w.fsnFlags[dirPath]; found {
- w.fsnFlags[filePath] = flags
- } else {
- w.fsnFlags[filePath] = FSN_ALL
- }
- w.fsnmut.Unlock()
-
// Send create event
fileEvent := new(FileEvent)
fileEvent.Name = filePath
fileEvent.create = true
- w.internalEvent <- fileEvent
+ w.Event <- fileEvent
}
w.femut.Lock()
w.fileExists[filePath] = true
}
type Watcher struct {
- mu sync.Mutex // Map access
- fd int // File descriptor (as returned by the inotify_init() syscall)
- watches map[string]*watch // Map of inotify watches (key: path)
- fsnFlags map[string]uint32 // Map of watched files to flags used for filter
- fsnmut sync.Mutex // Protects access to fsnFlags.
- paths map[int]string // Map of watched paths (key: watch descriptor)
- Error chan error // Errors are sent on this channel
- internalEvent chan *FileEvent // Events are queued on this channel
- Event chan *FileEvent // Events are returned on this channel
- done chan bool // Channel for sending a "quit message" to the reader goroutine
- isClosed bool // Set to true when Close() is first called
+ mu sync.Mutex // Map access
+ fd int // File descriptor (as returned by the inotify_init() syscall)
+ watches map[string]*watch // Map of inotify watches (key: path)
+ paths map[int]string // Map of watched paths (key: watch descriptor)
+ Error chan error // Errors are sent on this channel
+ Event chan *FileEvent // Events are returned on this channel
+ done chan bool // Channel for sending a "quit message" to the reader goroutine
+ isClosed bool // Set to true when Close() is first called
}
// NewWatcher creates and returns a new inotify instance using inotify_init(2)
return nil, os.NewSyscallError("inotify_init", errno)
}
w := &Watcher{
- fd: fd,
- watches: make(map[string]*watch),
- fsnFlags: make(map[string]uint32),
- paths: make(map[int]string),
- internalEvent: make(chan *FileEvent),
- Event: make(chan *FileEvent),
- Error: make(chan error),
- done: make(chan bool, 1),
+ fd: fd,
+ watches: make(map[string]*watch),
+ paths: make(map[int]string),
+ Event: make(chan *FileEvent),
+ Error: make(chan error),
+ done: make(chan bool, 1),
}
go w.readEvents()
- go w.purgeEvents()
return w, nil
}
select {
case <-w.done:
syscall.Close(w.fd)
- close(w.internalEvent)
+ close(w.Event)
close(w.Error)
return
default:
// If EOF is received
if n == 0 {
syscall.Close(w.fd)
- close(w.internalEvent)
+ close(w.Event)
close(w.Error)
return
}
w.mu.Lock()
event.Name = w.paths[int(raw.Wd)]
w.mu.Unlock()
- watchedName := event.Name
if nameLen > 0 {
// Point "bytes" at the first byte of the filename
bytes := (*[syscall.PathMax]byte)(unsafe.Pointer(&buf[offset+syscall.SizeofInotifyEvent]))
// Send the events that are not ignored on the events channel
if !event.ignoreLinux() {
- // Setup FSNotify flags (inherit from directory watch)
- w.fsnmut.Lock()
- if _, fsnFound := w.fsnFlags[event.Name]; !fsnFound {
- if fsnFlags, watchFound := w.fsnFlags[watchedName]; watchFound {
- w.fsnFlags[event.Name] = fsnFlags
- } else {
- w.fsnFlags[event.Name] = FSN_ALL
- }
- }
- w.fsnmut.Unlock()
-
- w.internalEvent <- event
+ w.Event <- event
}
// Move to the next event in the buffer
// A Watcher waits for and receives event notifications
// for a specific set of files and directories.
type Watcher struct {
- mu sync.Mutex // Map access
- port syscall.Handle // Handle to completion port
- watches watchMap // Map of watches (key: i-number)
- fsnFlags map[string]uint32 // Map of watched files to flags used for filter
- fsnmut sync.Mutex // Protects access to fsnFlags.
- input chan *input // Inputs to the reader are sent on this channel
- internalEvent chan *FileEvent // Events are queued on this channel
- Event chan *FileEvent // Events are returned on this channel
- Error chan error // Errors are sent on this channel
- isClosed bool // Set to true when Close() is first called
- quit chan chan<- error
- cookie uint32
+ mu sync.Mutex // Map access
+ port syscall.Handle // Handle to completion port
+ watches watchMap // Map of watches (key: i-number)
+ input chan *input // Inputs to the reader are sent on this channel
+ Event chan *FileEvent // Events are returned on this channel
+ Error chan error // Errors are sent on this channel
+ isClosed bool // Set to true when Close() is first called
+ quit chan chan<- error
+ cookie uint32
}
// NewWatcher creates and returns a Watcher.
return nil, os.NewSyscallError("CreateIoCompletionPort", e)
}
w := &Watcher{
- port: port,
- watches: make(watchMap),
- fsnFlags: make(map[string]uint32),
- input: make(chan *input, 1),
- Event: make(chan *FileEvent, 50),
- internalEvent: make(chan *FileEvent),
- Error: make(chan error),
- quit: make(chan chan<- error, 1),
+ port: port,
+ watches: make(watchMap),
+ input: make(chan *input, 1),
+ Event: make(chan *FileEvent, 50),
+ Error: make(chan error),
+ quit: make(chan chan<- error, 1),
}
go w.readEvents()
- go w.purgeEvents()
return w, nil
}
if e := syscall.CloseHandle(w.port); e != nil {
err = os.NewSyscallError("CloseHandle", e)
}
- close(w.internalEvent)
+ close(w.Event)
close(w.Error)
ch <- err
return
var offset uint32
for {
if n == 0 {
- w.internalEvent <- &FileEvent{mask: sys_FS_Q_OVERFLOW}
+ w.Event <- &FileEvent{mask: sys_FS_Q_OVERFLOW}
w.Error <- errors.New("short read in readEvents()")
break
}