1
2
3
4
5
6
7
8
30 package inotify
31
32 import (
33 "errors"
34 "fmt"
35 "os"
36 "strings"
37 "syscall"
38 "unsafe"
39 )
40
41
42 func NewWatcher() (*Watcher, error) {
43 fd, errno := syscall.InotifyInit1(syscall.IN_CLOEXEC)
44 if fd == -1 {
45 return nil, os.NewSyscallError("inotify_init", errno)
46 }
47 w := &Watcher{
48 fd: fd,
49 watches: make(map[string]*watch),
50 paths: make(map[int]string),
51 Event: make(chan *Event),
52 Error: make(chan error),
53 done: make(chan bool, 1),
54 }
55
56 go w.readEvents()
57 return w, nil
58 }
59
60
61
62
63 func (w *Watcher) Close() error {
64 if w.isClosed {
65 return nil
66 }
67 w.isClosed = true
68
69
70 w.done <- true
71 for path := range w.watches {
72 w.RemoveWatch(path)
73 }
74
75 return nil
76 }
77
78
79
80 func (w *Watcher) AddWatch(path string, flags uint32) error {
81 if w.isClosed {
82 return errors.New("inotify instance already closed")
83 }
84
85 watchEntry, found := w.watches[path]
86 if found {
87 watchEntry.flags |= flags
88 flags |= syscall.IN_MASK_ADD
89 }
90
91 w.mu.Lock()
92
93 wd, err := syscall.InotifyAddWatch(w.fd, path, flags)
94 if err != nil {
95 w.mu.Unlock()
96 return &os.PathError{
97 Op: "inotify_add_watch",
98 Path: path,
99 Err: err,
100 }
101 }
102
103 if !found {
104 w.watches[path] = &watch{wd: uint32(wd), flags: flags}
105 w.paths[wd] = path
106 }
107 w.mu.Unlock()
108 return nil
109 }
110
111
112 func (w *Watcher) Watch(path string) error {
113 return w.AddWatch(path, InAllEvents)
114 }
115
116
117 func (w *Watcher) RemoveWatch(path string) error {
118 watch, ok := w.watches[path]
119 if !ok {
120 return fmt.Errorf("can't remove non-existent inotify watch for: %s", path)
121 }
122 success, errno := syscall.InotifyRmWatch(w.fd, watch.wd)
123 if success == -1 {
124
125
126 if errno != syscall.EINVAL {
127 return os.NewSyscallError("inotify_rm_watch", errno)
128 }
129 }
130 delete(w.watches, path)
131
132 w.mu.Lock()
133 delete(w.paths, int(watch.wd))
134 w.mu.Unlock()
135 return nil
136 }
137
138
139
140 func (w *Watcher) readEvents() {
141 var buf [syscall.SizeofInotifyEvent * 4096]byte
142
143 for {
144 n, err := syscall.Read(w.fd, buf[:])
145
146 var done bool
147 select {
148 case done = <-w.done:
149 default:
150 }
151
152
153 if n == 0 || done {
154
155
156 close(w.Event)
157 err := syscall.Close(w.fd)
158 if err != nil {
159 w.Error <- os.NewSyscallError("close", err)
160 }
161 close(w.Error)
162 return
163 }
164 if n < 0 {
165 w.Error <- os.NewSyscallError("read", err)
166 continue
167 }
168 if n < syscall.SizeofInotifyEvent {
169 w.Error <- errors.New("inotify: short read in readEvents()")
170 continue
171 }
172
173 var offset uint32
174
175
176 for offset <= uint32(n-syscall.SizeofInotifyEvent) {
177
178 raw := (*syscall.InotifyEvent)(unsafe.Pointer(&buf[offset]))
179 event := new(Event)
180 event.Mask = uint32(raw.Mask)
181 event.Cookie = uint32(raw.Cookie)
182 nameLen := uint32(raw.Len)
183
184
185
186
187 w.mu.Lock()
188 name, ok := w.paths[int(raw.Wd)]
189 w.mu.Unlock()
190 if ok {
191 event.Name = name
192 if nameLen > 0 {
193
194 bytes := (*[syscall.PathMax]byte)(unsafe.Pointer(&buf[offset+syscall.SizeofInotifyEvent]))
195
196 event.Name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000")
197 }
198
199 w.Event <- event
200 }
201
202 offset += syscall.SizeofInotifyEvent + nameLen
203 }
204 }
205 }
206
207
208
209 func (e *Event) String() string {
210 var events string
211
212 m := e.Mask
213 for _, b := range eventBits {
214 if m&b.Value == b.Value {
215 m &^= b.Value
216 events += "|" + b.Name
217 }
218 }
219
220 if m != 0 {
221 events += fmt.Sprintf("|%#x", m)
222 }
223 if len(events) > 0 {
224 events = " == " + events[1:]
225 }
226
227 return fmt.Sprintf("%q: %#x%s", e.Name, e.Mask, events)
228 }
229
230 const (
231
232
233
234
235
236
237
238 InDontFollow uint32 = syscall.IN_DONT_FOLLOW
239
240 InOneshot uint32 = syscall.IN_ONESHOT
241
242 InOnlydir uint32 = syscall.IN_ONLYDIR
243
244
245
246
247
248
249
250
251 InAccess uint32 = syscall.IN_ACCESS
252
253 InAllEvents uint32 = syscall.IN_ALL_EVENTS
254
255 InAttrib uint32 = syscall.IN_ATTRIB
256
257 InClose uint32 = syscall.IN_CLOSE
258
259 InCloseNowrite uint32 = syscall.IN_CLOSE_NOWRITE
260
261 InCloseWrite uint32 = syscall.IN_CLOSE_WRITE
262
263 InCreate uint32 = syscall.IN_CREATE
264
265 InDelete uint32 = syscall.IN_DELETE
266
267 InDeleteSelf uint32 = syscall.IN_DELETE_SELF
268
269 InModify uint32 = syscall.IN_MODIFY
270
271 InMove uint32 = syscall.IN_MOVE
272
273 InMovedFrom uint32 = syscall.IN_MOVED_FROM
274
275 InMovedTo uint32 = syscall.IN_MOVED_TO
276
277 InMoveSelf uint32 = syscall.IN_MOVE_SELF
278
279 InOpen uint32 = syscall.IN_OPEN
280
281
282
283
284 InIsdir uint32 = syscall.IN_ISDIR
285
286 InIgnored uint32 = syscall.IN_IGNORED
287
288 InQOverflow uint32 = syscall.IN_Q_OVERFLOW
289
290 InUnmount uint32 = syscall.IN_UNMOUNT
291 )
292
293 var eventBits = []struct {
294 Value uint32
295 Name string
296 }{
297 {InAccess, "IN_ACCESS"},
298 {InAttrib, "IN_ATTRIB"},
299 {InClose, "IN_CLOSE"},
300 {InCloseNowrite, "IN_CLOSE_NOWRITE"},
301 {InCloseWrite, "IN_CLOSE_WRITE"},
302 {InCreate, "IN_CREATE"},
303 {InDelete, "IN_DELETE"},
304 {InDeleteSelf, "IN_DELETE_SELF"},
305 {InModify, "IN_MODIFY"},
306 {InMove, "IN_MOVE"},
307 {InMovedFrom, "IN_MOVED_FROM"},
308 {InMovedTo, "IN_MOVED_TO"},
309 {InMoveSelf, "IN_MOVE_SELF"},
310 {InOpen, "IN_OPEN"},
311 {InIsdir, "IN_ISDIR"},
312 {InIgnored, "IN_IGNORED"},
313 {InQOverflow, "IN_Q_OVERFLOW"},
314 {InUnmount, "IN_UNMOUNT"},
315 }
316
View as plain text