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  }