diff --git a/README.md b/README.md index c62ec3b..dc5c666 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ NOTE: Version 0.7.0 switched to the [kingpin](https://github.com/alecthomas/king The `statsd_exporter` can be configured to translate specific dot-separated StatsD metrics into labeled Prometheus metrics via a simple mapping language. The config -file is watched for changes and automatically reloaded. +file is reloaded on SIGHUP. A mapping definition starts with a line matching the StatsD metric in question, with `*`s acting as wildcards for each dot-separated metric component. The diff --git a/go.mod b/go.mod index c837907..e224e29 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,6 @@ module github.com/prometheus/statsd_exporter require ( github.com/hashicorp/golang-lru v0.5.1 - github.com/howeyc/fsnotify v0.0.0-20151003194602-f0c08ee9c607 github.com/kr/pretty v0.1.0 // indirect github.com/prometheus/client_golang v1.0.0 github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 diff --git a/go.sum b/go.sum index 1f17f14..b7ac6f4 100644 --- a/go.sum +++ b/go.sum @@ -19,8 +19,6 @@ github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/howeyc/fsnotify v0.0.0-20151003194602-f0c08ee9c607 h1:+7wvV++11s0Okyl1dekihkIiCIYDz+Qk2LvxAShINU4= -github.com/howeyc/fsnotify v0.0.0-20151003194602-f0c08ee9c607/go.mod h1:41HzSPxBGeFRQKEEwgh49TRw/nKBsYZ2cF1OzPjSJsA= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= diff --git a/main.go b/main.go index ade979b..fcc1bec 100644 --- a/main.go +++ b/main.go @@ -23,7 +23,6 @@ import ( "strconv" "syscall" - "github.com/howeyc/fsnotify" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/common/log" @@ -91,35 +90,24 @@ func tcpAddrFromString(addr string) *net.TCPAddr { } } -func watchConfig(fileName string, mapper *mapper.MetricMapper, cacheSize int) { - watcher, err := fsnotify.NewWatcher() - if err != nil { - log.Fatal(err) - } +func configReloader(fileName string, mapper *mapper.MetricMapper, cacheSize int) { - err = watcher.WatchFlags(fileName, fsnotify.FSN_MODIFY) - if err != nil { - log.Fatal(err) - } + signals := make(chan os.Signal, 1) + signal.Notify(signals, syscall.SIGHUP) - for { - select { - case ev := <-watcher.Event: - log.Infof("Config file changed (%s), attempting reload", ev) - err = mapper.InitFromFile(fileName, cacheSize) - if err != nil { - log.Errorln("Error reloading config:", err) - configLoads.WithLabelValues("failure").Inc() - } else { - log.Infoln("Config reloaded successfully") - configLoads.WithLabelValues("success").Inc() - } - // Re-add the file watcher since it can get lost on some changes. E.g. - // saving a file with vim results in a RENAME-MODIFY-DELETE event - // sequence, after which the newly written file is no longer watched. - _ = watcher.WatchFlags(fileName, fsnotify.FSN_MODIFY) - case err := <-watcher.Error: - log.Errorln("Error watching config:", err) + for s := range signals { + if fileName == "" { + log.Warnf("Received %s but no mapping config to reload", s) + continue + } + log.Infof("Received %s, attempting reload", s) + err := mapper.InitFromFile(fileName, cacheSize) + if err != nil { + log.Errorln("Error reloading config:", err) + configLoads.WithLabelValues("failure").Inc() + } else { + log.Infoln("Config reloaded successfully") + configLoads.WithLabelValues("success").Inc() } } } @@ -262,10 +250,12 @@ func main() { log.Fatal("Error dumping FSM:", err) } } - go watchConfig(*mappingConfig, mapper, *cacheSize) } else { mapper.InitCache(*cacheSize) } + + go configReloader(*mappingConfig, mapper, *cacheSize) + exporter := NewExporter(mapper) signals := make(chan os.Signal, 1) diff --git a/vendor/github.com/howeyc/fsnotify/.gitignore b/vendor/github.com/howeyc/fsnotify/.gitignore deleted file mode 100644 index e4706a9..0000000 --- a/vendor/github.com/howeyc/fsnotify/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -# Setup a Global .gitignore for OS and editor generated files: -# https://help.github.com/articles/ignoring-files -# git config --global core.excludesfile ~/.gitignore_global - -.vagrant diff --git a/vendor/github.com/howeyc/fsnotify/AUTHORS b/vendor/github.com/howeyc/fsnotify/AUTHORS deleted file mode 100644 index e52b72f..0000000 --- a/vendor/github.com/howeyc/fsnotify/AUTHORS +++ /dev/null @@ -1,28 +0,0 @@ -# Names should be added to this file as -# Name or Organization -# The email address is not required for organizations. - -# You can update this list using the following command: -# -# $ git shortlog -se | awk '{print $2 " " $3 " " $4}' - -# Please keep the list sorted. - -Adrien Bustany -Caleb Spare -Case Nelson -Chris Howey -Christoffer Buchholz -Dave Cheney -Francisco Souza -John C Barstow -Kelvin Fo -Nathan Youngman -Paul Hammond -Pursuit92 -Rob Figueiredo -Travis Cline -Tudor Golubenco -bronze1man -debrando -henrikedwards diff --git a/vendor/github.com/howeyc/fsnotify/CHANGELOG.md b/vendor/github.com/howeyc/fsnotify/CHANGELOG.md deleted file mode 100644 index 761686a..0000000 --- a/vendor/github.com/howeyc/fsnotify/CHANGELOG.md +++ /dev/null @@ -1,160 +0,0 @@ -# Changelog - -## v0.9.0 / 2014-01-17 - -* IsAttrib() for events that only concern a file's metadata [#79][] (thanks @abustany) -* [Fix] kqueue: fix deadlock [#77][] (thanks @cespare) -* [NOTICE] Development has moved to `code.google.com/p/go.exp/fsnotify` in preparation for inclusion in the Go standard library. - -## v0.8.12 / 2013-11-13 - -* [API] Remove FD_SET and friends from Linux adapter - -## v0.8.11 / 2013-11-02 - -* [Doc] Add Changelog [#72][] (thanks @nathany) -* [Doc] Spotlight and double modify events on OS X [#62][] (reported by @paulhammond) - -## v0.8.10 / 2013-10-19 - -* [Fix] kqueue: remove file watches when parent directory is removed [#71][] (reported by @mdwhatcott) -* [Fix] kqueue: race between Close and readEvents [#70][] (reported by @bernerdschaefer) -* [Doc] specify OS-specific limits in README (thanks @debrando) - -## v0.8.9 / 2013-09-08 - -* [Doc] Contributing (thanks @nathany) -* [Doc] update package path in example code [#63][] (thanks @paulhammond) -* [Doc] GoCI badge in README (Linux only) [#60][] -* [Doc] Cross-platform testing with Vagrant [#59][] (thanks @nathany) - -## v0.8.8 / 2013-06-17 - -* [Fix] Windows: handle `ERROR_MORE_DATA` on Windows [#49][] (thanks @jbowtie) - -## v0.8.7 / 2013-06-03 - -* [API] Make syscall flags internal -* [Fix] inotify: ignore event changes -* [Fix] race in symlink test [#45][] (reported by @srid) -* [Fix] tests on Windows -* lower case error messages - -## v0.8.6 / 2013-05-23 - -* kqueue: Use EVT_ONLY flag on Darwin -* [Doc] Update README with full example - -## v0.8.5 / 2013-05-09 - -* [Fix] inotify: allow monitoring of "broken" symlinks (thanks @tsg) - -## v0.8.4 / 2013-04-07 - -* [Fix] kqueue: watch all file events [#40][] (thanks @ChrisBuchholz) - -## v0.8.3 / 2013-03-13 - -* [Fix] inoitfy/kqueue memory leak [#36][] (reported by @nbkolchin) -* [Fix] kqueue: use fsnFlags for watching a directory [#33][] (reported by @nbkolchin) - -## v0.8.2 / 2013-02-07 - -* [Doc] add Authors -* [Fix] fix data races for map access [#29][] (thanks @fsouza) - -## v0.8.1 / 2013-01-09 - -* [Fix] Windows path separators -* [Doc] BSD License - -## v0.8.0 / 2012-11-09 - -* kqueue: directory watching improvements (thanks @vmirage) -* inotify: add `IN_MOVED_TO` [#25][] (requested by @cpisto) -* [Fix] kqueue: deleting watched directory [#24][] (reported by @jakerr) - -## v0.7.4 / 2012-10-09 - -* [Fix] inotify: fixes from https://codereview.appspot.com/5418045/ (ugorji) -* [Fix] kqueue: preserve watch flags when watching for delete [#21][] (reported by @robfig) -* [Fix] kqueue: watch the directory even if it isn't a new watch (thanks @robfig) -* [Fix] kqueue: modify after recreation of file - -## v0.7.3 / 2012-09-27 - -* [Fix] kqueue: watch with an existing folder inside the watched folder (thanks @vmirage) -* [Fix] kqueue: no longer get duplicate CREATE events - -## v0.7.2 / 2012-09-01 - -* kqueue: events for created directories - -## v0.7.1 / 2012-07-14 - -* [Fix] for renaming files - -## v0.7.0 / 2012-07-02 - -* [Feature] FSNotify flags -* [Fix] inotify: Added file name back to event path - -## v0.6.0 / 2012-06-06 - -* kqueue: watch files after directory created (thanks @tmc) - -## v0.5.1 / 2012-05-22 - -* [Fix] inotify: remove all watches before Close() - -## v0.5.0 / 2012-05-03 - -* [API] kqueue: return errors during watch instead of sending over channel -* kqueue: match symlink behavior on Linux -* inotify: add `DELETE_SELF` (requested by @taralx) -* [Fix] kqueue: handle EINTR (reported by @robfig) -* [Doc] Godoc example [#1][] (thanks @davecheney) - -## v0.4.0 / 2012-03-30 - -* Go 1 released: build with go tool -* [Feature] Windows support using winfsnotify -* Windows does not have attribute change notifications -* Roll attribute notifications into IsModify - -## v0.3.0 / 2012-02-19 - -* kqueue: add files when watch directory - -## v0.2.0 / 2011-12-30 - -* update to latest Go weekly code - -## v0.1.0 / 2011-10-19 - -* kqueue: add watch on file creation to match inotify -* kqueue: create file event -* inotify: ignore `IN_IGNORED` events -* event String() -* linux: common FileEvent functions -* initial commit - -[#79]: https://github.com/howeyc/fsnotify/pull/79 -[#77]: https://github.com/howeyc/fsnotify/pull/77 -[#72]: https://github.com/howeyc/fsnotify/issues/72 -[#71]: https://github.com/howeyc/fsnotify/issues/71 -[#70]: https://github.com/howeyc/fsnotify/issues/70 -[#63]: https://github.com/howeyc/fsnotify/issues/63 -[#62]: https://github.com/howeyc/fsnotify/issues/62 -[#60]: https://github.com/howeyc/fsnotify/issues/60 -[#59]: https://github.com/howeyc/fsnotify/issues/59 -[#49]: https://github.com/howeyc/fsnotify/issues/49 -[#45]: https://github.com/howeyc/fsnotify/issues/45 -[#40]: https://github.com/howeyc/fsnotify/issues/40 -[#36]: https://github.com/howeyc/fsnotify/issues/36 -[#33]: https://github.com/howeyc/fsnotify/issues/33 -[#29]: https://github.com/howeyc/fsnotify/issues/29 -[#25]: https://github.com/howeyc/fsnotify/issues/25 -[#24]: https://github.com/howeyc/fsnotify/issues/24 -[#21]: https://github.com/howeyc/fsnotify/issues/21 -[#1]: https://github.com/howeyc/fsnotify/issues/1 diff --git a/vendor/github.com/howeyc/fsnotify/CONTRIBUTING.md b/vendor/github.com/howeyc/fsnotify/CONTRIBUTING.md deleted file mode 100644 index b2025d7..0000000 --- a/vendor/github.com/howeyc/fsnotify/CONTRIBUTING.md +++ /dev/null @@ -1,7 +0,0 @@ -# Contributing - -## Moving Notice - -There is a fork being actively developed with a new API in preparation for the Go Standard Library: -[github.com/go-fsnotify/fsnotify](https://github.com/go-fsnotify/fsnotify) - diff --git a/vendor/github.com/howeyc/fsnotify/LICENSE b/vendor/github.com/howeyc/fsnotify/LICENSE deleted file mode 100644 index f21e540..0000000 --- a/vendor/github.com/howeyc/fsnotify/LICENSE +++ /dev/null @@ -1,28 +0,0 @@ -Copyright (c) 2012 The Go Authors. All rights reserved. -Copyright (c) 2012 fsnotify Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/howeyc/fsnotify/README.md b/vendor/github.com/howeyc/fsnotify/README.md deleted file mode 100644 index 7fdaf7c..0000000 --- a/vendor/github.com/howeyc/fsnotify/README.md +++ /dev/null @@ -1,93 +0,0 @@ -# File system notifications for Go - -[![GoDoc](https://godoc.org/github.com/howeyc/fsnotify?status.png)](http://godoc.org/github.com/howeyc/fsnotify) - -Cross platform: Windows, Linux, BSD and OS X. - -## Moving Notice - -There is a fork being actively developed with a new API in preparation for the Go Standard Library: -[github.com/go-fsnotify/fsnotify](https://github.com/go-fsnotify/fsnotify) - -## Example: - -```go -package main - -import ( - "log" - - "github.com/howeyc/fsnotify" -) - -func main() { - watcher, err := fsnotify.NewWatcher() - if err != nil { - log.Fatal(err) - } - - done := make(chan bool) - - // Process events - go func() { - for { - select { - case ev := <-watcher.Event: - log.Println("event:", ev) - case err := <-watcher.Error: - log.Println("error:", err) - } - } - }() - - err = watcher.Watch("testDir") - if err != nil { - log.Fatal(err) - } - - // Hang so program doesn't exit - <-done - - /* ... do stuff ... */ - watcher.Close() -} -``` - -For each event: -* Name -* IsCreate() -* IsDelete() -* IsModify() -* IsRename() - -## FAQ - -**When a file is moved to another directory is it still being watched?** - -No (it shouldn't be, unless you are watching where it was moved to). - -**When I watch a directory, are all subdirectories watched as well?** - -No, you must add watches for any directory you want to watch (a recursive watcher is in the works [#56][]). - -**Do I have to watch the Error and Event channels in a separate goroutine?** - -As of now, yes. Looking into making this single-thread friendly (see [#7][]) - -**Why am I receiving multiple events for the same file on OS X?** - -Spotlight indexing on OS X can result in multiple events (see [#62][]). A temporary workaround is to add your folder(s) to the *Spotlight Privacy settings* until we have a native FSEvents implementation (see [#54][]). - -**How many files can be watched at once?** - -There are OS-specific limits as to how many watches can be created: -* Linux: /proc/sys/fs/inotify/max_user_watches contains the limit, -reaching this limit results in a "no space left on device" error. -* BSD / OSX: sysctl variables "kern.maxfiles" and "kern.maxfilesperproc", reaching these limits results in a "too many open files" error. - - -[#62]: https://github.com/howeyc/fsnotify/issues/62 -[#56]: https://github.com/howeyc/fsnotify/issues/56 -[#54]: https://github.com/howeyc/fsnotify/issues/54 -[#7]: https://github.com/howeyc/fsnotify/issues/7 - diff --git a/vendor/github.com/howeyc/fsnotify/fsnotify.go b/vendor/github.com/howeyc/fsnotify/fsnotify.go deleted file mode 100644 index 9a48d84..0000000 --- a/vendor/github.com/howeyc/fsnotify/fsnotify.go +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package fsnotify implements file system notification. -package fsnotify - -import "fmt" - -const ( - FSN_CREATE = 1 - FSN_MODIFY = 2 - FSN_DELETE = 4 - FSN_RENAME = 8 - - FSN_ALL = FSN_MODIFY | FSN_DELETE | FSN_RENAME | FSN_CREATE -) - -// Purge events from interal chan to external chan if passes filter -func (w *Watcher) purgeEvents() { - for ev := range w.internalEvent { - sendEvent := false - w.fsnmut.Lock() - fsnFlags := w.fsnFlags[ev.Name] - w.fsnmut.Unlock() - - if (fsnFlags&FSN_CREATE == FSN_CREATE) && ev.IsCreate() { - sendEvent = true - } - - if (fsnFlags&FSN_MODIFY == FSN_MODIFY) && ev.IsModify() { - sendEvent = true - } - - if (fsnFlags&FSN_DELETE == FSN_DELETE) && ev.IsDelete() { - sendEvent = true - } - - if (fsnFlags&FSN_RENAME == FSN_RENAME) && ev.IsRename() { - sendEvent = true - } - - if sendEvent { - w.Event <- ev - } - - // If there's no file, then no more events for user - // BSD must keep watch for internal use (watches DELETEs to keep track - // what files exist for create events) - if ev.IsDelete() { - w.fsnmut.Lock() - delete(w.fsnFlags, ev.Name) - w.fsnmut.Unlock() - } - } - - close(w.Event) -} - -// Watch a given file path -func (w *Watcher) Watch(path string) error { - return w.WatchFlags(path, FSN_ALL) -} - -// Watch a given file path for a particular set of notifications (FSN_MODIFY etc.) -func (w *Watcher) WatchFlags(path string, flags uint32) error { - w.fsnmut.Lock() - w.fsnFlags[path] = flags - w.fsnmut.Unlock() - return w.watch(path) -} - -// Remove a watch on a file -func (w *Watcher) RemoveWatch(path string) error { - w.fsnmut.Lock() - delete(w.fsnFlags, path) - w.fsnmut.Unlock() - return w.removeWatch(path) -} - -// String formats the event e in the form -// "filename: DELETE|MODIFY|..." -func (e *FileEvent) String() string { - var events string = "" - - if e.IsCreate() { - events += "|" + "CREATE" - } - - if e.IsDelete() { - events += "|" + "DELETE" - } - - if e.IsModify() { - events += "|" + "MODIFY" - } - - if e.IsRename() { - events += "|" + "RENAME" - } - - if e.IsAttrib() { - events += "|" + "ATTRIB" - } - - if len(events) > 0 { - events = events[1:] - } - - return fmt.Sprintf("%q: %s", e.Name, events) -} diff --git a/vendor/github.com/howeyc/fsnotify/fsnotify_bsd.go b/vendor/github.com/howeyc/fsnotify/fsnotify_bsd.go deleted file mode 100644 index 40c8476..0000000 --- a/vendor/github.com/howeyc/fsnotify/fsnotify_bsd.go +++ /dev/null @@ -1,496 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build freebsd openbsd netbsd dragonfly darwin - -package fsnotify - -import ( - "errors" - "fmt" - "io/ioutil" - "os" - "path/filepath" - "sync" - "syscall" -) - -const ( - // Flags (from ) - sys_NOTE_DELETE = 0x0001 /* vnode was removed */ - sys_NOTE_WRITE = 0x0002 /* data contents changed */ - sys_NOTE_EXTEND = 0x0004 /* size increased */ - sys_NOTE_ATTRIB = 0x0008 /* attributes changed */ - sys_NOTE_LINK = 0x0010 /* link count changed */ - sys_NOTE_RENAME = 0x0020 /* vnode was renamed */ - sys_NOTE_REVOKE = 0x0040 /* vnode access was revoked */ - - // Watch all events - sys_NOTE_ALLEVENTS = sys_NOTE_DELETE | sys_NOTE_WRITE | sys_NOTE_ATTRIB | sys_NOTE_RENAME - - // Block for 100 ms on each call to kevent - keventWaitTime = 100e6 -) - -type FileEvent struct { - mask uint32 // Mask of events - Name string // File name (optional) - create bool // set by fsnotify package if found new file -} - -// IsCreate reports whether the FileEvent was triggered by a creation -func (e *FileEvent) IsCreate() bool { return e.create } - -// IsDelete reports whether the FileEvent was triggered by a delete -func (e *FileEvent) IsDelete() bool { return (e.mask & sys_NOTE_DELETE) == sys_NOTE_DELETE } - -// IsModify reports whether the FileEvent was triggered by a file modification -func (e *FileEvent) IsModify() bool { - return ((e.mask&sys_NOTE_WRITE) == sys_NOTE_WRITE || (e.mask&sys_NOTE_ATTRIB) == sys_NOTE_ATTRIB) -} - -// IsRename reports whether the FileEvent was triggered by a change name -func (e *FileEvent) IsRename() bool { return (e.mask & sys_NOTE_RENAME) == sys_NOTE_RENAME } - -// IsAttrib reports whether the FileEvent was triggered by a change in the file metadata. -func (e *FileEvent) IsAttrib() bool { - return (e.mask & sys_NOTE_ATTRIB) == sys_NOTE_ATTRIB -} - -type Watcher struct { - mu sync.Mutex // Mutex for the Watcher itself. - kq int // File descriptor (as returned by the kqueue() syscall) - watches map[string]int // Map of watched file descriptors (key: path) - wmut sync.Mutex // Protects access to watches. - fsnFlags map[string]uint32 // Map of watched files to flags used for filter - fsnmut sync.Mutex // Protects access to fsnFlags. - enFlags map[string]uint32 // Map of watched files to evfilt note flags used in kqueue - enmut sync.Mutex // Protects access to enFlags. - paths map[int]string // Map of watched paths (key: watch descriptor) - finfo map[int]os.FileInfo // Map of file information (isDir, isReg; key: watch descriptor) - pmut sync.Mutex // Protects access to paths and finfo. - fileExists map[string]bool // Keep track of if we know this file exists (to stop duplicate create events) - femut sync.Mutex // Protects access to fileExists. - externalWatches map[string]bool // Map of watches added by user of the library. - ewmut sync.Mutex // Protects access to externalWatches. - Error chan error // Errors are sent on this channel - internalEvent chan *FileEvent // Events are queued on this channel - Event chan *FileEvent // Events are returned on this channel - done chan bool // Channel for sending a "quit message" to the reader goroutine - isClosed bool // Set to true when Close() is first called -} - -// NewWatcher creates and returns a new kevent instance using kqueue(2) -func NewWatcher() (*Watcher, error) { - fd, errno := syscall.Kqueue() - if fd == -1 { - return nil, os.NewSyscallError("kqueue", errno) - } - w := &Watcher{ - kq: fd, - watches: make(map[string]int), - fsnFlags: make(map[string]uint32), - enFlags: make(map[string]uint32), - paths: make(map[int]string), - finfo: make(map[int]os.FileInfo), - fileExists: make(map[string]bool), - externalWatches: make(map[string]bool), - internalEvent: make(chan *FileEvent), - Event: make(chan *FileEvent), - Error: make(chan error), - done: make(chan bool, 1), - } - - go w.readEvents() - go w.purgeEvents() - return w, nil -} - -// Close closes a kevent watcher instance -// It sends a message to the reader goroutine to quit and removes all watches -// associated with the kevent instance -func (w *Watcher) Close() error { - w.mu.Lock() - if w.isClosed { - w.mu.Unlock() - return nil - } - w.isClosed = true - w.mu.Unlock() - - // Send "quit" message to the reader goroutine - w.done <- true - w.wmut.Lock() - ws := w.watches - w.wmut.Unlock() - for path := range ws { - w.removeWatch(path) - } - - return nil -} - -// AddWatch adds path to the watched file set. -// The flags are interpreted as described in kevent(2). -func (w *Watcher) addWatch(path string, flags uint32) error { - w.mu.Lock() - if w.isClosed { - w.mu.Unlock() - return errors.New("kevent instance already closed") - } - w.mu.Unlock() - - watchDir := false - - w.wmut.Lock() - watchfd, found := w.watches[path] - w.wmut.Unlock() - if !found { - fi, errstat := os.Lstat(path) - if errstat != nil { - return errstat - } - - // don't watch socket - if fi.Mode()&os.ModeSocket == os.ModeSocket { - return nil - } - - // Follow Symlinks - // Unfortunately, Linux can add bogus symlinks to watch list without - // issue, and Windows can't do symlinks period (AFAIK). To maintain - // consistency, we will act like everything is fine. There will simply - // be no file events for broken symlinks. - // Hence the returns of nil on errors. - if fi.Mode()&os.ModeSymlink == os.ModeSymlink { - path, err := filepath.EvalSymlinks(path) - if err != nil { - return nil - } - - fi, errstat = os.Lstat(path) - if errstat != nil { - return nil - } - } - - fd, errno := syscall.Open(path, open_FLAGS, 0700) - if fd == -1 { - return errno - } - watchfd = fd - - w.wmut.Lock() - w.watches[path] = watchfd - w.wmut.Unlock() - - w.pmut.Lock() - w.paths[watchfd] = path - w.finfo[watchfd] = fi - w.pmut.Unlock() - } - // Watch the directory if it has not been watched before. - w.pmut.Lock() - w.enmut.Lock() - if w.finfo[watchfd].IsDir() && - (flags&sys_NOTE_WRITE) == sys_NOTE_WRITE && - (!found || (w.enFlags[path]&sys_NOTE_WRITE) != sys_NOTE_WRITE) { - watchDir = true - } - w.enmut.Unlock() - w.pmut.Unlock() - - w.enmut.Lock() - w.enFlags[path] = flags - w.enmut.Unlock() - - var kbuf [1]syscall.Kevent_t - watchEntry := &kbuf[0] - watchEntry.Fflags = flags - syscall.SetKevent(watchEntry, watchfd, syscall.EVFILT_VNODE, syscall.EV_ADD|syscall.EV_CLEAR) - entryFlags := watchEntry.Flags - success, errno := syscall.Kevent(w.kq, kbuf[:], nil, nil) - if success == -1 { - return errno - } else if (entryFlags & syscall.EV_ERROR) == syscall.EV_ERROR { - return errors.New("kevent add error") - } - - if watchDir { - errdir := w.watchDirectoryFiles(path) - if errdir != nil { - return errdir - } - } - return nil -} - -// Watch adds path to the watched file set, watching all events. -func (w *Watcher) watch(path string) error { - w.ewmut.Lock() - w.externalWatches[path] = true - w.ewmut.Unlock() - return w.addWatch(path, sys_NOTE_ALLEVENTS) -} - -// RemoveWatch removes path from the watched file set. -func (w *Watcher) removeWatch(path string) error { - w.wmut.Lock() - watchfd, ok := w.watches[path] - w.wmut.Unlock() - if !ok { - return errors.New(fmt.Sprintf("can't remove non-existent kevent watch for: %s", path)) - } - var kbuf [1]syscall.Kevent_t - watchEntry := &kbuf[0] - syscall.SetKevent(watchEntry, watchfd, syscall.EVFILT_VNODE, syscall.EV_DELETE) - entryFlags := watchEntry.Flags - success, errno := syscall.Kevent(w.kq, kbuf[:], nil, nil) - if success == -1 { - return os.NewSyscallError("kevent_rm_watch", errno) - } else if (entryFlags & syscall.EV_ERROR) == syscall.EV_ERROR { - return errors.New("kevent rm error") - } - syscall.Close(watchfd) - w.wmut.Lock() - delete(w.watches, path) - w.wmut.Unlock() - w.enmut.Lock() - delete(w.enFlags, path) - w.enmut.Unlock() - w.pmut.Lock() - delete(w.paths, watchfd) - fInfo := w.finfo[watchfd] - delete(w.finfo, watchfd) - w.pmut.Unlock() - - // Find all watched paths that are in this directory that are not external. - if fInfo.IsDir() { - var pathsToRemove []string - w.pmut.Lock() - for _, wpath := range w.paths { - wdir, _ := filepath.Split(wpath) - if filepath.Clean(wdir) == filepath.Clean(path) { - w.ewmut.Lock() - if !w.externalWatches[wpath] { - pathsToRemove = append(pathsToRemove, wpath) - } - w.ewmut.Unlock() - } - } - w.pmut.Unlock() - for _, p := range pathsToRemove { - // Since these are internal, not much sense in propagating error - // to the user, as that will just confuse them with an error about - // a path they did not explicitly watch themselves. - w.removeWatch(p) - } - } - - return nil -} - -// readEvents reads from the kqueue file descriptor, converts the -// received events into Event objects and sends them via the Event channel -func (w *Watcher) readEvents() { - var ( - eventbuf [10]syscall.Kevent_t // Event buffer - events []syscall.Kevent_t // Received events - twait *syscall.Timespec // Time to block waiting for events - n int // Number of events returned from kevent - errno error // Syscall errno - ) - events = eventbuf[0:0] - twait = new(syscall.Timespec) - *twait = syscall.NsecToTimespec(keventWaitTime) - - for { - // See if there is a message on the "done" channel - var done bool - select { - case done = <-w.done: - default: - } - - // If "done" message is received - if done { - errno := syscall.Close(w.kq) - if errno != nil { - w.Error <- os.NewSyscallError("close", errno) - } - close(w.internalEvent) - close(w.Error) - return - } - - // Get new events - if len(events) == 0 { - n, errno = syscall.Kevent(w.kq, nil, eventbuf[:], twait) - - // EINTR is okay, basically the syscall was interrupted before - // timeout expired. - if errno != nil && errno != syscall.EINTR { - w.Error <- os.NewSyscallError("kevent", errno) - continue - } - - // Received some events - if n > 0 { - events = eventbuf[0:n] - } - } - - // Flush the events we received to the events channel - for len(events) > 0 { - fileEvent := new(FileEvent) - watchEvent := &events[0] - fileEvent.mask = uint32(watchEvent.Fflags) - w.pmut.Lock() - fileEvent.Name = w.paths[int(watchEvent.Ident)] - fileInfo := w.finfo[int(watchEvent.Ident)] - w.pmut.Unlock() - if fileInfo != nil && fileInfo.IsDir() && !fileEvent.IsDelete() { - // Double check to make sure the directory exist. This can happen when - // we do a rm -fr on a recursively watched folders and we receive a - // modification event first but the folder has been deleted and later - // receive the delete event - if _, err := os.Lstat(fileEvent.Name); os.IsNotExist(err) { - // mark is as delete event - fileEvent.mask |= sys_NOTE_DELETE - } - } - - if fileInfo != nil && fileInfo.IsDir() && fileEvent.IsModify() && !fileEvent.IsDelete() { - w.sendDirectoryChangeEvents(fileEvent.Name) - } else { - // Send the event on the events channel - w.internalEvent <- fileEvent - } - - // Move to next event - events = events[1:] - - if fileEvent.IsRename() { - w.removeWatch(fileEvent.Name) - w.femut.Lock() - delete(w.fileExists, fileEvent.Name) - w.femut.Unlock() - } - if fileEvent.IsDelete() { - w.removeWatch(fileEvent.Name) - w.femut.Lock() - delete(w.fileExists, fileEvent.Name) - w.femut.Unlock() - - // Look for a file that may have overwritten this - // (ie mv f1 f2 will delete f2 then create f2) - fileDir, _ := filepath.Split(fileEvent.Name) - fileDir = filepath.Clean(fileDir) - w.wmut.Lock() - _, found := w.watches[fileDir] - w.wmut.Unlock() - if found { - // make sure the directory exist before we watch for changes. When we - // do a recursive watch and perform rm -fr, the parent directory might - // have gone missing, ignore the missing directory and let the - // upcoming delete event remove the watch form the parent folder - if _, err := os.Lstat(fileDir); !os.IsNotExist(err) { - w.sendDirectoryChangeEvents(fileDir) - } - } - } - } - } -} - -func (w *Watcher) watchDirectoryFiles(dirPath string) error { - // Get all files - files, err := ioutil.ReadDir(dirPath) - if err != nil { - return err - } - - // Search for new files - for _, fileInfo := range files { - filePath := filepath.Join(dirPath, fileInfo.Name()) - - // Inherit fsnFlags from parent directory - w.fsnmut.Lock() - if flags, found := w.fsnFlags[dirPath]; found { - w.fsnFlags[filePath] = flags - } else { - w.fsnFlags[filePath] = FSN_ALL - } - w.fsnmut.Unlock() - - if fileInfo.IsDir() == false { - // Watch file to mimic linux fsnotify - e := w.addWatch(filePath, sys_NOTE_ALLEVENTS) - if e != nil { - return e - } - } else { - // If the user is currently watching directory - // we want to preserve the flags used - w.enmut.Lock() - currFlags, found := w.enFlags[filePath] - w.enmut.Unlock() - var newFlags uint32 = sys_NOTE_DELETE - if found { - newFlags |= currFlags - } - - // Linux gives deletes if not explicitly watching - e := w.addWatch(filePath, newFlags) - if e != nil { - return e - } - } - w.femut.Lock() - w.fileExists[filePath] = true - w.femut.Unlock() - } - - return nil -} - -// sendDirectoryEvents searches the directory for newly created files -// and sends them over the event channel. This functionality is to have -// the BSD version of fsnotify match linux fsnotify which provides a -// create event for files created in a watched directory. -func (w *Watcher) sendDirectoryChangeEvents(dirPath string) { - // Get all files - files, err := ioutil.ReadDir(dirPath) - if err != nil { - w.Error <- err - } - - // Search for new files - for _, fileInfo := range files { - filePath := filepath.Join(dirPath, fileInfo.Name()) - w.femut.Lock() - _, doesExist := w.fileExists[filePath] - w.femut.Unlock() - if !doesExist { - // Inherit fsnFlags from parent directory - w.fsnmut.Lock() - if flags, found := w.fsnFlags[dirPath]; found { - w.fsnFlags[filePath] = flags - } else { - w.fsnFlags[filePath] = FSN_ALL - } - w.fsnmut.Unlock() - - // Send create event - fileEvent := new(FileEvent) - fileEvent.Name = filePath - fileEvent.create = true - w.internalEvent <- fileEvent - } - w.femut.Lock() - w.fileExists[filePath] = true - w.femut.Unlock() - } - w.watchDirectoryFiles(dirPath) -} diff --git a/vendor/github.com/howeyc/fsnotify/fsnotify_linux.go b/vendor/github.com/howeyc/fsnotify/fsnotify_linux.go deleted file mode 100644 index 80ade87..0000000 --- a/vendor/github.com/howeyc/fsnotify/fsnotify_linux.go +++ /dev/null @@ -1,304 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build linux - -package fsnotify - -import ( - "errors" - "fmt" - "os" - "strings" - "sync" - "syscall" - "unsafe" -) - -const ( - // Options for inotify_init() are not exported - // sys_IN_CLOEXEC uint32 = syscall.IN_CLOEXEC - // sys_IN_NONBLOCK uint32 = syscall.IN_NONBLOCK - - // Options for AddWatch - sys_IN_DONT_FOLLOW uint32 = syscall.IN_DONT_FOLLOW - sys_IN_ONESHOT uint32 = syscall.IN_ONESHOT - sys_IN_ONLYDIR uint32 = syscall.IN_ONLYDIR - - // The "sys_IN_MASK_ADD" option is not exported, as AddWatch - // adds it automatically, if there is already a watch for the given path - // sys_IN_MASK_ADD uint32 = syscall.IN_MASK_ADD - - // Events - sys_IN_ACCESS uint32 = syscall.IN_ACCESS - sys_IN_ALL_EVENTS uint32 = syscall.IN_ALL_EVENTS - sys_IN_ATTRIB uint32 = syscall.IN_ATTRIB - sys_IN_CLOSE uint32 = syscall.IN_CLOSE - sys_IN_CLOSE_NOWRITE uint32 = syscall.IN_CLOSE_NOWRITE - sys_IN_CLOSE_WRITE uint32 = syscall.IN_CLOSE_WRITE - sys_IN_CREATE uint32 = syscall.IN_CREATE - sys_IN_DELETE uint32 = syscall.IN_DELETE - sys_IN_DELETE_SELF uint32 = syscall.IN_DELETE_SELF - sys_IN_MODIFY uint32 = syscall.IN_MODIFY - sys_IN_MOVE uint32 = syscall.IN_MOVE - sys_IN_MOVED_FROM uint32 = syscall.IN_MOVED_FROM - sys_IN_MOVED_TO uint32 = syscall.IN_MOVED_TO - sys_IN_MOVE_SELF uint32 = syscall.IN_MOVE_SELF - sys_IN_OPEN uint32 = syscall.IN_OPEN - - sys_AGNOSTIC_EVENTS = sys_IN_MOVED_TO | sys_IN_MOVED_FROM | sys_IN_CREATE | sys_IN_ATTRIB | sys_IN_MODIFY | sys_IN_MOVE_SELF | sys_IN_DELETE | sys_IN_DELETE_SELF - - // Special events - sys_IN_ISDIR uint32 = syscall.IN_ISDIR - sys_IN_IGNORED uint32 = syscall.IN_IGNORED - sys_IN_Q_OVERFLOW uint32 = syscall.IN_Q_OVERFLOW - sys_IN_UNMOUNT uint32 = syscall.IN_UNMOUNT -) - -type FileEvent struct { - mask uint32 // Mask of events - cookie uint32 // Unique cookie associating related events (for rename(2)) - Name string // File name (optional) -} - -// IsCreate reports whether the FileEvent was triggered by a creation -func (e *FileEvent) IsCreate() bool { - return (e.mask&sys_IN_CREATE) == sys_IN_CREATE || (e.mask&sys_IN_MOVED_TO) == sys_IN_MOVED_TO -} - -// IsDelete reports whether the FileEvent was triggered by a delete -func (e *FileEvent) IsDelete() bool { - return (e.mask&sys_IN_DELETE_SELF) == sys_IN_DELETE_SELF || (e.mask&sys_IN_DELETE) == sys_IN_DELETE -} - -// IsModify reports whether the FileEvent was triggered by a file modification or attribute change -func (e *FileEvent) IsModify() bool { - return ((e.mask&sys_IN_MODIFY) == sys_IN_MODIFY || (e.mask&sys_IN_ATTRIB) == sys_IN_ATTRIB) -} - -// IsRename reports whether the FileEvent was triggered by a change name -func (e *FileEvent) IsRename() bool { - return ((e.mask&sys_IN_MOVE_SELF) == sys_IN_MOVE_SELF || (e.mask&sys_IN_MOVED_FROM) == sys_IN_MOVED_FROM) -} - -// IsAttrib reports whether the FileEvent was triggered by a change in the file metadata. -func (e *FileEvent) IsAttrib() bool { - return (e.mask & sys_IN_ATTRIB) == sys_IN_ATTRIB -} - -type watch struct { - wd uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall) - flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags) -} - -type Watcher struct { - mu sync.Mutex // Map access - fd int // File descriptor (as returned by the inotify_init() syscall) - watches map[string]*watch // Map of inotify watches (key: path) - fsnFlags map[string]uint32 // Map of watched files to flags used for filter - fsnmut sync.Mutex // Protects access to fsnFlags. - paths map[int]string // Map of watched paths (key: watch descriptor) - Error chan error // Errors are sent on this channel - internalEvent chan *FileEvent // Events are queued on this channel - Event chan *FileEvent // Events are returned on this channel - done chan bool // Channel for sending a "quit message" to the reader goroutine - isClosed bool // Set to true when Close() is first called -} - -// NewWatcher creates and returns a new inotify instance using inotify_init(2) -func NewWatcher() (*Watcher, error) { - fd, errno := syscall.InotifyInit() - if fd == -1 { - return nil, os.NewSyscallError("inotify_init", errno) - } - w := &Watcher{ - fd: fd, - watches: make(map[string]*watch), - fsnFlags: make(map[string]uint32), - paths: make(map[int]string), - internalEvent: make(chan *FileEvent), - Event: make(chan *FileEvent), - Error: make(chan error), - done: make(chan bool, 1), - } - - go w.readEvents() - go w.purgeEvents() - return w, nil -} - -// Close closes an inotify watcher instance -// It sends a message to the reader goroutine to quit and removes all watches -// associated with the inotify instance -func (w *Watcher) Close() error { - if w.isClosed { - return nil - } - w.isClosed = true - - // Remove all watches - for path := range w.watches { - w.RemoveWatch(path) - } - - // Send "quit" message to the reader goroutine - w.done <- true - - return nil -} - -// AddWatch adds path to the watched file set. -// The flags are interpreted as described in inotify_add_watch(2). -func (w *Watcher) addWatch(path string, flags uint32) error { - if w.isClosed { - return errors.New("inotify instance already closed") - } - - w.mu.Lock() - watchEntry, found := w.watches[path] - w.mu.Unlock() - if found { - watchEntry.flags |= flags - flags |= syscall.IN_MASK_ADD - } - wd, errno := syscall.InotifyAddWatch(w.fd, path, flags) - if wd == -1 { - return errno - } - - w.mu.Lock() - w.watches[path] = &watch{wd: uint32(wd), flags: flags} - w.paths[wd] = path - w.mu.Unlock() - - return nil -} - -// Watch adds path to the watched file set, watching all events. -func (w *Watcher) watch(path string) error { - return w.addWatch(path, sys_AGNOSTIC_EVENTS) -} - -// RemoveWatch removes path from the watched file set. -func (w *Watcher) removeWatch(path string) error { - w.mu.Lock() - defer w.mu.Unlock() - watch, ok := w.watches[path] - if !ok { - return errors.New(fmt.Sprintf("can't remove non-existent inotify watch for: %s", path)) - } - success, errno := syscall.InotifyRmWatch(w.fd, watch.wd) - if success == -1 { - return os.NewSyscallError("inotify_rm_watch", errno) - } - delete(w.watches, path) - return nil -} - -// readEvents reads from the inotify file descriptor, converts the -// received events into Event objects and sends them via the Event channel -func (w *Watcher) readEvents() { - var ( - buf [syscall.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events - n int // Number of bytes read with read() - errno error // Syscall errno - ) - - for { - // See if there is a message on the "done" channel - select { - case <-w.done: - syscall.Close(w.fd) - close(w.internalEvent) - close(w.Error) - return - default: - } - - n, errno = syscall.Read(w.fd, buf[:]) - - // If EOF is received - if n == 0 { - syscall.Close(w.fd) - close(w.internalEvent) - close(w.Error) - return - } - - if n < 0 { - w.Error <- os.NewSyscallError("read", errno) - continue - } - if n < syscall.SizeofInotifyEvent { - w.Error <- errors.New("inotify: short read in readEvents()") - continue - } - - var offset uint32 = 0 - // We don't know how many events we just read into the buffer - // While the offset points to at least one whole event... - for offset <= uint32(n-syscall.SizeofInotifyEvent) { - // Point "raw" to the event in the buffer - raw := (*syscall.InotifyEvent)(unsafe.Pointer(&buf[offset])) - event := new(FileEvent) - event.mask = uint32(raw.Mask) - event.cookie = uint32(raw.Cookie) - nameLen := uint32(raw.Len) - // If the event happened to the watched directory or the watched file, the kernel - // doesn't append the filename to the event, but we would like to always fill the - // the "Name" field with a valid filename. We retrieve the path of the watch from - // the "paths" map. - w.mu.Lock() - event.Name = w.paths[int(raw.Wd)] - w.mu.Unlock() - watchedName := event.Name - if nameLen > 0 { - // Point "bytes" at the first byte of the filename - bytes := (*[syscall.PathMax]byte)(unsafe.Pointer(&buf[offset+syscall.SizeofInotifyEvent])) - // The filename is padded with NUL bytes. TrimRight() gets rid of those. - event.Name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000") - } - - // Send the events that are not ignored on the events channel - if !event.ignoreLinux() { - // Setup FSNotify flags (inherit from directory watch) - w.fsnmut.Lock() - if _, fsnFound := w.fsnFlags[event.Name]; !fsnFound { - if fsnFlags, watchFound := w.fsnFlags[watchedName]; watchFound { - w.fsnFlags[event.Name] = fsnFlags - } else { - w.fsnFlags[event.Name] = FSN_ALL - } - } - w.fsnmut.Unlock() - - w.internalEvent <- event - } - - // Move to the next event in the buffer - offset += syscall.SizeofInotifyEvent + nameLen - } - } -} - -// Certain types of events can be "ignored" and not sent over the Event -// channel. Such as events marked ignore by the kernel, or MODIFY events -// against files that do not exist. -func (e *FileEvent) ignoreLinux() bool { - // Ignore anything the inotify API says to ignore - if e.mask&sys_IN_IGNORED == sys_IN_IGNORED { - return true - } - - // If the event is not a DELETE or RENAME, the file must exist. - // Otherwise the event is ignored. - // *Note*: this was put in place because it was seen that a MODIFY - // event was sent after the DELETE. This ignores that MODIFY and - // assumes a DELETE will come or has come if the file doesn't exist. - if !(e.IsDelete() || e.IsRename()) { - _, statErr := os.Lstat(e.Name) - return os.IsNotExist(statErr) - } - return false -} diff --git a/vendor/github.com/howeyc/fsnotify/fsnotify_open_bsd.go b/vendor/github.com/howeyc/fsnotify/fsnotify_open_bsd.go deleted file mode 100644 index f728a6f..0000000 --- a/vendor/github.com/howeyc/fsnotify/fsnotify_open_bsd.go +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build freebsd openbsd netbsd dragonfly - -package fsnotify - -import "syscall" - -const open_FLAGS = syscall.O_NONBLOCK | syscall.O_RDONLY diff --git a/vendor/github.com/howeyc/fsnotify/fsnotify_open_darwin.go b/vendor/github.com/howeyc/fsnotify/fsnotify_open_darwin.go deleted file mode 100644 index d450318..0000000 --- a/vendor/github.com/howeyc/fsnotify/fsnotify_open_darwin.go +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build darwin - -package fsnotify - -import "syscall" - -const open_FLAGS = syscall.O_EVTONLY diff --git a/vendor/github.com/howeyc/fsnotify/fsnotify_windows.go b/vendor/github.com/howeyc/fsnotify/fsnotify_windows.go deleted file mode 100644 index d88ae63..0000000 --- a/vendor/github.com/howeyc/fsnotify/fsnotify_windows.go +++ /dev/null @@ -1,598 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build windows - -package fsnotify - -import ( - "errors" - "fmt" - "os" - "path/filepath" - "runtime" - "sync" - "syscall" - "unsafe" -) - -const ( - // Options for AddWatch - sys_FS_ONESHOT = 0x80000000 - sys_FS_ONLYDIR = 0x1000000 - - // Events - sys_FS_ACCESS = 0x1 - sys_FS_ALL_EVENTS = 0xfff - sys_FS_ATTRIB = 0x4 - sys_FS_CLOSE = 0x18 - sys_FS_CREATE = 0x100 - sys_FS_DELETE = 0x200 - sys_FS_DELETE_SELF = 0x400 - sys_FS_MODIFY = 0x2 - sys_FS_MOVE = 0xc0 - sys_FS_MOVED_FROM = 0x40 - sys_FS_MOVED_TO = 0x80 - sys_FS_MOVE_SELF = 0x800 - - // Special events - sys_FS_IGNORED = 0x8000 - sys_FS_Q_OVERFLOW = 0x4000 -) - -const ( - // TODO(nj): Use syscall.ERROR_MORE_DATA from ztypes_windows in Go 1.3+ - sys_ERROR_MORE_DATA syscall.Errno = 234 -) - -// Event is the type of the notification messages -// received on the watcher's Event channel. -type FileEvent struct { - mask uint32 // Mask of events - cookie uint32 // Unique cookie associating related events (for rename) - Name string // File name (optional) -} - -// IsCreate reports whether the FileEvent was triggered by a creation -func (e *FileEvent) IsCreate() bool { return (e.mask & sys_FS_CREATE) == sys_FS_CREATE } - -// IsDelete reports whether the FileEvent was triggered by a delete -func (e *FileEvent) IsDelete() bool { - return ((e.mask&sys_FS_DELETE) == sys_FS_DELETE || (e.mask&sys_FS_DELETE_SELF) == sys_FS_DELETE_SELF) -} - -// IsModify reports whether the FileEvent was triggered by a file modification or attribute change -func (e *FileEvent) IsModify() bool { - return ((e.mask&sys_FS_MODIFY) == sys_FS_MODIFY || (e.mask&sys_FS_ATTRIB) == sys_FS_ATTRIB) -} - -// IsRename reports whether the FileEvent was triggered by a change name -func (e *FileEvent) IsRename() bool { - return ((e.mask&sys_FS_MOVE) == sys_FS_MOVE || (e.mask&sys_FS_MOVE_SELF) == sys_FS_MOVE_SELF || (e.mask&sys_FS_MOVED_FROM) == sys_FS_MOVED_FROM || (e.mask&sys_FS_MOVED_TO) == sys_FS_MOVED_TO) -} - -// IsAttrib reports whether the FileEvent was triggered by a change in the file metadata. -func (e *FileEvent) IsAttrib() bool { - return (e.mask & sys_FS_ATTRIB) == sys_FS_ATTRIB -} - -const ( - opAddWatch = iota - opRemoveWatch -) - -const ( - provisional uint64 = 1 << (32 + iota) -) - -type input struct { - op int - path string - flags uint32 - reply chan error -} - -type inode struct { - handle syscall.Handle - volume uint32 - index uint64 -} - -type watch struct { - ov syscall.Overlapped - ino *inode // i-number - path string // Directory path - mask uint64 // Directory itself is being watched with these notify flags - names map[string]uint64 // Map of names being watched and their notify flags - rename string // Remembers the old name while renaming a file - buf [4096]byte -} - -type indexMap map[uint64]*watch -type watchMap map[uint32]indexMap - -// A Watcher waits for and receives event notifications -// for a specific set of files and directories. -type Watcher struct { - mu sync.Mutex // Map access - port syscall.Handle // Handle to completion port - watches watchMap // Map of watches (key: i-number) - fsnFlags map[string]uint32 // Map of watched files to flags used for filter - fsnmut sync.Mutex // Protects access to fsnFlags. - input chan *input // Inputs to the reader are sent on this channel - internalEvent chan *FileEvent // Events are queued on this channel - Event chan *FileEvent // Events are returned on this channel - Error chan error // Errors are sent on this channel - isClosed bool // Set to true when Close() is first called - quit chan chan<- error - cookie uint32 -} - -// NewWatcher creates and returns a Watcher. -func NewWatcher() (*Watcher, error) { - port, e := syscall.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 0) - if e != nil { - return nil, os.NewSyscallError("CreateIoCompletionPort", e) - } - w := &Watcher{ - port: port, - watches: make(watchMap), - fsnFlags: make(map[string]uint32), - input: make(chan *input, 1), - Event: make(chan *FileEvent, 50), - internalEvent: make(chan *FileEvent), - Error: make(chan error), - quit: make(chan chan<- error, 1), - } - go w.readEvents() - go w.purgeEvents() - return w, nil -} - -// Close closes a Watcher. -// It sends a message to the reader goroutine to quit and removes all watches -// associated with the watcher. -func (w *Watcher) Close() error { - if w.isClosed { - return nil - } - w.isClosed = true - - // Send "quit" message to the reader goroutine - ch := make(chan error) - w.quit <- ch - if err := w.wakeupReader(); err != nil { - return err - } - return <-ch -} - -// AddWatch adds path to the watched file set. -func (w *Watcher) AddWatch(path string, flags uint32) error { - if w.isClosed { - return errors.New("watcher already closed") - } - in := &input{ - op: opAddWatch, - path: filepath.Clean(path), - flags: flags, - reply: make(chan error), - } - w.input <- in - if err := w.wakeupReader(); err != nil { - return err - } - return <-in.reply -} - -// Watch adds path to the watched file set, watching all events. -func (w *Watcher) watch(path string) error { - return w.AddWatch(path, sys_FS_ALL_EVENTS) -} - -// RemoveWatch removes path from the watched file set. -func (w *Watcher) removeWatch(path string) error { - in := &input{ - op: opRemoveWatch, - path: filepath.Clean(path), - reply: make(chan error), - } - w.input <- in - if err := w.wakeupReader(); err != nil { - return err - } - return <-in.reply -} - -func (w *Watcher) wakeupReader() error { - e := syscall.PostQueuedCompletionStatus(w.port, 0, 0, nil) - if e != nil { - return os.NewSyscallError("PostQueuedCompletionStatus", e) - } - return nil -} - -func getDir(pathname string) (dir string, err error) { - attr, e := syscall.GetFileAttributes(syscall.StringToUTF16Ptr(pathname)) - if e != nil { - return "", os.NewSyscallError("GetFileAttributes", e) - } - if attr&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 { - dir = pathname - } else { - dir, _ = filepath.Split(pathname) - dir = filepath.Clean(dir) - } - return -} - -func getIno(path string) (ino *inode, err error) { - h, e := syscall.CreateFile(syscall.StringToUTF16Ptr(path), - syscall.FILE_LIST_DIRECTORY, - syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, - nil, syscall.OPEN_EXISTING, - syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OVERLAPPED, 0) - if e != nil { - return nil, os.NewSyscallError("CreateFile", e) - } - var fi syscall.ByHandleFileInformation - if e = syscall.GetFileInformationByHandle(h, &fi); e != nil { - syscall.CloseHandle(h) - return nil, os.NewSyscallError("GetFileInformationByHandle", e) - } - ino = &inode{ - handle: h, - volume: fi.VolumeSerialNumber, - index: uint64(fi.FileIndexHigh)<<32 | uint64(fi.FileIndexLow), - } - return ino, nil -} - -// Must run within the I/O thread. -func (m watchMap) get(ino *inode) *watch { - if i := m[ino.volume]; i != nil { - return i[ino.index] - } - return nil -} - -// Must run within the I/O thread. -func (m watchMap) set(ino *inode, watch *watch) { - i := m[ino.volume] - if i == nil { - i = make(indexMap) - m[ino.volume] = i - } - i[ino.index] = watch -} - -// Must run within the I/O thread. -func (w *Watcher) addWatch(pathname string, flags uint64) error { - dir, err := getDir(pathname) - if err != nil { - return err - } - if flags&sys_FS_ONLYDIR != 0 && pathname != dir { - return nil - } - ino, err := getIno(dir) - if err != nil { - return err - } - w.mu.Lock() - watchEntry := w.watches.get(ino) - w.mu.Unlock() - if watchEntry == nil { - if _, e := syscall.CreateIoCompletionPort(ino.handle, w.port, 0, 0); e != nil { - syscall.CloseHandle(ino.handle) - return os.NewSyscallError("CreateIoCompletionPort", e) - } - watchEntry = &watch{ - ino: ino, - path: dir, - names: make(map[string]uint64), - } - w.mu.Lock() - w.watches.set(ino, watchEntry) - w.mu.Unlock() - flags |= provisional - } else { - syscall.CloseHandle(ino.handle) - } - if pathname == dir { - watchEntry.mask |= flags - } else { - watchEntry.names[filepath.Base(pathname)] |= flags - } - if err = w.startRead(watchEntry); err != nil { - return err - } - if pathname == dir { - watchEntry.mask &= ^provisional - } else { - watchEntry.names[filepath.Base(pathname)] &= ^provisional - } - return nil -} - -// Must run within the I/O thread. -func (w *Watcher) remWatch(pathname string) error { - dir, err := getDir(pathname) - if err != nil { - return err - } - ino, err := getIno(dir) - if err != nil { - return err - } - w.mu.Lock() - watch := w.watches.get(ino) - w.mu.Unlock() - if watch == nil { - return fmt.Errorf("can't remove non-existent watch for: %s", pathname) - } - if pathname == dir { - w.sendEvent(watch.path, watch.mask&sys_FS_IGNORED) - watch.mask = 0 - } else { - name := filepath.Base(pathname) - w.sendEvent(watch.path+"\\"+name, watch.names[name]&sys_FS_IGNORED) - delete(watch.names, name) - } - return w.startRead(watch) -} - -// Must run within the I/O thread. -func (w *Watcher) deleteWatch(watch *watch) { - for name, mask := range watch.names { - if mask&provisional == 0 { - w.sendEvent(watch.path+"\\"+name, mask&sys_FS_IGNORED) - } - delete(watch.names, name) - } - if watch.mask != 0 { - if watch.mask&provisional == 0 { - w.sendEvent(watch.path, watch.mask&sys_FS_IGNORED) - } - watch.mask = 0 - } -} - -// Must run within the I/O thread. -func (w *Watcher) startRead(watch *watch) error { - if e := syscall.CancelIo(watch.ino.handle); e != nil { - w.Error <- os.NewSyscallError("CancelIo", e) - w.deleteWatch(watch) - } - mask := toWindowsFlags(watch.mask) - for _, m := range watch.names { - mask |= toWindowsFlags(m) - } - if mask == 0 { - if e := syscall.CloseHandle(watch.ino.handle); e != nil { - w.Error <- os.NewSyscallError("CloseHandle", e) - } - w.mu.Lock() - delete(w.watches[watch.ino.volume], watch.ino.index) - w.mu.Unlock() - return nil - } - e := syscall.ReadDirectoryChanges(watch.ino.handle, &watch.buf[0], - uint32(unsafe.Sizeof(watch.buf)), false, mask, nil, &watch.ov, 0) - if e != nil { - err := os.NewSyscallError("ReadDirectoryChanges", e) - if e == syscall.ERROR_ACCESS_DENIED && watch.mask&provisional == 0 { - // Watched directory was probably removed - if w.sendEvent(watch.path, watch.mask&sys_FS_DELETE_SELF) { - if watch.mask&sys_FS_ONESHOT != 0 { - watch.mask = 0 - } - } - err = nil - } - w.deleteWatch(watch) - w.startRead(watch) - return err - } - return nil -} - -// readEvents reads from the I/O completion port, converts the -// received events into Event objects and sends them via the Event channel. -// Entry point to the I/O thread. -func (w *Watcher) readEvents() { - var ( - n, key uint32 - ov *syscall.Overlapped - ) - runtime.LockOSThread() - - for { - e := syscall.GetQueuedCompletionStatus(w.port, &n, &key, &ov, syscall.INFINITE) - watch := (*watch)(unsafe.Pointer(ov)) - - if watch == nil { - select { - case ch := <-w.quit: - w.mu.Lock() - var indexes []indexMap - for _, index := range w.watches { - indexes = append(indexes, index) - } - w.mu.Unlock() - for _, index := range indexes { - for _, watch := range index { - w.deleteWatch(watch) - w.startRead(watch) - } - } - var err error - if e := syscall.CloseHandle(w.port); e != nil { - err = os.NewSyscallError("CloseHandle", e) - } - close(w.internalEvent) - close(w.Error) - ch <- err - return - case in := <-w.input: - switch in.op { - case opAddWatch: - in.reply <- w.addWatch(in.path, uint64(in.flags)) - case opRemoveWatch: - in.reply <- w.remWatch(in.path) - } - default: - } - continue - } - - switch e { - case sys_ERROR_MORE_DATA: - if watch == nil { - w.Error <- errors.New("ERROR_MORE_DATA has unexpectedly null lpOverlapped buffer") - } else { - // The i/o succeeded but the buffer is full. - // In theory we should be building up a full packet. - // In practice we can get away with just carrying on. - n = uint32(unsafe.Sizeof(watch.buf)) - } - case syscall.ERROR_ACCESS_DENIED: - // Watched directory was probably removed - w.sendEvent(watch.path, watch.mask&sys_FS_DELETE_SELF) - w.deleteWatch(watch) - w.startRead(watch) - continue - case syscall.ERROR_OPERATION_ABORTED: - // CancelIo was called on this handle - continue - default: - w.Error <- os.NewSyscallError("GetQueuedCompletionPort", e) - continue - case nil: - } - - var offset uint32 - for { - if n == 0 { - w.internalEvent <- &FileEvent{mask: sys_FS_Q_OVERFLOW} - w.Error <- errors.New("short read in readEvents()") - break - } - - // Point "raw" to the event in the buffer - raw := (*syscall.FileNotifyInformation)(unsafe.Pointer(&watch.buf[offset])) - buf := (*[syscall.MAX_PATH]uint16)(unsafe.Pointer(&raw.FileName)) - name := syscall.UTF16ToString(buf[:raw.FileNameLength/2]) - fullname := watch.path + "\\" + name - - var mask uint64 - switch raw.Action { - case syscall.FILE_ACTION_REMOVED: - mask = sys_FS_DELETE_SELF - case syscall.FILE_ACTION_MODIFIED: - mask = sys_FS_MODIFY - case syscall.FILE_ACTION_RENAMED_OLD_NAME: - watch.rename = name - case syscall.FILE_ACTION_RENAMED_NEW_NAME: - if watch.names[watch.rename] != 0 { - watch.names[name] |= watch.names[watch.rename] - delete(watch.names, watch.rename) - mask = sys_FS_MOVE_SELF - } - } - - sendNameEvent := func() { - if w.sendEvent(fullname, watch.names[name]&mask) { - if watch.names[name]&sys_FS_ONESHOT != 0 { - delete(watch.names, name) - } - } - } - if raw.Action != syscall.FILE_ACTION_RENAMED_NEW_NAME { - sendNameEvent() - } - if raw.Action == syscall.FILE_ACTION_REMOVED { - w.sendEvent(fullname, watch.names[name]&sys_FS_IGNORED) - delete(watch.names, name) - } - if w.sendEvent(fullname, watch.mask&toFSnotifyFlags(raw.Action)) { - if watch.mask&sys_FS_ONESHOT != 0 { - watch.mask = 0 - } - } - if raw.Action == syscall.FILE_ACTION_RENAMED_NEW_NAME { - fullname = watch.path + "\\" + watch.rename - sendNameEvent() - } - - // Move to the next event in the buffer - if raw.NextEntryOffset == 0 { - break - } - offset += raw.NextEntryOffset - - // Error! - if offset >= n { - w.Error <- errors.New("Windows system assumed buffer larger than it is, events have likely been missed.") - break - } - } - - if err := w.startRead(watch); err != nil { - w.Error <- err - } - } -} - -func (w *Watcher) sendEvent(name string, mask uint64) bool { - if mask == 0 { - return false - } - event := &FileEvent{mask: uint32(mask), Name: name} - if mask&sys_FS_MOVE != 0 { - if mask&sys_FS_MOVED_FROM != 0 { - w.cookie++ - } - event.cookie = w.cookie - } - select { - case ch := <-w.quit: - w.quit <- ch - case w.Event <- event: - } - return true -} - -func toWindowsFlags(mask uint64) uint32 { - var m uint32 - if mask&sys_FS_ACCESS != 0 { - m |= syscall.FILE_NOTIFY_CHANGE_LAST_ACCESS - } - if mask&sys_FS_MODIFY != 0 { - m |= syscall.FILE_NOTIFY_CHANGE_LAST_WRITE - } - if mask&sys_FS_ATTRIB != 0 { - m |= syscall.FILE_NOTIFY_CHANGE_ATTRIBUTES - } - if mask&(sys_FS_MOVE|sys_FS_CREATE|sys_FS_DELETE) != 0 { - m |= syscall.FILE_NOTIFY_CHANGE_FILE_NAME | syscall.FILE_NOTIFY_CHANGE_DIR_NAME - } - return m -} - -func toFSnotifyFlags(action uint32) uint64 { - switch action { - case syscall.FILE_ACTION_ADDED: - return sys_FS_CREATE - case syscall.FILE_ACTION_REMOVED: - return sys_FS_DELETE - case syscall.FILE_ACTION_MODIFIED: - return sys_FS_MODIFY - case syscall.FILE_ACTION_RENAMED_OLD_NAME: - return sys_FS_MOVED_FROM - case syscall.FILE_ACTION_RENAMED_NEW_NAME: - return sys_FS_MOVED_TO - } - return 0 -} diff --git a/vendor/modules.txt b/vendor/modules.txt index f431e23..6a475c6 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -10,8 +10,6 @@ github.com/golang/protobuf/proto # github.com/hashicorp/golang-lru v0.5.1 github.com/hashicorp/golang-lru github.com/hashicorp/golang-lru/simplelru -# github.com/howeyc/fsnotify v0.0.0-20151003194602-f0c08ee9c607 -github.com/howeyc/fsnotify # github.com/konsorten/go-windows-terminal-sequences v1.0.1 github.com/konsorten/go-windows-terminal-sequences # github.com/matttproud/golang_protobuf_extensions v1.0.1