123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306 |
- package fsnotify
- import (
- "errors"
- "fmt"
- "io"
- "os"
- "path/filepath"
- "strings"
- "sync"
- "syscall"
- "unsafe"
- )
- type Watcher struct {
- Events chan Event
- Errors chan error
- mu sync.Mutex
- fd int
- poller *fdPoller
- watches map[string]*watch
- paths map[int]string
- done chan struct{}
- doneResp chan struct{}
- }
- func NewWatcher() (*Watcher, error) {
-
- fd, errno := syscall.InotifyInit()
- if fd == -1 {
- return nil, errno
- }
-
- poller, err := newFdPoller(fd)
- if err != nil {
- syscall.Close(fd)
- return nil, err
- }
- w := &Watcher{
- fd: fd,
- poller: poller,
- watches: make(map[string]*watch),
- paths: make(map[int]string),
- Events: make(chan Event),
- Errors: make(chan error),
- done: make(chan struct{}),
- doneResp: make(chan struct{}),
- }
- go w.readEvents()
- return w, nil
- }
- func (w *Watcher) isClosed() bool {
- select {
- case <-w.done:
- return true
- default:
- return false
- }
- }
- func (w *Watcher) Close() error {
- if w.isClosed() {
- return nil
- }
-
- close(w.done)
-
- w.poller.wake()
-
- <-w.doneResp
- return nil
- }
- func (w *Watcher) Add(name string) error {
- name = filepath.Clean(name)
- if w.isClosed() {
- return errors.New("inotify instance already closed")
- }
- const agnosticEvents = syscall.IN_MOVED_TO | syscall.IN_MOVED_FROM |
- syscall.IN_CREATE | syscall.IN_ATTRIB | syscall.IN_MODIFY |
- syscall.IN_MOVE_SELF | syscall.IN_DELETE | syscall.IN_DELETE_SELF
- var flags uint32 = agnosticEvents
- w.mu.Lock()
- watchEntry, found := w.watches[name]
- w.mu.Unlock()
- if found {
- watchEntry.flags |= flags
- flags |= syscall.IN_MASK_ADD
- }
- wd, errno := syscall.InotifyAddWatch(w.fd, name, flags)
- if wd == -1 {
- return errno
- }
- w.mu.Lock()
- w.watches[name] = &watch{wd: uint32(wd), flags: flags}
- w.paths[wd] = name
- w.mu.Unlock()
- return nil
- }
- func (w *Watcher) Remove(name string) error {
- name = filepath.Clean(name)
-
- w.mu.Lock()
- defer w.mu.Unlock()
- watch, ok := w.watches[name]
-
- if !ok {
- return fmt.Errorf("can't remove non-existent inotify watch for: %s", name)
- }
-
-
-
- delete(w.watches, name)
- success, errno := syscall.InotifyRmWatch(w.fd, watch.wd)
- if success == -1 {
-
-
-
-
-
-
- return errno
- }
- return nil
- }
- type watch struct {
- wd uint32
- flags uint32
- }
- func (w *Watcher) readEvents() {
- var (
- buf [syscall.SizeofInotifyEvent * 4096]byte
- n int
- errno error
- ok bool
- )
- defer close(w.doneResp)
- defer close(w.Errors)
- defer close(w.Events)
- defer syscall.Close(w.fd)
- defer w.poller.close()
- for {
-
- if w.isClosed() {
- return
- }
- ok, errno = w.poller.wait()
- if errno != nil {
- select {
- case w.Errors <- errno:
- case <-w.done:
- return
- }
- continue
- }
- if !ok {
- continue
- }
- n, errno = syscall.Read(w.fd, buf[:])
-
-
-
- if errno == syscall.EINTR {
- continue
- }
-
- if w.isClosed() {
- return
- }
- if n < syscall.SizeofInotifyEvent {
- var err error
- if n == 0 {
-
- err = io.EOF
- } else if n < 0 {
-
- err = errno
- } else {
-
- err = errors.New("notify: short read in readEvents()")
- }
- select {
- case w.Errors <- err:
- case <-w.done:
- return
- }
- continue
- }
- var offset uint32
-
-
- for offset <= uint32(n-syscall.SizeofInotifyEvent) {
-
- raw := (*syscall.InotifyEvent)(unsafe.Pointer(&buf[offset]))
- mask := uint32(raw.Mask)
- nameLen := uint32(raw.Len)
-
-
-
-
- w.mu.Lock()
- name := w.paths[int(raw.Wd)]
- w.mu.Unlock()
- if nameLen > 0 {
-
- bytes := (*[syscall.PathMax]byte)(unsafe.Pointer(&buf[offset+syscall.SizeofInotifyEvent]))
-
- name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000")
- }
- event := newEvent(name, mask)
-
- if !event.ignoreLinux(mask) {
- select {
- case w.Events <- event:
- case <-w.done:
- return
- }
- }
-
- offset += syscall.SizeofInotifyEvent + nameLen
- }
- }
- }
- func (e *Event) ignoreLinux(mask uint32) bool {
-
- if mask&syscall.IN_IGNORED == syscall.IN_IGNORED {
- return true
- }
-
-
-
-
-
- if !(e.Op&Remove == Remove || e.Op&Rename == Rename) {
- _, statErr := os.Lstat(e.Name)
- return os.IsNotExist(statErr)
- }
- return false
- }
- func newEvent(name string, mask uint32) Event {
- e := Event{Name: name}
- if mask&syscall.IN_CREATE == syscall.IN_CREATE || mask&syscall.IN_MOVED_TO == syscall.IN_MOVED_TO {
- e.Op |= Create
- }
- if mask&syscall.IN_DELETE_SELF == syscall.IN_DELETE_SELF || mask&syscall.IN_DELETE == syscall.IN_DELETE {
- e.Op |= Remove
- }
- if mask&syscall.IN_MODIFY == syscall.IN_MODIFY {
- e.Op |= Write
- }
- if mask&syscall.IN_MOVE_SELF == syscall.IN_MOVE_SELF || mask&syscall.IN_MOVED_FROM == syscall.IN_MOVED_FROM {
- e.Op |= Rename
- }
- if mask&syscall.IN_ATTRIB == syscall.IN_ATTRIB {
- e.Op |= Chmod
- }
- return e
- }
|