summaryrefslogtreecommitdiff
path: root/src/inotify/inutil/inotify_util.go
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
commitb190157b8c568922f7f9b4039b67a34862fa9f54 (patch)
tree41890a5470e013c31e89d00fe6a134434f3417d4 /src/inotify/inutil/inotify_util.go
parenta71e76c9ab160f0ab7abbfe6db500b3b178e57de (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.
Diffstat (limited to 'src/inotify/inutil/inotify_util.go')
-rw-r--r--src/inotify/inutil/inotify_util.go78
1 files changed, 78 insertions, 0 deletions
diff --git a/src/inotify/inutil/inotify_util.go b/src/inotify/inutil/inotify_util.go
new file mode 100644
index 0000000..46146f5
--- /dev/null
+++ b/src/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
+ }
+ }
+}