github.com/pusher/oauth2_proxy@v3.2.0+incompatible/watcher.go (about) 1 // +build go1.3,!plan9,!solaris 2 3 package main 4 5 import ( 6 "log" 7 "os" 8 "path/filepath" 9 "time" 10 11 fsnotify "gopkg.in/fsnotify/fsnotify.v1" 12 ) 13 14 // WaitForReplacement waits for a file to exist on disk and then starts a watch 15 // for the file 16 func WaitForReplacement(filename string, op fsnotify.Op, 17 watcher *fsnotify.Watcher) { 18 const sleepInterval = 50 * time.Millisecond 19 20 // Avoid a race when fsnofity.Remove is preceded by fsnotify.Chmod. 21 if op&fsnotify.Chmod != 0 { 22 time.Sleep(sleepInterval) 23 } 24 for { 25 if _, err := os.Stat(filename); err == nil { 26 if err := watcher.Add(filename); err == nil { 27 log.Printf("watching resumed for %s", filename) 28 return 29 } 30 } 31 time.Sleep(sleepInterval) 32 } 33 } 34 35 // WatchForUpdates performs an action every time a file on disk is updated 36 func WatchForUpdates(filename string, done <-chan bool, action func()) { 37 filename = filepath.Clean(filename) 38 watcher, err := fsnotify.NewWatcher() 39 if err != nil { 40 log.Fatal("failed to create watcher for ", filename, ": ", err) 41 } 42 go func() { 43 defer watcher.Close() 44 for { 45 select { 46 case _ = <-done: 47 log.Printf("Shutting down watcher for: %s", filename) 48 return 49 case event := <-watcher.Events: 50 // On Arch Linux, it appears Chmod events precede Remove events, 51 // which causes a race between action() and the coming Remove event. 52 // If the Remove wins, the action() (which calls 53 // UserMap.LoadAuthenticatedEmailsFile()) crashes when the file 54 // can't be opened. 55 if event.Op&(fsnotify.Remove|fsnotify.Rename|fsnotify.Chmod) != 0 { 56 log.Printf("watching interrupted on event: %s", event) 57 watcher.Remove(filename) 58 WaitForReplacement(filename, event.Op, watcher) 59 } 60 log.Printf("reloading after event: %s", event) 61 action() 62 case err = <-watcher.Errors: 63 log.Printf("error watching %s: %s", filename, err) 64 } 65 } 66 }() 67 if err = watcher.Add(filename); err != nil { 68 log.Fatal("failed to add ", filename, " to watcher: ", err) 69 } 70 log.Printf("watching %s for updates", filename) 71 }