summaryrefslogtreecommitdiff
path: root/src
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
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')
-rw-r--r--src/inotify/inutil/inotify_util.go78
-rw-r--r--src/nshd/hackers_git/hackers.go9
-rw-r--r--src/nshd/hackers_git/hackers_watch.go110
3 files changed, 146 insertions, 51 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
+ }
+ }
+}
diff --git a/src/nshd/hackers_git/hackers.go b/src/nshd/hackers_git/hackers.go
index 446351d..f47868f 100644
--- a/src/nshd/hackers_git/hackers.go
+++ b/src/nshd/hackers_git/hackers.go
@@ -2,11 +2,11 @@ package hackers_git
import (
"inotify"
+ "inotify/inutil"
"nslcd_proto"
"nslcd_proto/util"
"nslcd_systemd"
"sd_daemon/logger"
- "sd_daemon/lsb"
"sync"
)
@@ -24,11 +24,12 @@ type Hackers struct {
util.NullBackend
Cfg Config
lock sync.RWMutex
+ workers sync.WaitGroup
users map[int32]user
groups map[string]map[string]bool
- in_fd *inotify.Inotify
+ in_fd *inutil.Watcher
in_wd_home inotify.Wd
in_wd_yaml inotify.Wd
in_uid2wd map[int32]inotify.Wd
@@ -44,10 +45,6 @@ func (o *Hackers) Init() error {
logger.Err("Could not initialize hackers.git: %v", err)
return err
}
- go func() {
- defer lsb.Recover()
- o.worker()
- }()
return nil
}
diff --git a/src/nshd/hackers_git/hackers_watch.go b/src/nshd/hackers_git/hackers_watch.go
index c10ec78..666237b 100644
--- a/src/nshd/hackers_git/hackers_watch.go
+++ b/src/nshd/hackers_git/hackers_watch.go
@@ -2,6 +2,7 @@ package hackers_git
import (
"inotify"
+ "inotify/inutil"
"os"
"path/filepath"
"sd_daemon/logger"
@@ -49,6 +50,7 @@ func (o *Hackers) unwatchHomedir(wd inotify.Wd) {
func (o *Hackers) close() {
if o.in_fd != nil {
o.in_fd.Close()
+ defer o.workers.Wait()
}
o.in_wd_home = -1
o.in_wd_yaml = -1
@@ -60,7 +62,7 @@ func (o *Hackers) close() {
func (o *Hackers) reload() (err error) {
o.close()
- o.in_fd, err = inotify.InotifyInit() ; if err != nil { return }
+ o.in_fd, err = inutil.WatcherInit() ; if err != nil { return }
o.in_wd_home, err = o.in_fd.AddWatch("/home" , in_DIR|in_CHILD_ADD); if err != nil { return }
o.in_wd_yaml, err = o.in_fd.AddWatch(o.Cfg.Yamldir, in_DIR|in_CHILD_ANY); if err != nil { return }
@@ -73,6 +75,12 @@ func (o *Hackers) reload() (err error) {
o.load_yaml_file(filename, false)
}
+ go func() {
+ defer lsb.Recover()
+ defer o.workers.Done()
+ o.worker()
+ }()
+
err = nil
return
}
@@ -160,55 +168,67 @@ func (o *Hackers) worker() {
if err != nil {
worker_error("failed to load %q: %v", o.Cfg.Yamldir, err)
}
- for event, err := o.in_fd.Read(); err == nil; event, err = o.in_fd.Read() {
- switch event.Wd {
- case o.in_wd_yaml:
- // handle updates to yaml files
- if event.Mask&in_DIR_INVALID != 0 {
- err := o.Reload()
- if err != nil {
- worker_error("failed to reload hackers.git yaml directory: %v", err)
-
- }
- err = os.Chdir(o.Cfg.Yamldir)
- if err != nil {
- worker_error("failed to load %q: %v", o.Cfg.Yamldir, err)
+Loop:
+ for {
+ select {
+ case event, ok := <-o.in_fd.Events:
+ if !ok {
+ break Loop
+ }
+ switch event.Wd {
+ case o.in_wd_yaml:
+ // handle updates to yaml files
+ if event.Mask&in_DIR_INVALID != 0 {
+ err := o.Reload()
+ if err != nil {
+ worker_error("failed to reload hackers.git yaml directory: %v", err)
+
+ }
+ err = os.Chdir(o.Cfg.Yamldir)
+ if err != nil {
+ worker_error("failed to load %q: %v", o.Cfg.Yamldir, err)
+ }
+ } else if event.Mask&in_CHILD_ANY != 0 {
+ if event.Name == nil {
+ panic("recieved child event from inotify, but no child name")
+ }
+ o.load_yaml_file(o.Cfg.Yamldir + "/" + *event.Name, true)
+ } else {
+ panic("recieved non-subscribed inotify event from kernel")
}
- } else if event.Mask&in_CHILD_ANY != 0 {
- if event.Name == nil {
- panic("recieved child event from inotify, but no child name")
+ case o.in_wd_home:
+ if event.Mask&in_DIR_INVALID != 0 {
+ err := o.Reload()
+ if err != nil {
+ panic(err)
+ }
+ } else if event.Mask&inotify.IN_ISDIR != 0 {
+ // handle added home directory
+ o.worker_watch_homedirs()
}
- o.load_yaml_file(o.Cfg.Yamldir + "/" + *event.Name, true)
- } else {
- panic("recieved non-subscribed inotify event from kernel")
- }
- case o.in_wd_home:
- if event.Mask&in_DIR_INVALID != 0 {
- err := o.Reload()
- if err != nil {
- panic(err)
+ default:
+ // handle a change to someone's password
+ if event.Mask&in_DIR_INVALID != 0 {
+ o.unwatchHomedir(event.Wd)
+ o.worker_watch_homedirs()
+ } else if event.Name != nil {
+ if *event.Name == ".password" {
+ func() {
+ o.lock.Lock()
+ defer o.lock.Unlock()
+ o.load_user_password(o.in_wd2uid[event.Wd])
+ }()
+ }
+ } else {
+ logger.Debug("hackers.git: event didn't match: %#v", event)
}
- } else if event.Mask&inotify.IN_ISDIR != 0 {
- // handle added home directory
- o.worker_watch_homedirs()
}
- default:
- // handle a change to someone's password
- if event.Mask&in_DIR_INVALID != 0 {
- o.unwatchHomedir(event.Wd)
- o.worker_watch_homedirs()
- } else if event.Name != nil {
- if *event.Name == ".password" {
- func() {
- o.lock.Lock()
- defer o.lock.Unlock()
- o.load_user_password(o.in_wd2uid[event.Wd])
- }()
- }
- } else {
- logger.Debug("hackers.git: event didn't match: %#v", event)
+ case err, ok := <-o.in_fd.Errors:
+ if !ok {
+ break Loop
}
+ logger.Warning("hackers.git: inotify error: %v", err)
}
}
- logger.Info("Stopped hackers.git inotify watcher")
+ logger.Info("hackers.git: Stopped inotify watcher")
}