OSDN Git Service

new repo
[bytom/vapor.git] / vendor / github.com / rjeczalik / notify / watcher_kqueue.go
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.
4
5 // +build darwin,kqueue dragonfly freebsd netbsd openbsd
6
7 package notify
8
9 import (
10         "fmt"
11         "os"
12         "syscall"
13 )
14
15 // newTrigger returns implementation of trigger.
16 func newTrigger(pthLkp map[string]*watched) trigger {
17         return &kq{
18                 pthLkp: pthLkp,
19                 idLkp:  make(map[int]*watched),
20         }
21 }
22
23 // kq is a structure implementing trigger for kqueue.
24 type kq struct {
25         // fd is a kqueue file descriptor
26         fd int
27         // pipefds are file descriptors used to stop `Kevent` call.
28         pipefds [2]int
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
35 }
36
37 // watched is a data structure representing watched file/directory.
38 type watched struct {
39         // p is a path to watched file/directory.
40         p string
41         // fd is a file descriptor for watched file/directory.
42         fd int
43         // fi provides information about watched file/dir.
44         fi os.FileInfo
45         // eDir represents events watched directly.
46         eDir Event
47         // eNonDir represents events watched indirectly.
48         eNonDir Event
49 }
50
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})
55         return
56 }
57
58 // Close implements trigger.
59 func (k *kq) Close() error {
60         return syscall.Close(k.fd)
61 }
62
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)
66         if err != nil {
67                 return nil, err
68         }
69         return &watched{fd: fd, p: p, fi: fi}, nil
70 }
71
72 // Record implements trigger.
73 func (k *kq) Record(w *watched) {
74         k.idLkp[w.fd], k.pthLkp[w.p] = w, w
75 }
76
77 // Del implements trigger.
78 func (k *kq) Del(w *watched) {
79         syscall.Close(w.fd)
80         delete(k.idLkp, w.fd)
81         delete(k.pthLkp, w.p)
82 }
83
84 func inter2kq(n interface{}) syscall.Kevent_t {
85         kq, ok := n.(syscall.Kevent_t)
86         if !ok {
87                 panic(fmt.Sprintf("kqueue: type should be Kevent_t, %T instead", n))
88         }
89         return kq
90 }
91
92 // Init implements trigger.
93 func (k *kq) Init() (err error) {
94         if k.fd, err = syscall.Kqueue(); err != nil {
95                 return
96         }
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())
101         }
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())
106         }
107         return
108 }
109
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)
114
115         _, err = syscall.Kevent(k.fd, kevn[:], nil, nil)
116         return
117 }
118
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)
125
126         _, err = syscall.Kevent(k.fd, kevn[:], nil, nil)
127         return
128 }
129
130 // Wait implements trigger.
131 func (k *kq) Wait() (interface{}, error) {
132         var (
133                 kevn [1]syscall.Kevent_t
134                 err  error
135         )
136         kevn[0] = syscall.Kevent_t{}
137         _, err = syscall.Kevent(k.fd, nil, kevn[:], nil)
138
139         return kevn[0], err
140 }
141
142 // Watched implements trigger.
143 func (k *kq) Watched(n interface{}) (*watched, int64, error) {
144         kevn, ok := n.(syscall.Kevent_t)
145         if !ok {
146                 panic(fmt.Sprintf("kq: type should be syscall.Kevent_t, %T instead", kevn))
147         }
148         if _, ok = k.idLkp[int(kevn.Ident)]; !ok {
149                 return nil, 0, errNotWatched
150         }
151         return k.idLkp[int(kevn.Ident)], int64(kevn.Fflags), nil
152 }
153
154 // IsStop implements trigger.
155 func (k *kq) IsStop(n interface{}, err error) bool {
156         return int(inter2kq(n).Ident) == k.pipefds[0]
157 }
158
159 func init() {
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)
170                 }
171                 if e&Rename != 0 {
172                         o = (o &^ int64(Rename)) | int64(NoteRename)
173                 }
174                 if e&Remove != 0 {
175                         o = (o &^ int64(Remove)) | int64(NoteDelete)
176                 }
177                 return
178         }
179         nat2not = map[Event]Event{
180                 NoteWrite:  Write,
181                 NoteRename: Rename,
182                 NoteDelete: Remove,
183                 NoteExtend: Event(0),
184                 NoteAttrib: Event(0),
185                 NoteRevoke: Event(0),
186                 NoteLink:   Event(0),
187         }
188         not2nat = map[Event]Event{
189                 Write:  NoteWrite,
190                 Rename: NoteRename,
191                 Remove: NoteDelete,
192         }
193 }