From 8941e9a90b9560e6b433c68725255f827922a5a8 Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Sat, 12 Sep 2015 09:14:40 -0600 Subject: Add an inotify watcher utility using channels; use it. The interface of inotify/inutil.Watcher more resembles golang.org/x/exp/inotify; with it using channels instead of repeated calls to Read(). In my use-case, this is useful because it allows implementing a "read" (select, really) that doesn't block Close(); which is required to handle the TERM signal correctly. --- inotify/inutil/inotify_util.go | 78 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 inotify/inutil/inotify_util.go diff --git a/inotify/inutil/inotify_util.go b/inotify/inutil/inotify_util.go new file mode 100644 index 0000000..46146f5 --- /dev/null +++ b/inotify/inutil/inotify_util.go @@ -0,0 +1,78 @@ +package inutil + +import ( + "inotify" + "os" + "syscall" +) + +const ( + // Flags for the parameter of InotifyInit1(). + // These, oddly, appear to be 24-bit numbers. + IN_CLOEXEC = inotify.IN_CLOEXEC +) + +type Watcher struct { + Events <-chan inotify.Event + events chan<- inotify.Event + Errors <-chan error + errors chan<- error + in *inotify.Inotify +} + +func WatcherInit() (*Watcher, error) { + in, err := inotify.InotifyInit() + return newWatcher(in, err) +} + +func WatcherInit1(flags int) (*Watcher, error) { + in, err := inotify.InotifyInit1(flags&^inotify.IN_NONBLOCK) + return newWatcher(in, err) +} + +func newWatcher(in *inotify.Inotify, err error) (*Watcher, error) { + events := make(chan inotify.Event, 1) + errors := make(chan error, 1) + o := &Watcher{ + Events: events, + events: events, + Errors: errors, + errors: errors, + in: in, + } + go o.worker() + return o, err +} + +func (o *Watcher) AddWatch(path string, mask inotify.Mask) (inotify.Wd, error) { + return o.in.AddWatch(path, mask); +} + +func (o *Watcher) RmWatch(wd inotify.Wd) error { + return o.in.RmWatch(wd); +} + +func (o *Watcher) Close() { + func() { + defer recover() + close(o.events) + close(o.errors) + }() + go o.in.Close() +} + +func (o *Watcher) worker() { + defer recover() + for { + ev, err := o.in.Read(); + if ev.Wd >= 0 { + o.events <- ev + } + if err != nil { + if err.(*os.SyscallError).Err == syscall.EBADF { + o.Close() + } + o.errors <- err + } + } +} -- cgit v1.1-4-g5e80