From b190157b8c568922f7f9b4039b67a34862fa9f54 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. --- src/nshd/hackers_git/hackers_watch.go | 110 ++++++++++++++++++++-------------- 1 file changed, 65 insertions(+), 45 deletions(-) (limited to 'src/nshd/hackers_git/hackers_watch.go') 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") } -- cgit v1.2.3