forked from dallarosa/eyenotify
/
eyenotify_linux.go
136 lines (120 loc) · 3.2 KB
/
eyenotify_linux.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
package main
import (
"bytes"
"encoding/binary"
"io/ioutil"
"log"
"os"
"strings"
"syscall"
)
const (
EVENT_SIZE = 16
)
//syscall has its own inotify event struct
//but I'm keeping this one just for the sake of having that string there.
//However I guess this could be eliminated later
type inotifyEvent struct {
wd int32
mask int32
cookie int32
length int32
name string
}
var (
lastEvent *inotifyEvent //keeping track of the last event. this is only useful for the vim problem
)
func intFromByte(byteSlice []byte, data interface{}) {
err := binary.Read(bytes.NewBuffer(byteSlice), binary.LittleEndian, data)
if err != nil {
log.Fatal("binary.read failed: ", err)
}
}
//Process the buffer from an inotify event.
func processBuffer(n int, buffer []byte) {
event := new(inotifyEvent)
var i int32
for i < int32(n) {
intFromByte(buffer[i:i+4], &event.wd)
intFromByte(buffer[i+4:i+8], &event.mask)
intFromByte(buffer[i+8:i+12], &event.cookie)
intFromByte(buffer[i+12:i+16], &event.length)
event.name = string(buffer[i+16 : i+16+event.length])
event.name = strings.TrimRight(event.name, "\x00")
i += EVENT_SIZE + event.length
if len(strings.Split(event.name, ".")) > 1 {
eventExt := strings.Split(event.name, ".")[1]
log.Print(ext, " - ", eventExt)
if ext == eventExt {
//TODO
//vim test: This should be done only if some "vim" flag is specified.
//Some background:
//=================
//Editors like Vim instead of saving the updated contents to the existing
//file, it creates a temp file (normally named "4093"), removes the existing
//file and renames the temp file to be the new file. This creates a bunch
//of unnecessary events that get the file tracking crazy.
//This check guarantees that we won't restart the process twice for the vim case
if lastEvent != nil && lastEvent.name == event.name && lastEvent.mask == syscall.IN_DELETE && event.mask == syscall.IN_CLOSE_WRITE {
break
}
lastEvent = event
restartProc()
break
}
}
}
}
//starts inotify tracking
func runInotify() {
fd, err := syscall.InotifyInit()
if err != nil {
log.Fatal("error initializing Inotify: ", err)
return
}
addFilesToInotify(fd, path)
var buffer []byte = make([]byte, 1024*EVENT_SIZE)
for {
n, err := syscall.Read(fd, buffer)
if err != nil {
log.Fatal("Read failed: ", err)
return
}
processBuffer(n, buffer)
}
}
//Add directories recursively to the tracking list
func addFilesToInotify(fd int, dirPath string) {
dir, err := os.Stat(dirPath)
if err != nil {
log.Fatal("error getting info on dir: ", err)
return
}
if dir.IsDir() && dir.Name() != ".git" {
log.Print("adding: ", dirPath)
_, err = syscall.InotifyAddWatch(fd, dirPath, syscall.IN_CLOSE_WRITE|syscall.IN_DELETE)
if err != nil {
log.Fatal("error adding watch: ", err)
return
}
fileList, err := ioutil.ReadDir(dirPath)
if err != nil {
log.Fatal("error reading dir: ", err)
return
}
for _, file := range fileList {
newPath := dirPath + "/" + file.Name()
if file.IsDir() && file.Name() != ".git" {
addFilesToInotify(fd, newPath)
}
}
}
}
func main() {
startProc()
if polling {
runPolling()
} else {
runInotify()
}
}