diff --git a/pkg/watcher/linux.go b/pkg/watcher/linux.go new file mode 100644 index 0000000..c3d97ae --- /dev/null +++ b/pkg/watcher/linux.go @@ -0,0 +1,79 @@ +package watcher + +import ( + "syscall" + "unsafe" + + "github.com/pkg/errors" +) + +const CrDelMask uint32 = syscall.IN_CREATE | syscall.IN_DELETE + +type FSWatcher struct { + fd int + wds []int + closed bool +} + +func NewFSWatcher() (fsw *FSWatcher, err error) { + fsw = &FSWatcher{closed: false} + + fsw.fd, err = syscall.InotifyInit() + if err != nil { + return nil, errors.Wrap(err, "failed to initialise inotify watcher") + } + + fsw.wds = make([]int, 0) + + return nil, nil +} + +func (fsw *FSWatcher) AddWatch(path string, mask uint32) error { + wd, err := syscall.InotifyAddWatch(fsw.fd, path, mask) + if err != nil { + return errors.Wrapf(err, "failed to set %s on watch", path) + } + + fsw.wds = append(fsw.wds, wd) + syscall.Read(fsw.fd, nil) + + return nil +} + +// WatchForMask checking for events from mask and returns inotify mask to channel. +func (fsw *FSWatcher) WatchForMask(fired chan uint32, mask uint32) { + buffer := make([]byte, syscall.SizeofInotifyEvent) + go func() { + for !fsw.closed { + n, err := syscall.Read(fsw.fd, buffer) + if err != nil { + break + } + + if n < syscall.SizeofInotifyEvent { + continue + } + + event := (*syscall.InotifyEvent)(unsafe.Pointer(&buffer[0])) + if event.Mask&mask > 0 { + fired <- event.Mask + } + } + }() +} + +func (fsw *FSWatcher) Close() error { + for _, wd := range fsw.wds { + if _, err := syscall.InotifyRmWatch(fsw.fd, uint32(wd)); err != nil { + return err + } + } + + if err := syscall.Close(fsw.fd); err != nil { + return err + } + + fsw.closed = true + + return nil +}