aboutsummaryrefslogtreecommitdiffstats
path: root/Godeps/_workspace/src/github.com/rjeczalik/notify/watcher_kqueue.go
diff options
context:
space:
mode:
Diffstat (limited to 'Godeps/_workspace/src/github.com/rjeczalik/notify/watcher_kqueue.go')
-rw-r--r--Godeps/_workspace/src/github.com/rjeczalik/notify/watcher_kqueue.go192
1 files changed, 192 insertions, 0 deletions
diff --git a/Godeps/_workspace/src/github.com/rjeczalik/notify/watcher_kqueue.go b/Godeps/_workspace/src/github.com/rjeczalik/notify/watcher_kqueue.go
new file mode 100644
index 000000000..d5f7788c4
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/rjeczalik/notify/watcher_kqueue.go
@@ -0,0 +1,192 @@
+// Copyright (c) 2014-2015 The Notify Authors. All rights reserved.
+// Use of this source code is governed by the MIT license that can be
+// found in the LICENSE file.
+
+// +build darwin,kqueue dragonfly freebsd netbsd openbsd
+
+package notify
+
+import (
+ "fmt"
+ "os"
+ "syscall"
+)
+
+// newTrigger returns implementation of trigger.
+func newTrigger(pthLkp map[string]*watched) trigger {
+ return &kq{
+ pthLkp: pthLkp,
+ idLkp: make(map[int]*watched),
+ }
+}
+
+// kq is a structure implementing trigger for kqueue.
+type kq struct {
+ // fd is a kqueue file descriptor
+ fd int
+ // pipefds are file descriptors used to stop `Kevent` call.
+ pipefds [2]int
+ // idLkp is a data structure mapping file descriptors with data about watching
+ // represented by them files/directories.
+ idLkp map[int]*watched
+ // pthLkp is a structure mapping monitored files/dir with data about them,
+ // shared with parent trg structure
+ pthLkp map[string]*watched
+}
+
+// watched is a data structure representing watched file/directory.
+type watched struct {
+ // p is a path to watched file/directory.
+ p string
+ // fd is a file descriptor for watched file/directory.
+ fd int
+ // fi provides information about watched file/dir.
+ fi os.FileInfo
+ // eDir represents events watched directly.
+ eDir Event
+ // eNonDir represents events watched indirectly.
+ eNonDir Event
+}
+
+// Stop implements trigger.
+func (k *kq) Stop() (err error) {
+ // trigger event used to interrupt Kevent call.
+ _, err = syscall.Write(k.pipefds[1], []byte{0x00})
+ return
+}
+
+// Close implements trigger.
+func (k *kq) Close() error {
+ return syscall.Close(k.fd)
+}
+
+// NewWatched implements trigger.
+func (*kq) NewWatched(p string, fi os.FileInfo) (*watched, error) {
+ fd, err := syscall.Open(p, syscall.O_NONBLOCK|syscall.O_RDONLY, 0)
+ if err != nil {
+ return nil, err
+ }
+ return &watched{fd: fd, p: p, fi: fi}, nil
+}
+
+// Record implements trigger.
+func (k *kq) Record(w *watched) {
+ k.idLkp[w.fd], k.pthLkp[w.p] = w, w
+}
+
+// Del implements trigger.
+func (k *kq) Del(w *watched) {
+ syscall.Close(w.fd)
+ delete(k.idLkp, w.fd)
+ delete(k.pthLkp, w.p)
+}
+
+func inter2kq(n interface{}) syscall.Kevent_t {
+ kq, ok := n.(syscall.Kevent_t)
+ if !ok {
+ panic(fmt.Sprintf("kqueue: type should be Kevent_t, %T instead", n))
+ }
+ return kq
+}
+
+// Init implements trigger.
+func (k *kq) 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 nonil(err, k.Close())
+ }
+ var kevn [1]syscall.Kevent_t
+ syscall.SetKevent(&kevn[0], k.pipefds[0], syscall.EVFILT_READ, syscall.EV_ADD)
+ if _, err = syscall.Kevent(k.fd, kevn[:], nil, nil); err != nil {
+ return nonil(err, k.Close())
+ }
+ return
+}
+
+// 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 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
+}
+
+// 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
+}
+
+// Watched implements trigger.
+func (k *kq) Watched(n interface{}) (*watched, int64, error) {
+ kevn, ok := n.(syscall.Kevent_t)
+ if !ok {
+ panic(fmt.Sprintf("kq: type should be syscall.Kevent_t, %T instead", kevn))
+ }
+ if _, ok = k.idLkp[int(kevn.Ident)]; !ok {
+ return nil, 0, errNotWatched
+ }
+ return k.idLkp[int(kevn.Ident)], int64(kevn.Fflags), nil
+}
+
+// IsStop implements trigger.
+func (k *kq) IsStop(n interface{}, err error) bool {
+ return int(inter2kq(n).Ident) == k.pipefds[0]
+}
+
+func init() {
+ encode = func(e Event) (o int64) {
+ // Create event is not supported by kqueue. Instead NoteWrite event will
+ // be registered. If this event will be reported on dir which is to be
+ // monitored for Create, dir will be rescanned and Create events will
+ // be generated and returned for new files. In case of files,
+ // if not requested NoteRename event is reported, it will be ignored.
+ o = int64(e &^ Create)
+ if e&Write != 0 {
+ o = (o &^ int64(Write)) | int64(NoteWrite)
+ }
+ if e&Rename != 0 {
+ o = (o &^ int64(Rename)) | int64(NoteRename)
+ }
+ if e&Remove != 0 {
+ o = (o &^ int64(Remove)) | int64(NoteDelete)
+ }
+ return
+ }
+ nat2not = map[Event]Event{
+ NoteWrite: Write,
+ NoteRename: Rename,
+ NoteDelete: Remove,
+ NoteExtend: Event(0),
+ NoteAttrib: Event(0),
+ NoteRevoke: Event(0),
+ NoteLink: Event(0),
+ }
+ not2nat = map[Event]Event{
+ Write: NoteWrite,
+ Rename: NoteRename,
+ Remove: NoteDelete,
+ }
+}