summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@sbcglobal.net>2015-09-12 09:14:40 -0600
committerLuke Shumaker <lukeshu@sbcglobal.net>2015-09-12 09:14:40 -0600
commit8941e9a90b9560e6b433c68725255f827922a5a8 (patch)
treee8978ecfa32a6822bfb91700d330e69da01fe242
parentb4ba0d061c1ef2f8069a568992eb98b7c060f5cf (diff)
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.
-rw-r--r--inotify/inutil/inotify_util.go78
1 files changed, 78 insertions, 0 deletions
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
+ }
+ }
+}