func (this *watcher) doDel(path string) error { info, found := this.watchedByPath[path] if !found { return errors.Newf("can't remove non-existent kevent watch for: %s", path) } var kbuf [1]syscall.Kevent_t watchEntry := &kbuf[0] syscall.SetKevent(watchEntry, info.fd, syscall.EVFILT_VNODE, syscall.EV_DELETE) entryFlags := watchEntry.Flags success, errno := syscall.Kevent(this.kq, kbuf[:], nil, nil) if success == sysCallFailed { return os.NewSyscallError("kevent_rm_watch", errno) } else if entryFlags&syscall.EV_ERROR == syscall.EV_ERROR { return errors.New("kevent rm error") } syscall.Close(info.fd) //Remove childs if it's directory for _, child := range info.childes { this.doDel(child) } delete(this.watchedByPath, path) delete(this.watchedByFD, info.fd) return nil }
// unwatch stops watching p file/directory. func (k *kqueue) singleunwatch(p string, direct bool) error { w := k.pthLkp[p] if w == nil { return errNotWatched } if direct { w.eDir = 0 } else { w.eNonDir = 0 } var kevn [1]syscall.Kevent_t syscall.SetKevent(&kevn[0], w.fd, syscall.EVFILT_VNODE, syscall.EV_DELETE) if _, err := syscall.Kevent(k.fd, kevn[:], nil, nil); err != nil { return err } if w.eNonDir&w.eDir != 0 { if err := k.singlewatch(p, w.eNonDir|w.eDir, w.eNonDir == 0, w.fi); err != nil { return err } } else { k.del(*w) } return nil }
// AddWatch adds path to the watched file set. // The flags are interpreted as described in kevent(2). func (w *Watcher) addWatch(path string, flags uint32) error { if w.isClosed { return errors.New("kevent instance already closed") } watchEntry := &w.kbuf[0] watchEntry.Fflags = flags watchfd, found := w.watches[path] if !found { fd, errno := syscall.Open(path, syscall.O_NONBLOCK|syscall.O_RDONLY, 0700) if fd == -1 { return &os.PathError{"kevent_add_watch", path, errno} } watchfd = fd w.watches[path] = watchfd w.paths[watchfd] = path fi, _ := os.Stat(path) w.finfo[watchfd] = &fi } syscall.SetKevent(watchEntry, watchfd, syscall.EVFILT_VNODE, syscall.EV_ADD|syscall.EV_CLEAR) wd, errno := syscall.Kevent(w.kq, w.kbuf[:], nil, nil) if wd == -1 { return &os.PathError{"kevent_add_watch", path, errno} } else if (watchEntry.Flags & syscall.EV_ERROR) == syscall.EV_ERROR { return &os.PathError{"kevent_add_watch", path, errors.New(fmt.Sprintf("%v", watchEntry.Data))} } return nil }
// Unwatch implements trigger. func (k *kq) Unwatch(w *watched) (err error) { var kevn [1]syscall.Kevent_t syscall.SetKevent(&kevn[0], w.fd, syscall.EVFILT_VNODE, syscall.EV_DELETE) _, err = syscall.Kevent(k.fd, kevn[:], nil, nil) return }
// RemoveWatch removes path from the watched file set. func (w *Watcher) removeWatch(path string) error { w.wmut.Lock() watchfd, ok := w.watches[path] w.wmut.Unlock() if !ok { return errors.New(fmt.Sprintf("can't remove non-existent kevent watch for: %s", path)) } w.bufmut.Lock() defer w.bufmut.Unlock() watchEntry := &w.kbuf[0] syscall.SetKevent(watchEntry, watchfd, syscall.EVFILT_VNODE, syscall.EV_DELETE) success, errno := syscall.Kevent(w.kq, w.kbuf[:], nil, nil) if success == -1 { return os.NewSyscallError("kevent_rm_watch", errno) } else if (watchEntry.Flags & syscall.EV_ERROR) == syscall.EV_ERROR { return errors.New("kevent rm error") } syscall.Close(watchfd) w.wmut.Lock() delete(w.watches, path) w.wmut.Unlock() w.enmut.Lock() delete(w.enFlags, path) w.enmut.Unlock() w.pmut.Lock() delete(w.paths, watchfd) delete(w.finfo, watchfd) w.pmut.Unlock() return nil }
// First return value is whether the pollServer should be woken up. // This version always returns false. func (p *pollster) AddFD(fd int, mode int, repeat bool) (bool, error) { // pollServer is locked. var kmode int if mode == 'r' { kmode = syscall.EVFILT_READ } else { kmode = syscall.EVFILT_WRITE } ev := &p.kbuf[0] // EV_ADD - add event to kqueue list // EV_ONESHOT - delete the event the first time it triggers flags := syscall.EV_ADD if !repeat { flags |= syscall.EV_ONESHOT } syscall.SetKevent(ev, fd, kmode, flags) n, err := syscall.Kevent(p.kq, p.kbuf[:], nil, nil) if err != nil { return false, os.NewSyscallError("kevent", err) } if n != 1 || (ev.Flags&syscall.EV_ERROR) == 0 || int(ev.Ident) != fd || int(ev.Filter) != kmode { return false, os.NewSyscallError("kqueue phase error", err) } if ev.Data != 0 { return false, syscall.Errno(int(ev.Data)) } return false, nil }
// watch starts to watch given p file/directory. func (k *kqueue) singlewatch(p string, e Event, direct bool, fi os.FileInfo) error { w, ok := k.pthLkp[p] if !ok { fd, err := syscall.Open(p, syscall.O_NONBLOCK|syscall.O_RDONLY, 0) if err != nil { return err } w = &watched{fd: fd, p: p, fi: fi} } if direct { w.eDir |= e } else { w.eNonDir |= e } var kevn [1]syscall.Kevent_t syscall.SetKevent(&kevn[0], w.fd, syscall.EVFILT_VNODE, syscall.EV_ADD|syscall.EV_CLEAR) kevn[0].Fflags = encode(w.eDir | w.eNonDir) if _, err := syscall.Kevent(k.fd, kevn[:], nil, nil); err != nil { return err } if !ok { k.idLkp[w.fd], k.pthLkp[w.p] = w, w return nil } return errAlreadyWatched }
func (p *pollster) AddFD(fd int, mode int, repeat bool) os.Error { var kmode int if mode == 'r' { kmode = syscall.EVFILT_READ } else { kmode = syscall.EVFILT_WRITE } var events [1]syscall.Kevent_t ev := &events[0] // EV_ADD - add event to kqueue list // EV_RECEIPT - generate fake EV_ERROR as result of add, // rather than waiting for real event // EV_ONESHOT - delete the event the first time it triggers flags := syscall.EV_ADD | syscall.EV_RECEIPT if !repeat { flags |= syscall.EV_ONESHOT } syscall.SetKevent(ev, fd, kmode, flags) n, e := syscall.Kevent(p.kq, &events, &events, nil) if e != 0 { return os.NewSyscallError("kevent", e) } if n != 1 || (ev.Flags&syscall.EV_ERROR) == 0 || int(ev.Ident) != fd || int(ev.Filter) != kmode { return os.ErrorString("kqueue phase error") } if ev.Data != 0 { return os.Errno(int(ev.Data)) } return nil }
// Watch implements trigger. func (k *kq) Watch(fi os.FileInfo, w *watched, e int64) (err error) { var kevn [1]syscall.Kevent_t syscall.SetKevent(&kevn[0], w.fd, syscall.EVFILT_VNODE, syscall.EV_ADD|syscall.EV_CLEAR) kevn[0].Fflags = uint32(e) _, err = syscall.Kevent(k.fd, kevn[:], nil, nil) return }
// Remove stops watching the the named file or directory (non-recursively). func (w *Watcher) Remove(name string) error { name = filepath.Clean(name) w.wmut.Lock() watchfd, ok := w.watches[name] w.wmut.Unlock() if !ok { return fmt.Errorf("can't remove non-existent kevent watch for: %s", name) } var kbuf [1]syscall.Kevent_t watchEntry := &kbuf[0] syscall.SetKevent(watchEntry, watchfd, syscall.EVFILT_VNODE, syscall.EV_DELETE) entryFlags := watchEntry.Flags success, errno := syscall.Kevent(w.kq, kbuf[:], nil, nil) if success == -1 { return os.NewSyscallError("kevent_rm_watch", errno) } else if (entryFlags & syscall.EV_ERROR) == syscall.EV_ERROR { return errors.New("kevent rm error") } syscall.Close(watchfd) w.wmut.Lock() delete(w.watches, name) w.wmut.Unlock() w.enmut.Lock() delete(w.enFlags, name) w.enmut.Unlock() w.pmut.Lock() delete(w.paths, watchfd) fInfo := w.finfo[watchfd] delete(w.finfo, watchfd) w.pmut.Unlock() // Find all watched paths that are in this directory that are not external. if fInfo.IsDir() { var pathsToRemove []string w.pmut.Lock() for _, wpath := range w.paths { wdir, _ := filepath.Split(wpath) if filepath.Clean(wdir) == filepath.Clean(name) { w.ewmut.Lock() if !w.externalWatches[wpath] { pathsToRemove = append(pathsToRemove, wpath) } w.ewmut.Unlock() } } w.pmut.Unlock() for _, name := range pathsToRemove { // Since these are internal, not much sense in propagating error // to the user, as that will just confuse them with an error about // a path they did not explicitly watch themselves. w.Remove(name) } } return nil }
// RemoveWatch removes path from the watched file set. func (w *Watcher) removeWatch(path string) error { w.wmut.Lock() watchfd, ok := w.watches[path] w.wmut.Unlock() if !ok { return errors.New(fmt.Sprintf("can't remove non-existent kevent watch for: %s", path)) } w.bufmut.Lock() watchEntry := &w.kbuf[0] syscall.SetKevent(watchEntry, watchfd, syscall.EVFILT_VNODE, syscall.EV_DELETE) success, errno := syscall.Kevent(w.kq, w.kbuf[:], nil, nil) w.bufmut.Unlock() if success == -1 { return os.NewSyscallError("kevent_rm_watch", errno) } else if (watchEntry.Flags & syscall.EV_ERROR) == syscall.EV_ERROR { return errors.New("kevent rm error") } syscall.Close(watchfd) w.wmut.Lock() delete(w.watches, path) w.wmut.Unlock() w.enmut.Lock() delete(w.enFlags, path) w.enmut.Unlock() w.pmut.Lock() delete(w.paths, watchfd) fInfo := w.finfo[watchfd] delete(w.finfo, watchfd) w.pmut.Unlock() // Find all watched paths that are in this directory that are not external. if fInfo.IsDir() { pathsToRemove := make([]string, 0) w.pmut.Lock() for _, wpath := range w.paths { wdir, _ := filepath.Split(wpath) if filepath.Clean(wdir) == filepath.Clean(path) { w.ewmut.Lock() if _, extern := w.externalWatches[wpath]; !extern { pathsToRemove = append(pathsToRemove, wpath) } w.ewmut.Unlock() } } w.pmut.Unlock() for idx := 0; idx < len(pathsToRemove); idx++ { // Since these are internal, not much sense in propogating error // to the user, as that will just confuse them with an error about // a path they did not explicitly watch themselves. w.removeWatch(pathsToRemove[idx]) } } return nil }
// Initialize Kevent_t fields and propagate changelist for the given pid func (w *Watcher) kevent(pid int, fflags uint32, flags int) error { listener, _ := w.listener.(*kqueueListener) event := &listener.buf[0] syscall.SetKevent(event, pid, syscall.EVFILT_PROC, flags) event.Fflags = fflags _, err := syscall.Kevent(listener.kq, listener.buf[:], nil, nil) return err }
func (p *pollster) DelFD(fd int, mode int) { var kmode int if mode == 'r' { kmode = syscall.EVFILT_READ } else { kmode = syscall.EVFILT_WRITE } var events [1]syscall.Kevent_t ev := &events[0] // EV_DELETE - delete event from kqueue list syscall.SetKevent(ev, fd, kmode, syscall.EV_DELETE) syscall.Kevent(p.kq, &events, nil, nil) }
// init initializes kqueue. func (k *kqueue) init() (err error) { if k.fd, err = syscall.Kqueue(); err != nil { return } // Creates pipe used to stop `Kevent` call by registering it, // watching read end and writing to other end of it. if err = syscall.Pipe(k.pipefds[:]); err != nil { return } var kevn [1]syscall.Kevent_t syscall.SetKevent(&kevn[0], k.pipefds[0], syscall.EVFILT_READ, syscall.EV_ADD) _, err = syscall.Kevent(k.fd, kevn[:], nil, nil) return }
func (p *pollster) DelFD(fd int, mode int) { // pollServer is locked. var kmode int if mode == 'r' { kmode = syscall.EVFILT_READ } else { kmode = syscall.EVFILT_WRITE } ev := &p.kbuf[0] // EV_DELETE - delete event from kqueue list syscall.SetKevent(ev, fd, kmode, syscall.EV_DELETE) syscall.Kevent(p.kq, p.kbuf[:], nil, nil) }
func (p *pollster) DelFD(fd int, mode int) { var kmode int if mode == 'r' { kmode = syscall.EVFILT_READ } else { kmode = syscall.EVFILT_WRITE } var events [1]syscall.Kevent_t ev := &events[0] // EV_DELETE - delete event from kqueue list // EV_RECEIPT - generate fake EV_ERROR as result of add, // rather than waiting for real event syscall.SetKevent(ev, fd, kmode, syscall.EV_DELETE|syscall.EV_RECEIPT) syscall.Kevent(p.kq, &events, &events, nil) }
func (p *pollster) DelFD(fd int, mode int) { // pollServer is locked. var kmode int if mode == 'r' { kmode = syscall.EVFILT_READ } else { kmode = syscall.EVFILT_WRITE } ev := &p.kbuf[0] // EV_DELETE - delete event from kqueue list // EV_RECEIPT - generate fake EV_ERROR as result of add, // rather than waiting for real event syscall.SetKevent(ev, fd, kmode, syscall.EV_DELETE|syscall.EV_RECEIPT) syscall.Kevent(p.kq, p.kbuf[0:], p.kbuf[0:], nil) }
// register events with the queue func register(kq int, fds []int, flags int, fflags uint32) error { changes := make([]syscall.Kevent_t, len(fds)) for i, fd := range fds { // SetKevent converts int to the platform-specific types: syscall.SetKevent(&changes[i], fd, syscall.EVFILT_VNODE, flags) changes[i].Fflags = fflags } // register the events success, err := syscall.Kevent(kq, changes, nil, nil) if success == -1 { return err } return nil }
// RemoveWatch removes path from the watched file set. func (w *Watcher) RemoveWatch(path string) error { watchfd, ok := w.watches[path] if !ok { return errors.New(fmt.Sprintf("can't remove non-existent kevent watch for: %s", path)) } syscall.Close(watchfd) watchEntry := &w.kbuf[0] syscall.SetKevent(watchEntry, w.watches[path], syscall.EVFILT_VNODE, syscall.EV_DELETE) success, errno := syscall.Kevent(w.kq, w.kbuf[:], nil, nil) if success == -1 { return os.NewSyscallError("kevent_rm_watch", errno) } else if (watchEntry.Flags & syscall.EV_ERROR) == syscall.EV_ERROR { return os.NewSyscallError("kevent_rm_watch", errors.New(fmt.Sprintf("%v", watchEntry.Data))) } delete(w.watches, path) return nil }
// AddWatch adds path to the watched file set. // The flags are interpreted as described in kevent(2). func (w *Watcher) addWatch(path string, flags uint32) error { if w.isClosed { return errors.New("kevent instance already closed") } watchEntry := &w.kbuf[0] watchEntry.Fflags = flags watchfd, found := w.watches[path] if !found { fd, errno := syscall.Open(path, syscall.O_NONBLOCK|syscall.O_RDONLY, 0700) if fd == -1 { return errno } watchfd = fd w.watches[path] = watchfd w.paths[watchfd] = path fi, _ := os.Stat(path) w.finfo[watchfd] = fi if fi.IsDir() { w.watchDirectoryFiles(path) } } syscall.SetKevent(watchEntry, watchfd, syscall.EVFILT_VNODE, syscall.EV_ADD|syscall.EV_CLEAR) wd, errno := syscall.Kevent(w.kq, w.kbuf[:], nil, nil) if wd == -1 { return errno } else if (watchEntry.Flags & syscall.EV_ERROR) == syscall.EV_ERROR { return errors.New("kevent add error") } return nil }
// AddWatch adds path to the watched file set. // The flags are interpreted as described in kevent(2). func (w *Watcher) addWatch(path string, flags uint32) error { if w.isClosed { return errors.New("kevent instance already closed") } watchEntry := &w.kbuf[0] watchEntry.Fflags = flags watchDir := false watchfd, found := w.watches[path] if !found { fi, errstat := os.Lstat(path) if errstat != nil { return errstat } // Follow Symlinks // Unfortunately, Linux can add bogus symlinks to watch list without // issue, and Windows can't do symlinks period (AFAIK). To maintain // consistency, we will act like everything is fine. There will simply // be no file events for broken symlinks. // Hence the returns of nil on errors. if fi.Mode()&os.ModeSymlink == os.ModeSymlink { path, err := filepath.EvalSymlinks(path) if err != nil { return nil } fi, errstat = os.Lstat(path) if errstat != nil { return nil } } fd, errno := syscall.Open(path, syscall.O_NONBLOCK|syscall.O_RDONLY, 0700) if fd == -1 { return errno } watchfd = fd w.watches[path] = watchfd w.paths[watchfd] = path w.finfo[watchfd] = fi } if w.finfo[watchfd].IsDir() && (flags&NOTE_WRITE) == NOTE_WRITE { watchDir = true } w.enFlags[path] = watchEntry.Fflags syscall.SetKevent(watchEntry, watchfd, syscall.EVFILT_VNODE, syscall.EV_ADD|syscall.EV_CLEAR) wd, errno := syscall.Kevent(w.kq, w.kbuf[:], nil, nil) if wd == -1 { return errno } else if (watchEntry.Flags & syscall.EV_ERROR) == syscall.EV_ERROR { return errors.New("kevent add error") } if watchDir { errdir := w.watchDirectoryFiles(path) if errdir != nil { return errdir } } return nil }
func (this *watcher) doAdd(path string, flags uint32) error { //Check that file exists fileInfo, err := os.Lstat(path) if err != nil { return err } mode := fileInfo.Mode() // don't watch socket if mode&os.ModeSocket == os.ModeSocket { return nil } if mode&os.ModeSymlink == os.ModeSymlink { if path, err = filepath.EvalSymlinks(path); err != nil { return err } if fileInfo, err = os.Lstat(path); err != nil { return err } } fd, err := syscall.Open(path, open_FLAGS, 0700) if fd == sysCallFailed { return err } info := &watchInfo{ fd: fd, flags: flags, realPath: path, fInto: fileInfo, } this.watchedByFD[fd] = info this.watchedByPath[path] = info var kbuf [1]syscall.Kevent_t watchEntry := &kbuf[0] watchEntry.Fflags = flags syscall.SetKevent(watchEntry, info.fd, syscall.EVFILT_VNODE, syscall.EV_ADD|syscall.EV_CLEAR) entryFlags := watchEntry.Flags if success, err := syscall.Kevent(this.kq, kbuf[:], nil, nil); success == -1 { return err } else if (entryFlags & syscall.EV_ERROR) == syscall.EV_ERROR { return errors.New("kevent add error") } if (true == info.fInto.IsDir()) && (info.flags&Note_WRITE == Note_WRITE) { if infos, err := ioutil.ReadDir(path); err != nil { return err } else { for _, fileInfo := range infos { childPath := filepath.Join(path, fileInfo.Name()) if err := this.doAdd(childPath, flags); err != nil { return err } info.childes = append(info.childes, childPath) } } } return nil }
// AddWatch adds path to the watched file set. // The flags are interpreted as described in kevent(2). func (w *Watcher) addWatch(path string, flags uint32) error { w.mu.Lock() if w.isClosed { w.mu.Unlock() return errors.New("kevent instance already closed") } w.mu.Unlock() watchDir := false w.wmut.Lock() watchfd, found := w.watches[path] w.wmut.Unlock() if !found { fi, errstat := os.Lstat(path) if errstat != nil { return errstat } // don't watch socket if fi.Mode()&os.ModeSocket == os.ModeSocket { return nil } // Follow Symlinks // Unfortunately, Linux can add bogus symlinks to watch list without // issue, and Windows can't do symlinks period (AFAIK). To maintain // consistency, we will act like everything is fine. There will simply // be no file events for broken symlinks. // Hence the returns of nil on errors. if fi.Mode()&os.ModeSymlink == os.ModeSymlink { path, err := filepath.EvalSymlinks(path) if err != nil { return nil } fi, errstat = os.Lstat(path) if errstat != nil { return nil } } fd, errno := syscall.Open(path, open_FLAGS, 0700) if fd == -1 { return errno } watchfd = fd w.wmut.Lock() w.watches[path] = watchfd w.wmut.Unlock() w.pmut.Lock() w.paths[watchfd] = path w.finfo[watchfd] = fi w.pmut.Unlock() } // Watch the directory if it has not been watched before. w.pmut.Lock() w.enmut.Lock() if w.finfo[watchfd].IsDir() && (flags&sys_NOTE_WRITE) == sys_NOTE_WRITE && (!found || (w.enFlags[path]&sys_NOTE_WRITE) != sys_NOTE_WRITE) { watchDir = true } w.enmut.Unlock() w.pmut.Unlock() w.enmut.Lock() w.enFlags[path] = flags w.enmut.Unlock() w.bufmut.Lock() watchEntry := &w.kbuf[0] watchEntry.Fflags = flags syscall.SetKevent(watchEntry, watchfd, syscall.EVFILT_VNODE, syscall.EV_ADD|syscall.EV_CLEAR) entryFlags := watchEntry.Flags w.bufmut.Unlock() wd, errno := syscall.Kevent(w.kq, w.kbuf[:], nil, nil) if wd == -1 { return errno } else if (entryFlags & syscall.EV_ERROR) == syscall.EV_ERROR { return errors.New("kevent add error") } if watchDir { errdir := w.watchDirectoryFiles(path) if errdir != nil { return errdir } } return nil }