1 // Copyright (c) 2014-2015 The Notify Authors. All rights reserved.
2 // Use of this source code is governed by the MIT license that can be
3 // found in the LICENSE file.
5 // +build darwin,kqueue dragonfly freebsd netbsd openbsd
15 // newTrigger returns implementation of trigger.
16 func newTrigger(pthLkp map[string]*watched) trigger {
19 idLkp: make(map[int]*watched),
23 // kq is a structure implementing trigger for kqueue.
25 // fd is a kqueue file descriptor
27 // pipefds are file descriptors used to stop `Kevent` call.
29 // idLkp is a data structure mapping file descriptors with data about watching
30 // represented by them files/directories.
31 idLkp map[int]*watched
32 // pthLkp is a structure mapping monitored files/dir with data about them,
33 // shared with parent trg structure
34 pthLkp map[string]*watched
37 // watched is a data structure representing watched file/directory.
39 // p is a path to watched file/directory.
41 // fd is a file descriptor for watched file/directory.
43 // fi provides information about watched file/dir.
45 // eDir represents events watched directly.
47 // eNonDir represents events watched indirectly.
51 // Stop implements trigger.
52 func (k *kq) Stop() (err error) {
53 // trigger event used to interrupt Kevent call.
54 _, err = syscall.Write(k.pipefds[1], []byte{0x00})
58 // Close implements trigger.
59 func (k *kq) Close() error {
60 return syscall.Close(k.fd)
63 // NewWatched implements trigger.
64 func (*kq) NewWatched(p string, fi os.FileInfo) (*watched, error) {
65 fd, err := syscall.Open(p, syscall.O_NONBLOCK|syscall.O_RDONLY, 0)
69 return &watched{fd: fd, p: p, fi: fi}, nil
72 // Record implements trigger.
73 func (k *kq) Record(w *watched) {
74 k.idLkp[w.fd], k.pthLkp[w.p] = w, w
77 // Del implements trigger.
78 func (k *kq) Del(w *watched) {
84 func inter2kq(n interface{}) syscall.Kevent_t {
85 kq, ok := n.(syscall.Kevent_t)
87 panic(fmt.Sprintf("kqueue: type should be Kevent_t, %T instead", n))
92 // Init implements trigger.
93 func (k *kq) Init() (err error) {
94 if k.fd, err = syscall.Kqueue(); err != nil {
97 // Creates pipe used to stop `Kevent` call by registering it,
98 // watching read end and writing to other end of it.
99 if err = syscall.Pipe(k.pipefds[:]); err != nil {
100 return nonil(err, k.Close())
102 var kevn [1]syscall.Kevent_t
103 syscall.SetKevent(&kevn[0], k.pipefds[0], syscall.EVFILT_READ, syscall.EV_ADD)
104 if _, err = syscall.Kevent(k.fd, kevn[:], nil, nil); err != nil {
105 return nonil(err, k.Close())
110 // Unwatch implements trigger.
111 func (k *kq) Unwatch(w *watched) (err error) {
112 var kevn [1]syscall.Kevent_t
113 syscall.SetKevent(&kevn[0], w.fd, syscall.EVFILT_VNODE, syscall.EV_DELETE)
115 _, err = syscall.Kevent(k.fd, kevn[:], nil, nil)
119 // Watch implements trigger.
120 func (k *kq) Watch(fi os.FileInfo, w *watched, e int64) (err error) {
121 var kevn [1]syscall.Kevent_t
122 syscall.SetKevent(&kevn[0], w.fd, syscall.EVFILT_VNODE,
123 syscall.EV_ADD|syscall.EV_CLEAR)
124 kevn[0].Fflags = uint32(e)
126 _, err = syscall.Kevent(k.fd, kevn[:], nil, nil)
130 // Wait implements trigger.
131 func (k *kq) Wait() (interface{}, error) {
133 kevn [1]syscall.Kevent_t
136 kevn[0] = syscall.Kevent_t{}
137 _, err = syscall.Kevent(k.fd, nil, kevn[:], nil)
142 // Watched implements trigger.
143 func (k *kq) Watched(n interface{}) (*watched, int64, error) {
144 kevn, ok := n.(syscall.Kevent_t)
146 panic(fmt.Sprintf("kq: type should be syscall.Kevent_t, %T instead", kevn))
148 if _, ok = k.idLkp[int(kevn.Ident)]; !ok {
149 return nil, 0, errNotWatched
151 return k.idLkp[int(kevn.Ident)], int64(kevn.Fflags), nil
154 // IsStop implements trigger.
155 func (k *kq) IsStop(n interface{}, err error) bool {
156 return int(inter2kq(n).Ident) == k.pipefds[0]
160 encode = func(e Event, dir bool) (o int64) {
161 // Create event is not supported by kqueue. Instead NoteWrite event will
162 // be registered for a directory. If this event will be reported on dir
163 // which is to be monitored for Create, dir will be rescanned
164 // and Create events will be generated and returned for new files.
165 // In case of files, if not requested NoteRename event is reported,
166 // it will be ignored.
167 o = int64(e &^ Create)
168 if (e&Create != 0 && dir) || e&Write != 0 {
169 o = (o &^ int64(Write)) | int64(NoteWrite)
172 o = (o &^ int64(Rename)) | int64(NoteRename)
175 o = (o &^ int64(Remove)) | int64(NoteDelete)
179 nat2not = map[Event]Event{
183 NoteExtend: Event(0),
184 NoteAttrib: Event(0),
185 NoteRevoke: Event(0),
188 not2nat = map[Event]Event{