I want to use inotify to watch some file changes, and use epoll to monitor if any inotify event occurs. However, I was having some trouble receiving inotify events. Say, how do I know if I got every events that occured.
If inotify fd sets to NON_BLOCK mode, EAGAIN would get returned when read() indicating that there's nothing else left to read, which is nice. But If inotify fd sets to BLOCKING mode, read() would block infinitely.
For example, if you call read(2) by asking to read a certain amount of data and read(2) returns a lower number of bytes, you can be sure of having exhausted the read I/O space for the file descriptor.
Ref: epoll(7)
According to epoll(7), I can assure a thorough read if read() returns a lower number of bytes. But how should I deal with read() returning the same number of bytes ?
Here's the code I've tried.
func main() {
fd, _ := unix.InotifyInit()
epollfd, _ := unix.EpollCreate(1)
unix.EpollCtl(epollfd, unix.EPOLL_CTL_ADD, fd, &unix.EpollEvent{
Fd: int32(fd),
Events: unix.EPOLLIN,
})
unix.InotifyAddWatch(fd, os.Args[1], unix.IN_ALL_EVENTS)
epollevents := make([]unix.EpollEvent, 8)
for {
// epoll wait
var nepoll int
if nepoll, err = unix.EpollWait(epollfd, epollevents, -1); err == nil {
// nothing happens
} else if errors.Is(err, unix.EINTR) {
continue // ignore interrupt
} else {
log.Fatal(err.Error())
}
// process inotify events
for i := 0; i < nepoll; i += 1 {
var nr int
// buff for receiving
// eventBuf for processing
// two buff to prevent a large amount of data
eventBuff := make([]byte, (unix.SizeofInotifyEvent+unix.PathMax)*2)
buff := make([]byte, unix.SizeofInotifyEvent+unix.PathMax)
offset, nr, lastUnread := 0, 0, 0
for {
// read loop in case of a large amount of data
if nr, err = unix.Read(int(epollevents[i].Fd), buff); err == nil {
// nothing happens
} else if errors.Is(err, unix.EAGAIN) {
println("exhaust")
break
} else {
log.Fatal(err.Error())
}
copy(eventBuff[lastUnread:lastUnread+nr], buff[:nr])
lastUnread += nr
for offset < lastUnread &&
(nr < len(buff) || lastUnread-offset >= unix.PathMax+unix.SizeofInotifyEvent) {
event := (*unix.InotifyEvent)(unsafe.Pointer(&eventBuff[offset]))
switch event.Mask {
case unix.IN_ACCESS:
// ...
case unix.IN_OPEN:
// ...
case unix.IN_CLOSE_WRITE:
// ...
case unix.IN_CLOSE_NOWRITE:
// ...
}
offset += unix.SizeofInotifyEvent + int(event.Len)
}
copy(eventBuff[offset:lastUnread], eventBuff[:lastUnread-offset])
lastUnread -= offset
offset = 0
}
}
fmt.Println("Over") // NEVER got print out in BLOCKING mode
}
}