// 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 }
// 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 (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 }
func (p *pollster) WaitFD(s *pollServer, nsec int64) (fd int, mode int, err error) { var t *syscall.Timespec for len(p.events) == 0 { if nsec > 0 { if t == nil { t = new(syscall.Timespec) } *t = syscall.NsecToTimespec(nsec) } s.Unlock() n, err := syscall.Kevent(p.kq, nil, p.eventbuf[:], t) s.Lock() if err != nil { if err == syscall.EINTR { continue } return -1, 0, os.NewSyscallError("kevent", err) } if n == 0 { return -1, 0, nil } p.events = p.eventbuf[:n] } ev := &p.events[0] p.events = p.events[1:] fd = int(ev.Ident) if ev.Filter == syscall.EVFILT_READ { mode = 'r' } else { mode = 'w' } return fd, mode, nil }
func runKqueue() { fd, err := syscall.Kqueue() evTrackList := make([]syscall.Kevent_t, 1024) if err != nil { log.Fatal("error initializing Kqueue: ", err) return } addFilesToKqueue(evTrackList, path) // configure timeout timeout := syscall.Timespec{ Sec: 0, Nsec: 0, } // wait for events for { // create kevent events := make([]syscall.Kevent_t, 10) _, err := syscall.Kevent(fd, evTrackList, events, &timeout) if err != nil { log.Println("Error creating kevent") } // check if there was an event and process it if len(events) > 0 && events[0].Ident > 0 { processEvent() } } }
func (p *pollster) Kevent(nsec int64) (result int, events []net_event, err error) { var t *syscall.Timespec if nsec > 0 { if t == nil { t = new(syscall.Timespec) } *t = syscall.NsecToTimespec(nsec) } n, err := syscall.Kevent(p.kq, nil, p.eventbuf[:], t) if err != nil { if err == syscall.EINTR { return 0, nil, nil } else { return -1, nil, os.NewSyscallError("kevent", err) } } if n == 0 { return -2, nil, nil } events = make([]net_event, n) for i, ev := range p.eventbuf { events[i].fd = int(ev.Ident) events[i].data = int(ev.Data) if ev.Filter == syscall.EVFILT_READ { events[i].mode = 'r' } else { events[i].mode = 'w' } } return n, events, nil }
// Poll the kqueue file descriptor and dispatch to the Event channels func (w *Watcher) readEvents() { listener, _ := w.listener.(*kqueueListener) events := make([]syscall.Kevent_t, 10) for { if w.isDone() { return } n, err := syscall.Kevent(listener.kq, nil, events, nil) if err != nil { w.Error <- err continue } for _, ev := range events[:n] { pid := int(ev.Ident) switch ev.Fflags { case syscall.NOTE_FORK: w.Fork <- &ProcEventFork{ParentPid: pid} case syscall.NOTE_EXEC: w.Exec <- &ProcEventExec{Pid: pid} case syscall.NOTE_EXIT: w.RemoveWatch(pid) w.Exit <- &ProcEventExit{Pid: pid} } } } }
func (p *pollster) WaitFD(nsec int64) (fd int, mode int, err os.Error) { var t *syscall.Timespec for len(p.events) == 0 { if nsec > 0 { if t == nil { t = new(syscall.Timespec) } *t = syscall.NsecToTimespec(nsec) } nn, e := syscall.Kevent(p.kq, nil, &p.eventbuf, t) if e != 0 { if e == syscall.EINTR { continue } return -1, 0, os.NewSyscallError("kevent", e) } if nn == 0 { return -1, 0, nil } p.events = p.eventbuf[0:nn] } ev := &p.events[0] p.events = p.events[1:len(p.events)] fd = int(ev.Ident) if ev.Filter == syscall.EVFILT_READ { mode = 'r' } else { mode = 'w' } return fd, mode, nil }
func (k *Kqueue) Wait(buf []syscall.Kevent_t) (int, error) { n, err := syscall.Kevent(k.Fd, nil, buf, nil) if err != nil { return 0, os.NewSyscallError("kevent", err) } return n, nil }
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 }
// read retrieves pending events, or waits until an event occurs. // A timeout of nil blocks indefinitely, while 0 polls the queue. func read(kq int, events []syscall.Kevent_t, timeout *syscall.Timespec) ([]syscall.Kevent_t, error) { n, err := syscall.Kevent(kq, nil, events, timeout) if err != nil { return nil, err } return events[0:n], 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 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 }
// 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 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 }
// Wait implements trigger. func (k *kq) Wait() (interface{}, error) { var ( kevn [1]syscall.Kevent_t err error ) kevn[0] = syscall.Kevent_t{} _, err = syscall.Kevent(k.fd, nil, kevn[:], nil) return kevn[0], err }
// 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 }
func (k *Kqueue) Add(fd uintptr, filter, flags int, fflags uint32) error { var buf [1]syscall.Kevent_t ev := &buf[0] flags = flags | syscall.EV_ADD evset(ev, fd, filter, flags, fflags) _, err := syscall.Kevent(k.Fd, buf[:], nil, nil) if err != nil { return os.NewSyscallError("kevent", err) } 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 }
func (k *kqueue) add(fd uintptr, filter, flags int, fflags uint32) error { var buf [1]syscall.Kevent_t ev := &buf[0] flags = flags | syscall.EV_ADD | syscall.EV_RECEIPT evset(ev, fd, filter, flags, fflags) n, err := syscall.Kevent(k.fd, buf[:], buf[:], nil) if err != nil { return os.NewSyscallError("kevent", err) } if n != 1 || (ev.Flags&syscall.EV_ERROR) == 0 || uintptr(ev.Ident) != fd || int(ev.Filter) != filter { return errors.New("kqueue phase error") } if ev.Data != 0 { return syscall.Errno(ev.Data) } 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 }
// monitor reads reported kqueue events and forwards them further after // performing additional processing. If read event concerns directory, // it generates Create/Remove event and sent them further instead of directory // event. This event is detected based on reading contents of analyzed // directory. If no changes in file list are detected, no event is send further. // Reading directory structure is less accurate than kqueue and can lead // to lack of detection of all events. func (k *kqueue) monitor() { var ( kevn [1]syscall.Kevent_t n int err error ) for { kevn[0] = syscall.Kevent_t{} switch n, err = syscall.Kevent(k.fd, nil, kevn[:], nil); { case err == syscall.EINTR: case err != nil: dbgprintf("kqueue: failed to read events: %q\n", err) case int(kevn[0].Ident) == k.pipefds[0]: k.s <- struct{}{} return case n > 0: k.sendEvents(k.process(kevn[0])) } } }