/
notify.go
387 lines (323 loc) · 9.92 KB
/
notify.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
package sfserver
import (
//"bytes"
"fmt"
"log"
"os"
//"os/exec"
"syscall"
//"errors"
"path/filepath"
"strings"
"unsafe"
)
const (
// Options for inotify_init() are not exported
// sys_IN_CLOEXEC uint32 = syscall.IN_CLOEXEC
// sys_IN_NONBLOCK uint32 = syscall.IN_NONBLOCK
// Options for AddWatch
sys_IN_DONT_FOLLOW uint32 = syscall.IN_DONT_FOLLOW
sys_IN_ONESHOT uint32 = syscall.IN_ONESHOT
sys_IN_ONLYDIR uint32 = syscall.IN_ONLYDIR
// The "sys_IN_MASK_ADD" option is not exported, as AddWatch
// adds it automatically, if there is already a watch for the given path
// sys_IN_MASK_ADD uint32 = syscall.IN_MASK_ADD
// Events
sys_IN_ACCESS uint32 = syscall.IN_ACCESS
sys_IN_ALL_EVENTS uint32 = syscall.IN_ALL_EVENTS
sys_IN_ATTRIB uint32 = syscall.IN_ATTRIB
sys_IN_CLOSE uint32 = syscall.IN_CLOSE
sys_IN_CLOSE_NOWRITE uint32 = syscall.IN_CLOSE_NOWRITE
sys_IN_CLOSE_WRITE uint32 = syscall.IN_CLOSE_WRITE
sys_IN_CREATE uint32 = syscall.IN_CREATE
sys_IN_DELETE uint32 = syscall.IN_DELETE
sys_IN_DELETE_SELF uint32 = syscall.IN_DELETE_SELF
sys_IN_MODIFY uint32 = syscall.IN_MODIFY
sys_IN_MOVE uint32 = syscall.IN_MOVE
sys_IN_MOVED_FROM uint32 = syscall.IN_MOVED_FROM
sys_IN_MOVED_TO uint32 = syscall.IN_MOVED_TO
sys_IN_MOVE_SELF uint32 = syscall.IN_MOVE_SELF
sys_IN_OPEN uint32 = syscall.IN_OPEN
sys_AGNOSTIC_EVENTS = sys_IN_MOVED_TO | sys_IN_MOVED_FROM | sys_IN_CREATE | sys_IN_ATTRIB | sys_IN_MODIFY | sys_IN_MOVE_SELF | sys_IN_DELETE | sys_IN_DELETE_SELF
// Special events
sys_IN_ISDIR uint32 = syscall.IN_ISDIR
sys_IN_IGNORED uint32 = syscall.IN_IGNORED
sys_IN_Q_OVERFLOW uint32 = syscall.IN_Q_OVERFLOW
sys_IN_UNMOUNT uint32 = syscall.IN_UNMOUNT
// Block for 100ms on each call to Select
selectWaitTime = 100e6
)
type FileEvent struct {
wd int32
mask uint32
cookie uint32
nameLen uint32
fileName string
}
// IsCreate reports whether the FileEvent was triggerd by a creation
func (e *FileEvent) IsCreate() bool {
return (e.mask&sys_IN_CREATE) == sys_IN_CREATE || (e.mask&sys_IN_MOVED_TO) == sys_IN_MOVED_TO
}
// IsDelete reports whether the FileEvent was triggerd by a delete
func (e *FileEvent) IsDelete() bool {
return (e.mask&sys_IN_DELETE_SELF) == sys_IN_DELETE_SELF || (e.mask&sys_IN_DELETE) == sys_IN_DELETE
}
// IsModify reports whether the FileEvent was triggerd by a file modification or attribute change
func (e *FileEvent) IsModify() bool {
return ((e.mask & sys_IN_MODIFY) == sys_IN_MODIFY)
}
// IsRename reports whether the FileEvent was triggerd by a change name
func (e *FileEvent) IsRename() bool {
return ((e.mask&sys_IN_MOVE_SELF) == sys_IN_MOVE_SELF || (e.mask&sys_IN_MOVED_FROM) == sys_IN_MOVED_FROM)
}
type Watcher struct {
wm *WatchMap
fd int "inotify事件描述符"
acceptEvent chan *FileEvent "接受事件通道"
handleEvent chan *FileEvent "处理事件通道"
Error chan error "错误通道"
done chan bool "当监控事件关闭时,发送退出信息到此通道"
isClose bool "判断是否已关闭"
skipDir map[string]int "不监控的目录"
skipExt map[string]int "不监控的文件后缀"
}
func NewWatcher() (w *Watcher, err error) {
fd, err := syscall.InotifyInit()
if fd == -1 {
log.Fatal("Watcher Init Error", err)
}
watcher := &Watcher{
wm: newWatchMap(),
fd: fd,
acceptEvent: make(chan *FileEvent),
handleEvent: make(chan *FileEvent),
Error: make(chan error),
done: make(chan bool),
skipDir: make(map[string]int),
skipExt: make(map[string]int),
isClose: false,
}
go watcher.readEvent() //读取事件,把事件发送到acceptEvent通道中
go watcher.purgeEvents() //处理时间,把处理的结果发送到handleEvent通道中
return watcher, nil
}
func (w *Watcher) setSkipDir(skipDir map[string]int) {
w.skipDir = skipDir
fmt.Println(w.skipDir)
}
func (w *Watcher) setSkipExt(skipExt map[string]int) {
w.skipExt = skipExt
fmt.Println(w.skipExt)
}
func PrintMap(m map[string]int) {
for a, b := range m {
fmt.Println(a, "->", b)
}
}
func (w *Watcher) Watch(path string, flags uint32) error {
f, err := os.Lstat(path)
if err != nil {
return err
}
//如果起始监控的是目录,则监控所有的目录
if f.IsDir() {
err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
if info.IsDir() {
//判断是否是需要过滤的监控的目录
if _, found := w.skipDir[path]; found {
fmt.Println("过滤目录:", path)
return nil
}
w.AddWatch(path, flags)
}
return nil
})
if err != nil {
return err
}
} else {
w.AddWatch(path, flags) //如果是文件,则只监听文件
}
return nil
}
func (w *Watcher) AddWatch(path string, flags uint32) error {
if w.isClose {
log.Fatal("监控已经关闭")
}
//判断是否已经监控了
if found := w.wm.find(path); found != nil {
f := found.(watch)
f.flags |= flags
flags |= syscall.IN_MASK_ADD
}
wd, err := syscall.InotifyAddWatch(w.fd, path, flags)
fmt.Println("开始监控目录:", path, wd)
if wd == -1 {
return err
}
w.wm.add(path, wd, flags)
return nil
}
func (w *Watcher) RemoveWatch(path string) error {
f, err := os.Lstat(path)
if err != nil {
return err
}
//如果起始监控的是目录,则删除已监控的所有目录
if f.IsDir() {
err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
if info.IsDir() {
w.rmWatch(path)
}
return nil
})
if err != nil {
return err
}
}
return nil
}
func (w *Watcher) rmWatch(path string) error {
if found := w.wm.find(path); found != nil {
success, errno := syscall.InotifyRmWatch(w.fd, uint32(found.(watch).wd))
if success == -1 {
return os.NewSyscallError("inotify_rm_watch", errno)
}
w.wm.remove(path)
fmt.Println("移除监控目录:", path)
} else {
fmt.Println("没有找到对应的监控", path)
}
return nil
}
func (w *Watcher) Close() error {
if w.isClose {
return nil
}
w.isClose = true
for path, _ := range w.wm.watcher {
w.rmWatch(path)
}
w.done <- true
return nil
}
func (w *Watcher) readEvent() {
var (
buf [syscall.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events
n int // Number of bytes read with read()
errno error // Syscall errno
)
for {
select {
case <-w.done:
syscall.Close(w.fd)
close(w.acceptEvent)
close(w.Error)
return
default:
}
n, errno = syscall.Read(w.fd, buf[0:])
if n == 0 {
syscall.Close(w.fd)
close(w.acceptEvent)
close(w.Error)
return
}
if n < syscall.SizeofInotifyEvent {
log.Fatal("size of InotifyEvent error", errno)
}
var offset uint32 = 0
for offset <= uint32(n-syscall.SizeofInotifyEvent) {
raw := (*syscall.InotifyEvent)(unsafe.Pointer(&buf[offset]))
event := new(FileEvent)
event.wd = raw.Wd
event.nameLen = raw.Len
event.mask = raw.Mask
event.cookie = raw.Cookie
path := w.wm.paths[int(raw.Wd)]
if raw.Len > 0 {
// Point "bytes" at the first byte of the filename
bytes := (*[syscall.PathMax]byte)(unsafe.Pointer(&buf[offset+syscall.SizeofInotifyEvent]))
// The filename is padded with NUL bytes. TrimRight() gets rid of those.
event.fileName = path + "/" + strings.TrimRight(string(bytes[0:raw.Len]), "\000")
}
if _, found := w.skipExt[filepath.Ext(event.fileName)]; !found {
fmt.Println("--->", w.skipExt, "--->", filepath.Ext(event.fileName), "--->", found)
//发送事件acceptEvent通道
w.acceptEvent <- event
} else {
fmt.Println("过滤文件:", event.fileName)
}
offset += syscall.SizeofInotifyEvent + raw.Len
}
}
}
func (w *Watcher) purgeEvents() {
for event := range w.acceptEvent {
// if (event.mask & sys_IN_OPEN) == sys_IN_OPEN {
// fmt.Println("sys_IN_OPEN is use")
// }
// if (event.mask & sys_IN_MOVE_SELF) == sys_IN_MOVE_SELF {
// fmt.Println("sys_IN_MOVE_SELF is use")
// }
// if (event.mask & sys_IN_MOVED_TO) == sys_IN_MOVED_TO {
// fmt.Println("sys_IN_MOVED_TO is use")
// }
// if (event.mask & sys_IN_MOVED_FROM) == sys_IN_MOVED_FROM {
// fmt.Println("sys_IN_MOVED_FROM is use")
// }
// if (event.mask & sys_IN_ACCESS) == sys_IN_ACCESS {
// fmt.Println("sys_in_access is use")
// }
// if (event.mask & sys_IN_ALL_EVENTS) == sys_IN_ALL_EVENTS {
// fmt.Println("sys_IN_ALL_EVENTS is use")
// }
// if (event.mask & sys_IN_ATTRIB) == sys_IN_ATTRIB {
// fmt.Println("sys_IN_ATTRIB is use")
// }
// if (event.mask & sys_IN_CLOSE) == sys_IN_CLOSE {
// fmt.Println("sys_IN_CLOSE is use")
// }
// if (event.mask & sys_IN_CLOSE_WRITE) == sys_IN_CLOSE_WRITE {
// fmt.Println("sys_IN_CLOSE_WRITE is use")
// }
// if (event.mask & sys_IN_CREATE) == sys_IN_CREATE {
// fmt.Println("sys_IN_CREATE is use")
// }
// if (event.mask & sys_IN_DELETE) == sys_IN_DELETE {
// fmt.Println("sys_IN_DELETE is use")
// }
// if (event.mask & sys_IN_DELETE_SELF) == sys_IN_DELETE_SELF {
// fmt.Println("sys_IN_DELETE_SELF is use")
// }
// if (event.mask & sys_IN_MODIFY) == sys_IN_MODIFY {
// fmt.Println("sys_IN_MODIFY is use")
// }
// if (event.mask & sys_IN_MOVE) == sys_IN_MOVE {
// fmt.Println("sys_IN_MOVE is use")
// }
sendEvent := false
switch {
case event.IsCreate():
if (event.mask & sys_IN_ISDIR) == sys_IN_ISDIR {
w.Watch(event.fileName, sys_IN_ALL_EVENTS)
}
sendEvent = true
case event.IsDelete():
if (event.mask & sys_IN_ISDIR) == sys_IN_ISDIR {
w.rmWatch(event.fileName)
pathlen := len(event.fileName) - len(filepath.Base(event.fileName))
event.fileName = event.fileName[0:pathlen]
}
event.fileName = filepath.Dir(event.fileName) + "/"
sendEvent = true
case event.IsModify():
sendEvent = true
case event.IsRename():
sendEvent = true
}
if sendEvent {
syncEvent <- event
}
}
}