Merge pull request #1405 from fuweid/me-async-load-cnicnf
reload cni network config if has fs change events
This commit is contained in:
		
							
								
								
									
										121
									
								
								pkg/server/cni_conf_syncer.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								pkg/server/cni_conf_syncer.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,121 @@ | ||||
| /* | ||||
| Copyright The Containerd Authors. | ||||
|  | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
|  | ||||
|     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
|  | ||||
| package server | ||||
|  | ||||
| import ( | ||||
| 	"os" | ||||
| 	"sync" | ||||
|  | ||||
| 	cni "github.com/containerd/go-cni" | ||||
| 	"github.com/fsnotify/fsnotify" | ||||
| 	"github.com/pkg/errors" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| ) | ||||
|  | ||||
| // cniNetConfSyncer is used to reload cni network conf triggered by fs change | ||||
| // events. | ||||
| type cniNetConfSyncer struct { | ||||
| 	// only used for lastSyncStatus | ||||
| 	sync.RWMutex | ||||
| 	lastSyncStatus error | ||||
|  | ||||
| 	watcher   *fsnotify.Watcher | ||||
| 	confDir   string | ||||
| 	netPlugin cni.CNI | ||||
| 	loadOpts  []cni.CNIOpt | ||||
| } | ||||
|  | ||||
| // newCNINetConfSyncer creates cni network conf syncer. | ||||
| func newCNINetConfSyncer(confDir string, netPlugin cni.CNI, loadOpts []cni.CNIOpt) (*cniNetConfSyncer, error) { | ||||
| 	watcher, err := fsnotify.NewWatcher() | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, "failed to create fsnotify watcher") | ||||
| 	} | ||||
|  | ||||
| 	if err := os.MkdirAll(confDir, 0700); err != nil { | ||||
| 		return nil, errors.Wrapf(err, "failed to create cni conf dir=%s for watch", confDir) | ||||
| 	} | ||||
|  | ||||
| 	if err := watcher.Add(confDir); err != nil { | ||||
| 		return nil, errors.Wrapf(err, "failed to watch cni conf dir %s", confDir) | ||||
| 	} | ||||
|  | ||||
| 	syncer := &cniNetConfSyncer{ | ||||
| 		watcher:   watcher, | ||||
| 		confDir:   confDir, | ||||
| 		netPlugin: netPlugin, | ||||
| 		loadOpts:  loadOpts, | ||||
| 	} | ||||
|  | ||||
| 	if err := syncer.netPlugin.Load(syncer.loadOpts...); err != nil { | ||||
| 		logrus.WithError(err).Error("failed to load cni during init, please check CRI plugin status before setting up network for pods") | ||||
| 		syncer.updateLastStatus(err) | ||||
| 	} | ||||
| 	return syncer, nil | ||||
| } | ||||
|  | ||||
| // syncLoop monitors any fs change events from cni conf dir and tries to reload | ||||
| // cni configuration. | ||||
| func (syncer *cniNetConfSyncer) syncLoop() error { | ||||
| 	for { | ||||
| 		select { | ||||
| 		case event := <-syncer.watcher.Events: | ||||
| 			// Only reload config when receiving write/rename/remove | ||||
| 			// events | ||||
| 			// | ||||
| 			// TODO(fuweid): Might only reload target cni config | ||||
| 			// files to prevent no-ops. | ||||
| 			if event.Op&(fsnotify.Chmod|fsnotify.Create) > 0 { | ||||
| 				logrus.Debugf("ignore event from cni conf dir: %s", event) | ||||
| 				continue | ||||
| 			} | ||||
| 			logrus.Debugf("receiving change event from cni conf dir: %s", event) | ||||
|  | ||||
| 			lerr := syncer.netPlugin.Load(syncer.loadOpts...) | ||||
| 			if lerr != nil { | ||||
| 				logrus.WithError(lerr). | ||||
| 					Errorf("failed to reload cni configuration after receiving fs change event(%s)", event) | ||||
| 			} | ||||
| 			syncer.updateLastStatus(lerr) | ||||
|  | ||||
| 		case err := <-syncer.watcher.Errors: | ||||
| 			if err != nil { | ||||
| 				logrus.WithError(err).Error("failed to continue sync cni conf change") | ||||
| 				return err | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // lastStatus retrieves last sync status. | ||||
| func (syncer *cniNetConfSyncer) lastStatus() error { | ||||
| 	syncer.RLock() | ||||
| 	defer syncer.RUnlock() | ||||
| 	return syncer.lastSyncStatus | ||||
| } | ||||
|  | ||||
| // updateLastStatus will be called after every single cni load. | ||||
| func (syncer *cniNetConfSyncer) updateLastStatus(err error) { | ||||
| 	syncer.Lock() | ||||
| 	defer syncer.Unlock() | ||||
| 	syncer.lastSyncStatus = err | ||||
| } | ||||
|  | ||||
| // stop stops watcher in the syncLoop. | ||||
| func (syncer *cniNetConfSyncer) stop() error { | ||||
| 	return syncer.watcher.Close() | ||||
| } | ||||
| @@ -91,6 +91,9 @@ type criService struct { | ||||
| 	// initialized indicates whether the server is initialized. All GRPC services | ||||
| 	// should return error before the server is initialized. | ||||
| 	initialized atomic.Bool | ||||
| 	// cniNetConfMonitor is used to reload cni network conf if there is | ||||
| 	// any valid fs change events from cni network conf dir. | ||||
| 	cniNetConfMonitor *cniNetConfSyncer | ||||
| } | ||||
|  | ||||
| // NewCRIService returns a new instance of CRIService | ||||
| @@ -128,6 +131,11 @@ func NewCRIService(config criconfig.Config, client *containerd.Client) (CRIServi | ||||
|  | ||||
| 	c.eventMonitor = newEventMonitor(c) | ||||
|  | ||||
| 	c.cniNetConfMonitor, err = newCNINetConfSyncer(c.config.NetworkPluginConfDir, c.netPlugin, c.cniLoadOptions()) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, "failed to create cni conf monitor") | ||||
| 	} | ||||
|  | ||||
| 	return c, nil | ||||
| } | ||||
|  | ||||
| @@ -169,6 +177,14 @@ func (c *criService) Run() error { | ||||
| 	) | ||||
| 	snapshotsSyncer.start() | ||||
|  | ||||
| 	// Start CNI network conf syncer | ||||
| 	logrus.Info("Start cni network conf syncer") | ||||
| 	cniNetConfMonitorErrCh := make(chan error, 1) | ||||
| 	go func() { | ||||
| 		defer close(cniNetConfMonitorErrCh) | ||||
| 		cniNetConfMonitorErrCh <- c.cniNetConfMonitor.syncLoop() | ||||
| 	}() | ||||
|  | ||||
| 	// Start streaming server. | ||||
| 	logrus.Info("Start streaming server") | ||||
| 	streamServerErrCh := make(chan error) | ||||
| @@ -183,11 +199,12 @@ func (c *criService) Run() error { | ||||
| 	// Set the server as initialized. GRPC services could start serving traffic. | ||||
| 	c.initialized.Set() | ||||
|  | ||||
| 	var eventMonitorErr, streamServerErr error | ||||
| 	var eventMonitorErr, streamServerErr, cniNetConfMonitorErr error | ||||
| 	// Stop the whole CRI service if any of the critical service exits. | ||||
| 	select { | ||||
| 	case eventMonitorErr = <-eventMonitorErrCh: | ||||
| 	case streamServerErr = <-streamServerErrCh: | ||||
| 	case cniNetConfMonitorErr = <-cniNetConfMonitorErrCh: | ||||
| 	} | ||||
| 	if err := c.Close(); err != nil { | ||||
| 		return errors.Wrap(err, "failed to stop cri service") | ||||
| @@ -222,6 +239,9 @@ func (c *criService) Run() error { | ||||
| 	if streamServerErr != nil { | ||||
| 		return errors.Wrap(streamServerErr, "stream server error") | ||||
| 	} | ||||
| 	if cniNetConfMonitorErr != nil { | ||||
| 		return errors.Wrap(cniNetConfMonitorErr, "cni network conf monitor error") | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| @@ -229,6 +249,9 @@ func (c *criService) Run() error { | ||||
| // TODO(random-liu): Make close synchronous. | ||||
| func (c *criService) Close() error { | ||||
| 	logrus.Info("Stop CRI service") | ||||
| 	if err := c.cniNetConfMonitor.stop(); err != nil { | ||||
| 		logrus.WithError(err).Error("failed to stop cni network conf monitor") | ||||
| 	} | ||||
| 	c.eventMonitor.stop() | ||||
| 	if err := c.streamServer.Stop(); err != nil { | ||||
| 		return errors.Wrap(err, "failed to stop stream server") | ||||
|   | ||||
| @@ -60,11 +60,6 @@ func (c *criService) initPlatform() error { | ||||
| 		return errors.Wrap(err, "failed to initialize cni") | ||||
| 	} | ||||
|  | ||||
| 	// Try to load the config if it exists. Just log the error if load fails | ||||
| 	// This is not disruptive for containerd to panic | ||||
| 	if err := c.netPlugin.Load(c.cniLoadOptions()...); err != nil { | ||||
| 		logrus.WithError(err).Error("Failed to load cni during init, please check CRI plugin status before setting up network for pods") | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -21,7 +21,6 @@ package server | ||||
| import ( | ||||
| 	cni "github.com/containerd/go-cni" | ||||
| 	"github.com/pkg/errors" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| ) | ||||
|  | ||||
| // windowsNetworkAttachCount is the minimum number of networks the PodSandbox | ||||
| @@ -44,11 +43,6 @@ func (c *criService) initPlatform() error { | ||||
| 		return errors.Wrap(err, "failed to initialize cni") | ||||
| 	} | ||||
|  | ||||
| 	// Try to load the config if it exists. Just log the error if load fails | ||||
| 	// This is not disruptive for containerd to panic | ||||
| 	if err := c.netPlugin.Load(c.cniLoadOptions()...); err != nil { | ||||
| 		logrus.WithError(err).Error("Failed to load cni during init, please check CRI plugin status before setting up network for pods") | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -41,10 +41,6 @@ func (c *criService) Status(ctx context.Context, r *runtime.StatusRequest) (*run | ||||
| 		Type:   runtime.NetworkReady, | ||||
| 		Status: true, | ||||
| 	} | ||||
| 	// Load the latest cni configuration to be in sync with the latest network configuration | ||||
| 	if err := c.netPlugin.Load(c.cniLoadOptions()...); err != nil { | ||||
| 		log.G(ctx).WithError(err).Errorf("Failed to load cni configuration") | ||||
| 	} | ||||
| 	// Check the status of the cni initialization | ||||
| 	if err := c.netPlugin.Status(); err != nil { | ||||
| 		networkCondition.Status = false | ||||
| @@ -76,6 +72,12 @@ func (c *criService) Status(ctx context.Context, r *runtime.StatusRequest) (*run | ||||
| 			log.G(ctx).WithError(err).Errorf("Failed to marshal CNI config %v", err) | ||||
| 		} | ||||
| 		resp.Info["cniconfig"] = string(cniConfig) | ||||
|  | ||||
| 		lastCNILoadStatus := "OK" | ||||
| 		if lerr := c.cniNetConfMonitor.lastStatus(); lerr != nil { | ||||
| 			lastCNILoadStatus = lerr.Error() | ||||
| 		} | ||||
| 		resp.Info["lastCNILoadStatus"] = lastCNILoadStatus | ||||
| 	} | ||||
| 	return resp, nil | ||||
| } | ||||
|   | ||||
| @@ -89,6 +89,7 @@ sigs.k8s.io/yaml                                    9fc95527decd95bb9d28cc2eab08 | ||||
| github.com/containerd/go-cni                        0d360c50b10b350b6bb23863fd4dfb1c232b01c9 | ||||
| github.com/containernetworking/cni                  4cfb7b568922a3c79a23e438dc52fe537fc9687e # v0.7.1 | ||||
| github.com/containernetworking/plugins              9f96827c7cabb03f21d86326000c00f61e181f6a # v0.7.6 | ||||
| github.com/fsnotify/fsnotify                        4bf2d1fec78374803a39307bfb8d340688f4f28e # v1.4.8 | ||||
|  | ||||
| # image decrypt depedencies | ||||
| github.com/containerd/imgcrypt                      9e761ccd6069fb707ec9493435f31475b5524b38 # v1.0.1 | ||||
|   | ||||
							
								
								
									
										28
									
								
								vendor/github.com/fsnotify/fsnotify/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								vendor/github.com/fsnotify/fsnotify/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| Copyright (c) 2012 The Go Authors. All rights reserved. | ||||
| Copyright (c) 2012-2019 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. | ||||
							
								
								
									
										83
									
								
								vendor/github.com/fsnotify/fsnotify/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								vendor/github.com/fsnotify/fsnotify/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | ||||
| # File system notifications for Go | ||||
|  | ||||
| [](https://godoc.org/github.com/fsnotify/fsnotify) [](https://goreportcard.com/report/github.com/fsnotify/fsnotify) | ||||
|  | ||||
| fsnotify utilizes [golang.org/x/sys](https://godoc.org/golang.org/x/sys) rather than `syscall` from the standard library. Ensure you have the latest version installed by running: | ||||
|  | ||||
| ```console | ||||
| go get -u golang.org/x/sys/... | ||||
| ``` | ||||
|  | ||||
| Cross platform: Windows, Linux, BSD and macOS. | ||||
|  | ||||
| | Adapter               | OS                               | Status                                                                                                                          | | ||||
| | --------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | | ||||
| | inotify               | Linux 2.6.27 or later, Android\* | Supported [](https://travis-ci.org/fsnotify/fsnotify) | | ||||
| | kqueue                | BSD, macOS, iOS\*                | Supported [](https://travis-ci.org/fsnotify/fsnotify) | | ||||
| | ReadDirectoryChangesW | Windows                          | Supported [](https://travis-ci.org/fsnotify/fsnotify) | | ||||
| | FSEvents              | macOS                            | [Planned](https://github.com/fsnotify/fsnotify/issues/11)                                                                       | | ||||
| | FEN                   | Solaris 11                       | [In Progress](https://github.com/fsnotify/fsnotify/issues/12)                                                                   | | ||||
| | fanotify              | Linux 2.6.37+                    | [Planned](https://github.com/fsnotify/fsnotify/issues/114)                                                                      | | ||||
| | USN Journals          | Windows                          | [Maybe](https://github.com/fsnotify/fsnotify/issues/53)                                                                         | | ||||
| | Polling               | *All*                            | [Maybe](https://github.com/fsnotify/fsnotify/issues/9)                                                                          | | ||||
|  | ||||
| \* Android and iOS are untested. | ||||
|  | ||||
| Please see [the documentation](https://godoc.org/github.com/fsnotify/fsnotify) and consult the [FAQ](#faq) for usage information. | ||||
|  | ||||
| ## API stability | ||||
|  | ||||
| fsnotify is a fork of [howeyc/fsnotify](https://godoc.org/github.com/howeyc/fsnotify) with a new API as of v1.0. The API is based on [this design document](http://goo.gl/MrYxyA).  | ||||
|  | ||||
| All [releases](https://github.com/fsnotify/fsnotify/releases) are tagged based on [Semantic Versioning](http://semver.org/). Further API changes are [planned](https://github.com/fsnotify/fsnotify/milestones), and will be tagged with a new major revision number. | ||||
|  | ||||
| Go 1.6 supports dependencies located in the `vendor/` folder. Unless you are creating a library, it is recommended that you copy fsnotify into `vendor/github.com/fsnotify/fsnotify` within your project, and likewise for `golang.org/x/sys`. | ||||
|  | ||||
| ## Contributing | ||||
|  | ||||
| Please refer to [CONTRIBUTING][] before opening an issue or pull request. | ||||
|  | ||||
| ## Example | ||||
|  | ||||
| See [example_test.go](https://github.com/fsnotify/fsnotify/blob/master/example_test.go). | ||||
|  | ||||
| ## 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 on the roadmap [#18][]). | ||||
|  | ||||
| **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 [howeyc #7][#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 [howeyc #62][#62]). A temporary workaround is to add your folder(s) to the *Spotlight Privacy settings* until we have a native FSEvents implementation (see [#11][]). | ||||
|  | ||||
| **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. | ||||
|  | ||||
| **Why don't notifications work with NFS filesystems or filesystem in userspace (FUSE)?** | ||||
|  | ||||
| fsnotify requires support from underlying OS to work. The current NFS protocol does not provide network level support for file notifications. | ||||
|  | ||||
| [#62]: https://github.com/howeyc/fsnotify/issues/62 | ||||
| [#18]: https://github.com/fsnotify/fsnotify/issues/18 | ||||
| [#11]: https://github.com/fsnotify/fsnotify/issues/11 | ||||
| [#7]: https://github.com/howeyc/fsnotify/issues/7 | ||||
|  | ||||
| [contributing]: https://github.com/fsnotify/fsnotify/blob/master/CONTRIBUTING.md | ||||
|  | ||||
| ## Related Projects | ||||
|  | ||||
| * [notify](https://github.com/rjeczalik/notify) | ||||
| * [fsevents](https://github.com/fsnotify/fsevents) | ||||
|  | ||||
							
								
								
									
										37
									
								
								vendor/github.com/fsnotify/fsnotify/fen.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								vendor/github.com/fsnotify/fsnotify/fen.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| // 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 solaris | ||||
|  | ||||
| package fsnotify | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| ) | ||||
|  | ||||
| // Watcher watches a set of files, delivering events to a channel. | ||||
| type Watcher struct { | ||||
| 	Events chan Event | ||||
| 	Errors chan error | ||||
| } | ||||
|  | ||||
| // NewWatcher establishes a new watcher with the underlying OS and begins waiting for events. | ||||
| func NewWatcher() (*Watcher, error) { | ||||
| 	return nil, errors.New("FEN based watcher not yet supported for fsnotify\n") | ||||
| } | ||||
|  | ||||
| // Close removes all watches and closes the events channel. | ||||
| func (w *Watcher) Close() error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Add starts watching the named file or directory (non-recursively). | ||||
| func (w *Watcher) Add(name string) error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Remove stops watching the the named file or directory (non-recursively). | ||||
| func (w *Watcher) Remove(name string) error { | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										68
									
								
								vendor/github.com/fsnotify/fsnotify/fsnotify.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								vendor/github.com/fsnotify/fsnotify/fsnotify.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | ||||
| // 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. | ||||
|  | ||||
| // +build !plan9 | ||||
|  | ||||
| // Package fsnotify provides a platform-independent interface for file system notifications. | ||||
| package fsnotify | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| ) | ||||
|  | ||||
| // Event represents a single file system notification. | ||||
| type Event struct { | ||||
| 	Name string // Relative path to the file or directory. | ||||
| 	Op   Op     // File operation that triggered the event. | ||||
| } | ||||
|  | ||||
| // Op describes a set of file operations. | ||||
| type Op uint32 | ||||
|  | ||||
| // These are the generalized file operations that can trigger a notification. | ||||
| const ( | ||||
| 	Create Op = 1 << iota | ||||
| 	Write | ||||
| 	Remove | ||||
| 	Rename | ||||
| 	Chmod | ||||
| ) | ||||
|  | ||||
| func (op Op) String() string { | ||||
| 	// Use a buffer for efficient string concatenation | ||||
| 	var buffer bytes.Buffer | ||||
|  | ||||
| 	if op&Create == Create { | ||||
| 		buffer.WriteString("|CREATE") | ||||
| 	} | ||||
| 	if op&Remove == Remove { | ||||
| 		buffer.WriteString("|REMOVE") | ||||
| 	} | ||||
| 	if op&Write == Write { | ||||
| 		buffer.WriteString("|WRITE") | ||||
| 	} | ||||
| 	if op&Rename == Rename { | ||||
| 		buffer.WriteString("|RENAME") | ||||
| 	} | ||||
| 	if op&Chmod == Chmod { | ||||
| 		buffer.WriteString("|CHMOD") | ||||
| 	} | ||||
| 	if buffer.Len() == 0 { | ||||
| 		return "" | ||||
| 	} | ||||
| 	return buffer.String()[1:] // Strip leading pipe | ||||
| } | ||||
|  | ||||
| // String returns a string representation of the event in the form | ||||
| // "file: REMOVE|WRITE|..." | ||||
| func (e Event) String() string { | ||||
| 	return fmt.Sprintf("%q: %s", e.Name, e.Op.String()) | ||||
| } | ||||
|  | ||||
| // Common errors that can be reported by a watcher | ||||
| var ( | ||||
| 	ErrEventOverflow = errors.New("fsnotify queue overflow") | ||||
| ) | ||||
							
								
								
									
										5
									
								
								vendor/github.com/fsnotify/fsnotify/go.mod
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								vendor/github.com/fsnotify/fsnotify/go.mod
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| module github.com/fsnotify/fsnotify | ||||
|  | ||||
| go 1.13 | ||||
|  | ||||
| require golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9 | ||||
							
								
								
									
										337
									
								
								vendor/github.com/fsnotify/fsnotify/inotify.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										337
									
								
								vendor/github.com/fsnotify/fsnotify/inotify.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,337 @@ | ||||
| // 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" | ||||
| 	"io" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"unsafe" | ||||
|  | ||||
| 	"golang.org/x/sys/unix" | ||||
| ) | ||||
|  | ||||
| // Watcher watches a set of files, delivering events to a channel. | ||||
| type Watcher struct { | ||||
| 	Events   chan Event | ||||
| 	Errors   chan error | ||||
| 	mu       sync.Mutex // Map access | ||||
| 	fd       int | ||||
| 	poller   *fdPoller | ||||
| 	watches  map[string]*watch // Map of inotify watches (key: path) | ||||
| 	paths    map[int]string    // Map of watched paths (key: watch descriptor) | ||||
| 	done     chan struct{}     // Channel for sending a "quit message" to the reader goroutine | ||||
| 	doneResp chan struct{}     // Channel to respond to Close | ||||
| } | ||||
|  | ||||
| // NewWatcher establishes a new watcher with the underlying OS and begins waiting for events. | ||||
| func NewWatcher() (*Watcher, error) { | ||||
| 	// Create inotify fd | ||||
| 	fd, errno := unix.InotifyInit1(unix.IN_CLOEXEC) | ||||
| 	if fd == -1 { | ||||
| 		return nil, errno | ||||
| 	} | ||||
| 	// Create epoll | ||||
| 	poller, err := newFdPoller(fd) | ||||
| 	if err != nil { | ||||
| 		unix.Close(fd) | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	w := &Watcher{ | ||||
| 		fd:       fd, | ||||
| 		poller:   poller, | ||||
| 		watches:  make(map[string]*watch), | ||||
| 		paths:    make(map[int]string), | ||||
| 		Events:   make(chan Event), | ||||
| 		Errors:   make(chan error), | ||||
| 		done:     make(chan struct{}), | ||||
| 		doneResp: make(chan struct{}), | ||||
| 	} | ||||
|  | ||||
| 	go w.readEvents() | ||||
| 	return w, nil | ||||
| } | ||||
|  | ||||
| func (w *Watcher) isClosed() bool { | ||||
| 	select { | ||||
| 	case <-w.done: | ||||
| 		return true | ||||
| 	default: | ||||
| 		return false | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Close removes all watches and closes the events channel. | ||||
| func (w *Watcher) Close() error { | ||||
| 	if w.isClosed() { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	// Send 'close' signal to goroutine, and set the Watcher to closed. | ||||
| 	close(w.done) | ||||
|  | ||||
| 	// Wake up goroutine | ||||
| 	w.poller.wake() | ||||
|  | ||||
| 	// Wait for goroutine to close | ||||
| 	<-w.doneResp | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Add starts watching the named file or directory (non-recursively). | ||||
| func (w *Watcher) Add(name string) error { | ||||
| 	name = filepath.Clean(name) | ||||
| 	if w.isClosed() { | ||||
| 		return errors.New("inotify instance already closed") | ||||
| 	} | ||||
|  | ||||
| 	const agnosticEvents = unix.IN_MOVED_TO | unix.IN_MOVED_FROM | | ||||
| 		unix.IN_CREATE | unix.IN_ATTRIB | unix.IN_MODIFY | | ||||
| 		unix.IN_MOVE_SELF | unix.IN_DELETE | unix.IN_DELETE_SELF | ||||
|  | ||||
| 	var flags uint32 = agnosticEvents | ||||
|  | ||||
| 	w.mu.Lock() | ||||
| 	defer w.mu.Unlock() | ||||
| 	watchEntry := w.watches[name] | ||||
| 	if watchEntry != nil { | ||||
| 		flags |= watchEntry.flags | unix.IN_MASK_ADD | ||||
| 	} | ||||
| 	wd, errno := unix.InotifyAddWatch(w.fd, name, flags) | ||||
| 	if wd == -1 { | ||||
| 		return errno | ||||
| 	} | ||||
|  | ||||
| 	if watchEntry == nil { | ||||
| 		w.watches[name] = &watch{wd: uint32(wd), flags: flags} | ||||
| 		w.paths[wd] = name | ||||
| 	} else { | ||||
| 		watchEntry.wd = uint32(wd) | ||||
| 		watchEntry.flags = flags | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Remove stops watching the named file or directory (non-recursively). | ||||
| func (w *Watcher) Remove(name string) error { | ||||
| 	name = filepath.Clean(name) | ||||
|  | ||||
| 	// Fetch the watch. | ||||
| 	w.mu.Lock() | ||||
| 	defer w.mu.Unlock() | ||||
| 	watch, ok := w.watches[name] | ||||
|  | ||||
| 	// Remove it from inotify. | ||||
| 	if !ok { | ||||
| 		return fmt.Errorf("can't remove non-existent inotify watch for: %s", name) | ||||
| 	} | ||||
|  | ||||
| 	// We successfully removed the watch if InotifyRmWatch doesn't return an | ||||
| 	// error, we need to clean up our internal state to ensure it matches | ||||
| 	// inotify's kernel state. | ||||
| 	delete(w.paths, int(watch.wd)) | ||||
| 	delete(w.watches, name) | ||||
|  | ||||
| 	// inotify_rm_watch will return EINVAL if the file has been deleted; | ||||
| 	// the inotify will already have been removed. | ||||
| 	// watches and pathes are deleted in ignoreLinux() implicitly and asynchronously | ||||
| 	// by calling inotify_rm_watch() below. e.g. readEvents() goroutine receives IN_IGNORE | ||||
| 	// so that EINVAL means that the wd is being rm_watch()ed or its file removed | ||||
| 	// by another thread and we have not received IN_IGNORE event. | ||||
| 	success, errno := unix.InotifyRmWatch(w.fd, watch.wd) | ||||
| 	if success == -1 { | ||||
| 		// TODO: Perhaps it's not helpful to return an error here in every case. | ||||
| 		// the only two possible errors are: | ||||
| 		// EBADF, which happens when w.fd is not a valid file descriptor of any kind. | ||||
| 		// EINVAL, which is when fd is not an inotify descriptor or wd is not a valid watch descriptor. | ||||
| 		// Watch descriptors are invalidated when they are removed explicitly or implicitly; | ||||
| 		// explicitly by inotify_rm_watch, implicitly when the file they are watching is deleted. | ||||
| 		return errno | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| 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) | ||||
| } | ||||
|  | ||||
| // readEvents reads from the inotify file descriptor, converts the | ||||
| // received events into Event objects and sends them via the Events channel | ||||
| func (w *Watcher) readEvents() { | ||||
| 	var ( | ||||
| 		buf   [unix.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events | ||||
| 		n     int                                  // Number of bytes read with read() | ||||
| 		errno error                                // Syscall errno | ||||
| 		ok    bool                                 // For poller.wait | ||||
| 	) | ||||
|  | ||||
| 	defer close(w.doneResp) | ||||
| 	defer close(w.Errors) | ||||
| 	defer close(w.Events) | ||||
| 	defer unix.Close(w.fd) | ||||
| 	defer w.poller.close() | ||||
|  | ||||
| 	for { | ||||
| 		// See if we have been closed. | ||||
| 		if w.isClosed() { | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		ok, errno = w.poller.wait() | ||||
| 		if errno != nil { | ||||
| 			select { | ||||
| 			case w.Errors <- errno: | ||||
| 			case <-w.done: | ||||
| 				return | ||||
| 			} | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		if !ok { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		n, errno = unix.Read(w.fd, buf[:]) | ||||
| 		// If a signal interrupted execution, see if we've been asked to close, and try again. | ||||
| 		// http://man7.org/linux/man-pages/man7/signal.7.html : | ||||
| 		// "Before Linux 3.8, reads from an inotify(7) file descriptor were not restartable" | ||||
| 		if errno == unix.EINTR { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		// unix.Read might have been woken up by Close. If so, we're done. | ||||
| 		if w.isClosed() { | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		if n < unix.SizeofInotifyEvent { | ||||
| 			var err error | ||||
| 			if n == 0 { | ||||
| 				// If EOF is received. This should really never happen. | ||||
| 				err = io.EOF | ||||
| 			} else if n < 0 { | ||||
| 				// If an error occurred while reading. | ||||
| 				err = errno | ||||
| 			} else { | ||||
| 				// Read was too short. | ||||
| 				err = errors.New("notify: short read in readEvents()") | ||||
| 			} | ||||
| 			select { | ||||
| 			case w.Errors <- err: | ||||
| 			case <-w.done: | ||||
| 				return | ||||
| 			} | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		var offset uint32 | ||||
| 		// 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-unix.SizeofInotifyEvent) { | ||||
| 			// Point "raw" to the event in the buffer | ||||
| 			raw := (*unix.InotifyEvent)(unsafe.Pointer(&buf[offset])) | ||||
|  | ||||
| 			mask := uint32(raw.Mask) | ||||
| 			nameLen := uint32(raw.Len) | ||||
|  | ||||
| 			if mask&unix.IN_Q_OVERFLOW != 0 { | ||||
| 				select { | ||||
| 				case w.Errors <- ErrEventOverflow: | ||||
| 				case <-w.done: | ||||
| 					return | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			// 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() | ||||
| 			name, ok := w.paths[int(raw.Wd)] | ||||
| 			// IN_DELETE_SELF occurs when the file/directory being watched is removed. | ||||
| 			// This is a sign to clean up the maps, otherwise we are no longer in sync | ||||
| 			// with the inotify kernel state which has already deleted the watch | ||||
| 			// automatically. | ||||
| 			if ok && mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF { | ||||
| 				delete(w.paths, int(raw.Wd)) | ||||
| 				delete(w.watches, name) | ||||
| 			} | ||||
| 			w.mu.Unlock() | ||||
|  | ||||
| 			if nameLen > 0 { | ||||
| 				// Point "bytes" at the first byte of the filename | ||||
| 				bytes := (*[unix.PathMax]byte)(unsafe.Pointer(&buf[offset+unix.SizeofInotifyEvent])) | ||||
| 				// The filename is padded with NULL bytes. TrimRight() gets rid of those. | ||||
| 				name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000") | ||||
| 			} | ||||
|  | ||||
| 			event := newEvent(name, mask) | ||||
|  | ||||
| 			// Send the events that are not ignored on the events channel | ||||
| 			if !event.ignoreLinux(mask) { | ||||
| 				select { | ||||
| 				case w.Events <- event: | ||||
| 				case <-w.done: | ||||
| 					return | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			// Move to the next event in the buffer | ||||
| 			offset += unix.SizeofInotifyEvent + nameLen | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Certain types of events can be "ignored" and not sent over the Events | ||||
| // channel. Such as events marked ignore by the kernel, or MODIFY events | ||||
| // against files that do not exist. | ||||
| func (e *Event) ignoreLinux(mask uint32) bool { | ||||
| 	// Ignore anything the inotify API says to ignore | ||||
| 	if mask&unix.IN_IGNORED == unix.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.Op&Remove == Remove || e.Op&Rename == Rename) { | ||||
| 		_, statErr := os.Lstat(e.Name) | ||||
| 		return os.IsNotExist(statErr) | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| // newEvent returns an platform-independent Event based on an inotify mask. | ||||
| func newEvent(name string, mask uint32) Event { | ||||
| 	e := Event{Name: name} | ||||
| 	if mask&unix.IN_CREATE == unix.IN_CREATE || mask&unix.IN_MOVED_TO == unix.IN_MOVED_TO { | ||||
| 		e.Op |= Create | ||||
| 	} | ||||
| 	if mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF || mask&unix.IN_DELETE == unix.IN_DELETE { | ||||
| 		e.Op |= Remove | ||||
| 	} | ||||
| 	if mask&unix.IN_MODIFY == unix.IN_MODIFY { | ||||
| 		e.Op |= Write | ||||
| 	} | ||||
| 	if mask&unix.IN_MOVE_SELF == unix.IN_MOVE_SELF || mask&unix.IN_MOVED_FROM == unix.IN_MOVED_FROM { | ||||
| 		e.Op |= Rename | ||||
| 	} | ||||
| 	if mask&unix.IN_ATTRIB == unix.IN_ATTRIB { | ||||
| 		e.Op |= Chmod | ||||
| 	} | ||||
| 	return e | ||||
| } | ||||
							
								
								
									
										187
									
								
								vendor/github.com/fsnotify/fsnotify/inotify_poller.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								vendor/github.com/fsnotify/fsnotify/inotify_poller.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,187 @@ | ||||
| // Copyright 2015 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" | ||||
|  | ||||
| 	"golang.org/x/sys/unix" | ||||
| ) | ||||
|  | ||||
| type fdPoller struct { | ||||
| 	fd   int    // File descriptor (as returned by the inotify_init() syscall) | ||||
| 	epfd int    // Epoll file descriptor | ||||
| 	pipe [2]int // Pipe for waking up | ||||
| } | ||||
|  | ||||
| func emptyPoller(fd int) *fdPoller { | ||||
| 	poller := new(fdPoller) | ||||
| 	poller.fd = fd | ||||
| 	poller.epfd = -1 | ||||
| 	poller.pipe[0] = -1 | ||||
| 	poller.pipe[1] = -1 | ||||
| 	return poller | ||||
| } | ||||
|  | ||||
| // Create a new inotify poller. | ||||
| // This creates an inotify handler, and an epoll handler. | ||||
| func newFdPoller(fd int) (*fdPoller, error) { | ||||
| 	var errno error | ||||
| 	poller := emptyPoller(fd) | ||||
| 	defer func() { | ||||
| 		if errno != nil { | ||||
| 			poller.close() | ||||
| 		} | ||||
| 	}() | ||||
| 	poller.fd = fd | ||||
|  | ||||
| 	// Create epoll fd | ||||
| 	poller.epfd, errno = unix.EpollCreate1(unix.EPOLL_CLOEXEC) | ||||
| 	if poller.epfd == -1 { | ||||
| 		return nil, errno | ||||
| 	} | ||||
| 	// Create pipe; pipe[0] is the read end, pipe[1] the write end. | ||||
| 	errno = unix.Pipe2(poller.pipe[:], unix.O_NONBLOCK|unix.O_CLOEXEC) | ||||
| 	if errno != nil { | ||||
| 		return nil, errno | ||||
| 	} | ||||
|  | ||||
| 	// Register inotify fd with epoll | ||||
| 	event := unix.EpollEvent{ | ||||
| 		Fd:     int32(poller.fd), | ||||
| 		Events: unix.EPOLLIN, | ||||
| 	} | ||||
| 	errno = unix.EpollCtl(poller.epfd, unix.EPOLL_CTL_ADD, poller.fd, &event) | ||||
| 	if errno != nil { | ||||
| 		return nil, errno | ||||
| 	} | ||||
|  | ||||
| 	// Register pipe fd with epoll | ||||
| 	event = unix.EpollEvent{ | ||||
| 		Fd:     int32(poller.pipe[0]), | ||||
| 		Events: unix.EPOLLIN, | ||||
| 	} | ||||
| 	errno = unix.EpollCtl(poller.epfd, unix.EPOLL_CTL_ADD, poller.pipe[0], &event) | ||||
| 	if errno != nil { | ||||
| 		return nil, errno | ||||
| 	} | ||||
|  | ||||
| 	return poller, nil | ||||
| } | ||||
|  | ||||
| // Wait using epoll. | ||||
| // Returns true if something is ready to be read, | ||||
| // false if there is not. | ||||
| func (poller *fdPoller) wait() (bool, error) { | ||||
| 	// 3 possible events per fd, and 2 fds, makes a maximum of 6 events. | ||||
| 	// I don't know whether epoll_wait returns the number of events returned, | ||||
| 	// or the total number of events ready. | ||||
| 	// I decided to catch both by making the buffer one larger than the maximum. | ||||
| 	events := make([]unix.EpollEvent, 7) | ||||
| 	for { | ||||
| 		n, errno := unix.EpollWait(poller.epfd, events, -1) | ||||
| 		if n == -1 { | ||||
| 			if errno == unix.EINTR { | ||||
| 				continue | ||||
| 			} | ||||
| 			return false, errno | ||||
| 		} | ||||
| 		if n == 0 { | ||||
| 			// If there are no events, try again. | ||||
| 			continue | ||||
| 		} | ||||
| 		if n > 6 { | ||||
| 			// This should never happen. More events were returned than should be possible. | ||||
| 			return false, errors.New("epoll_wait returned more events than I know what to do with") | ||||
| 		} | ||||
| 		ready := events[:n] | ||||
| 		epollhup := false | ||||
| 		epollerr := false | ||||
| 		epollin := false | ||||
| 		for _, event := range ready { | ||||
| 			if event.Fd == int32(poller.fd) { | ||||
| 				if event.Events&unix.EPOLLHUP != 0 { | ||||
| 					// This should not happen, but if it does, treat it as a wakeup. | ||||
| 					epollhup = true | ||||
| 				} | ||||
| 				if event.Events&unix.EPOLLERR != 0 { | ||||
| 					// If an error is waiting on the file descriptor, we should pretend | ||||
| 					// something is ready to read, and let unix.Read pick up the error. | ||||
| 					epollerr = true | ||||
| 				} | ||||
| 				if event.Events&unix.EPOLLIN != 0 { | ||||
| 					// There is data to read. | ||||
| 					epollin = true | ||||
| 				} | ||||
| 			} | ||||
| 			if event.Fd == int32(poller.pipe[0]) { | ||||
| 				if event.Events&unix.EPOLLHUP != 0 { | ||||
| 					// Write pipe descriptor was closed, by us. This means we're closing down the | ||||
| 					// watcher, and we should wake up. | ||||
| 				} | ||||
| 				if event.Events&unix.EPOLLERR != 0 { | ||||
| 					// If an error is waiting on the pipe file descriptor. | ||||
| 					// This is an absolute mystery, and should never ever happen. | ||||
| 					return false, errors.New("Error on the pipe descriptor.") | ||||
| 				} | ||||
| 				if event.Events&unix.EPOLLIN != 0 { | ||||
| 					// This is a regular wakeup, so we have to clear the buffer. | ||||
| 					err := poller.clearWake() | ||||
| 					if err != nil { | ||||
| 						return false, err | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if epollhup || epollerr || epollin { | ||||
| 			return true, nil | ||||
| 		} | ||||
| 		return false, nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Close the write end of the poller. | ||||
| func (poller *fdPoller) wake() error { | ||||
| 	buf := make([]byte, 1) | ||||
| 	n, errno := unix.Write(poller.pipe[1], buf) | ||||
| 	if n == -1 { | ||||
| 		if errno == unix.EAGAIN { | ||||
| 			// Buffer is full, poller will wake. | ||||
| 			return nil | ||||
| 		} | ||||
| 		return errno | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (poller *fdPoller) clearWake() error { | ||||
| 	// You have to be woken up a LOT in order to get to 100! | ||||
| 	buf := make([]byte, 100) | ||||
| 	n, errno := unix.Read(poller.pipe[0], buf) | ||||
| 	if n == -1 { | ||||
| 		if errno == unix.EAGAIN { | ||||
| 			// Buffer is empty, someone else cleared our wake. | ||||
| 			return nil | ||||
| 		} | ||||
| 		return errno | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Close all poller file descriptors, but not the one passed to it. | ||||
| func (poller *fdPoller) close() { | ||||
| 	if poller.pipe[1] != -1 { | ||||
| 		unix.Close(poller.pipe[1]) | ||||
| 	} | ||||
| 	if poller.pipe[0] != -1 { | ||||
| 		unix.Close(poller.pipe[0]) | ||||
| 	} | ||||
| 	if poller.epfd != -1 { | ||||
| 		unix.Close(poller.epfd) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										521
									
								
								vendor/github.com/fsnotify/fsnotify/kqueue.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										521
									
								
								vendor/github.com/fsnotify/fsnotify/kqueue.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,521 @@ | ||||
| // 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" | ||||
| 	"time" | ||||
|  | ||||
| 	"golang.org/x/sys/unix" | ||||
| ) | ||||
|  | ||||
| // Watcher watches a set of files, delivering events to a channel. | ||||
| type Watcher struct { | ||||
| 	Events chan Event | ||||
| 	Errors chan error | ||||
| 	done   chan struct{} // Channel for sending a "quit message" to the reader goroutine | ||||
|  | ||||
| 	kq int // File descriptor (as returned by the kqueue() syscall). | ||||
|  | ||||
| 	mu              sync.Mutex        // Protects access to watcher data | ||||
| 	watches         map[string]int    // Map of watched file descriptors (key: path). | ||||
| 	externalWatches map[string]bool   // Map of watches added by user of the library. | ||||
| 	dirFlags        map[string]uint32 // Map of watched directories to fflags used in kqueue. | ||||
| 	paths           map[int]pathInfo  // Map file descriptors to path names for processing kqueue events. | ||||
| 	fileExists      map[string]bool   // Keep track of if we know this file exists (to stop duplicate create events). | ||||
| 	isClosed        bool              // Set to true when Close() is first called | ||||
| } | ||||
|  | ||||
| type pathInfo struct { | ||||
| 	name  string | ||||
| 	isDir bool | ||||
| } | ||||
|  | ||||
| // NewWatcher establishes a new watcher with the underlying OS and begins waiting for events. | ||||
| func NewWatcher() (*Watcher, error) { | ||||
| 	kq, err := kqueue() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	w := &Watcher{ | ||||
| 		kq:              kq, | ||||
| 		watches:         make(map[string]int), | ||||
| 		dirFlags:        make(map[string]uint32), | ||||
| 		paths:           make(map[int]pathInfo), | ||||
| 		fileExists:      make(map[string]bool), | ||||
| 		externalWatches: make(map[string]bool), | ||||
| 		Events:          make(chan Event), | ||||
| 		Errors:          make(chan error), | ||||
| 		done:            make(chan struct{}), | ||||
| 	} | ||||
|  | ||||
| 	go w.readEvents() | ||||
| 	return w, nil | ||||
| } | ||||
|  | ||||
| // Close removes all watches and closes the events channel. | ||||
| func (w *Watcher) Close() error { | ||||
| 	w.mu.Lock() | ||||
| 	if w.isClosed { | ||||
| 		w.mu.Unlock() | ||||
| 		return nil | ||||
| 	} | ||||
| 	w.isClosed = true | ||||
|  | ||||
| 	// copy paths to remove while locked | ||||
| 	var pathsToRemove = make([]string, 0, len(w.watches)) | ||||
| 	for name := range w.watches { | ||||
| 		pathsToRemove = append(pathsToRemove, name) | ||||
| 	} | ||||
| 	w.mu.Unlock() | ||||
| 	// unlock before calling Remove, which also locks | ||||
|  | ||||
| 	for _, name := range pathsToRemove { | ||||
| 		w.Remove(name) | ||||
| 	} | ||||
|  | ||||
| 	// send a "quit" message to the reader goroutine | ||||
| 	close(w.done) | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Add starts watching the named file or directory (non-recursively). | ||||
| func (w *Watcher) Add(name string) error { | ||||
| 	w.mu.Lock() | ||||
| 	w.externalWatches[name] = true | ||||
| 	w.mu.Unlock() | ||||
| 	_, err := w.addWatch(name, noteAllEvents) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // Remove stops watching the the named file or directory (non-recursively). | ||||
| func (w *Watcher) Remove(name string) error { | ||||
| 	name = filepath.Clean(name) | ||||
| 	w.mu.Lock() | ||||
| 	watchfd, ok := w.watches[name] | ||||
| 	w.mu.Unlock() | ||||
| 	if !ok { | ||||
| 		return fmt.Errorf("can't remove non-existent kevent watch for: %s", name) | ||||
| 	} | ||||
|  | ||||
| 	const registerRemove = unix.EV_DELETE | ||||
| 	if err := register(w.kq, []int{watchfd}, registerRemove, 0); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	unix.Close(watchfd) | ||||
|  | ||||
| 	w.mu.Lock() | ||||
| 	isDir := w.paths[watchfd].isDir | ||||
| 	delete(w.watches, name) | ||||
| 	delete(w.paths, watchfd) | ||||
| 	delete(w.dirFlags, name) | ||||
| 	w.mu.Unlock() | ||||
|  | ||||
| 	// Find all watched paths that are in this directory that are not external. | ||||
| 	if isDir { | ||||
| 		var pathsToRemove []string | ||||
| 		w.mu.Lock() | ||||
| 		for _, path := range w.paths { | ||||
| 			wdir, _ := filepath.Split(path.name) | ||||
| 			if filepath.Clean(wdir) == name { | ||||
| 				if !w.externalWatches[path.name] { | ||||
| 					pathsToRemove = append(pathsToRemove, path.name) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		w.mu.Unlock() | ||||
| 		for _, name := 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.Remove(name) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Watch all events (except NOTE_EXTEND, NOTE_LINK, NOTE_REVOKE) | ||||
| const noteAllEvents = unix.NOTE_DELETE | unix.NOTE_WRITE | unix.NOTE_ATTRIB | unix.NOTE_RENAME | ||||
|  | ||||
| // keventWaitTime to block on each read from kevent | ||||
| var keventWaitTime = durationToTimespec(100 * time.Millisecond) | ||||
|  | ||||
| // addWatch adds name to the watched file set. | ||||
| // The flags are interpreted as described in kevent(2). | ||||
| // Returns the real path to the file which was added, if any, which may be different from the one passed in the case of symlinks. | ||||
| func (w *Watcher) addWatch(name string, flags uint32) (string, error) { | ||||
| 	var isDir bool | ||||
| 	// Make ./name and name equivalent | ||||
| 	name = filepath.Clean(name) | ||||
|  | ||||
| 	w.mu.Lock() | ||||
| 	if w.isClosed { | ||||
| 		w.mu.Unlock() | ||||
| 		return "", errors.New("kevent instance already closed") | ||||
| 	} | ||||
| 	watchfd, alreadyWatching := w.watches[name] | ||||
| 	// We already have a watch, but we can still override flags. | ||||
| 	if alreadyWatching { | ||||
| 		isDir = w.paths[watchfd].isDir | ||||
| 	} | ||||
| 	w.mu.Unlock() | ||||
|  | ||||
| 	if !alreadyWatching { | ||||
| 		fi, err := os.Lstat(name) | ||||
| 		if err != nil { | ||||
| 			return "", err | ||||
| 		} | ||||
|  | ||||
| 		// Don't watch sockets. | ||||
| 		if fi.Mode()&os.ModeSocket == os.ModeSocket { | ||||
| 			return "", nil | ||||
| 		} | ||||
|  | ||||
| 		// Don't watch named pipes. | ||||
| 		if fi.Mode()&os.ModeNamedPipe == os.ModeNamedPipe { | ||||
| 			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 { | ||||
| 			name, err = filepath.EvalSymlinks(name) | ||||
| 			if err != nil { | ||||
| 				return "", nil | ||||
| 			} | ||||
|  | ||||
| 			w.mu.Lock() | ||||
| 			_, alreadyWatching = w.watches[name] | ||||
| 			w.mu.Unlock() | ||||
|  | ||||
| 			if alreadyWatching { | ||||
| 				return name, nil | ||||
| 			} | ||||
|  | ||||
| 			fi, err = os.Lstat(name) | ||||
| 			if err != nil { | ||||
| 				return "", nil | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		watchfd, err = unix.Open(name, openMode, 0700) | ||||
| 		if watchfd == -1 { | ||||
| 			return "", err | ||||
| 		} | ||||
|  | ||||
| 		isDir = fi.IsDir() | ||||
| 	} | ||||
|  | ||||
| 	const registerAdd = unix.EV_ADD | unix.EV_CLEAR | unix.EV_ENABLE | ||||
| 	if err := register(w.kq, []int{watchfd}, registerAdd, flags); err != nil { | ||||
| 		unix.Close(watchfd) | ||||
| 		return "", err | ||||
| 	} | ||||
|  | ||||
| 	if !alreadyWatching { | ||||
| 		w.mu.Lock() | ||||
| 		w.watches[name] = watchfd | ||||
| 		w.paths[watchfd] = pathInfo{name: name, isDir: isDir} | ||||
| 		w.mu.Unlock() | ||||
| 	} | ||||
|  | ||||
| 	if isDir { | ||||
| 		// Watch the directory if it has not been watched before, | ||||
| 		// or if it was watched before, but perhaps only a NOTE_DELETE (watchDirectoryFiles) | ||||
| 		w.mu.Lock() | ||||
|  | ||||
| 		watchDir := (flags&unix.NOTE_WRITE) == unix.NOTE_WRITE && | ||||
| 			(!alreadyWatching || (w.dirFlags[name]&unix.NOTE_WRITE) != unix.NOTE_WRITE) | ||||
| 		// Store flags so this watch can be updated later | ||||
| 		w.dirFlags[name] = flags | ||||
| 		w.mu.Unlock() | ||||
|  | ||||
| 		if watchDir { | ||||
| 			if err := w.watchDirectoryFiles(name); err != nil { | ||||
| 				return "", err | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return name, nil | ||||
| } | ||||
|  | ||||
| // readEvents reads from kqueue and converts the received kevents into | ||||
| // Event values that it sends down the Events channel. | ||||
| func (w *Watcher) readEvents() { | ||||
| 	eventBuffer := make([]unix.Kevent_t, 10) | ||||
|  | ||||
| loop: | ||||
| 	for { | ||||
| 		// See if there is a message on the "done" channel | ||||
| 		select { | ||||
| 		case <-w.done: | ||||
| 			break loop | ||||
| 		default: | ||||
| 		} | ||||
|  | ||||
| 		// Get new events | ||||
| 		kevents, err := read(w.kq, eventBuffer, &keventWaitTime) | ||||
| 		// EINTR is okay, the syscall was interrupted before timeout expired. | ||||
| 		if err != nil && err != unix.EINTR { | ||||
| 			select { | ||||
| 			case w.Errors <- err: | ||||
| 			case <-w.done: | ||||
| 				break loop | ||||
| 			} | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		// Flush the events we received to the Events channel | ||||
| 		for len(kevents) > 0 { | ||||
| 			kevent := &kevents[0] | ||||
| 			watchfd := int(kevent.Ident) | ||||
| 			mask := uint32(kevent.Fflags) | ||||
| 			w.mu.Lock() | ||||
| 			path := w.paths[watchfd] | ||||
| 			w.mu.Unlock() | ||||
| 			event := newEvent(path.name, mask) | ||||
|  | ||||
| 			if path.isDir && !(event.Op&Remove == Remove) { | ||||
| 				// Double check to make sure the directory exists. 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(event.Name); os.IsNotExist(err) { | ||||
| 					// mark is as delete event | ||||
| 					event.Op |= Remove | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			if event.Op&Rename == Rename || event.Op&Remove == Remove { | ||||
| 				w.Remove(event.Name) | ||||
| 				w.mu.Lock() | ||||
| 				delete(w.fileExists, event.Name) | ||||
| 				w.mu.Unlock() | ||||
| 			} | ||||
|  | ||||
| 			if path.isDir && event.Op&Write == Write && !(event.Op&Remove == Remove) { | ||||
| 				w.sendDirectoryChangeEvents(event.Name) | ||||
| 			} else { | ||||
| 				// Send the event on the Events channel. | ||||
| 				select { | ||||
| 				case w.Events <- event: | ||||
| 				case <-w.done: | ||||
| 					break loop | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			if event.Op&Remove == Remove { | ||||
| 				// Look for a file that may have overwritten this. | ||||
| 				// For example, mv f1 f2 will delete f2, then create f2. | ||||
| 				if path.isDir { | ||||
| 					fileDir := filepath.Clean(event.Name) | ||||
| 					w.mu.Lock() | ||||
| 					_, found := w.watches[fileDir] | ||||
| 					w.mu.Unlock() | ||||
| 					if found { | ||||
| 						// make sure the directory exists 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 from the parent directory. | ||||
| 						if _, err := os.Lstat(fileDir); err == nil { | ||||
| 							w.sendDirectoryChangeEvents(fileDir) | ||||
| 						} | ||||
| 					} | ||||
| 				} else { | ||||
| 					filePath := filepath.Clean(event.Name) | ||||
| 					if fileInfo, err := os.Lstat(filePath); err == nil { | ||||
| 						w.sendFileCreatedEventIfNew(filePath, fileInfo) | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			// Move to next event | ||||
| 			kevents = kevents[1:] | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// cleanup | ||||
| 	err := unix.Close(w.kq) | ||||
| 	if err != nil { | ||||
| 		// only way the previous loop breaks is if w.done was closed so we need to async send to w.Errors. | ||||
| 		select { | ||||
| 		case w.Errors <- err: | ||||
| 		default: | ||||
| 		} | ||||
| 	} | ||||
| 	close(w.Events) | ||||
| 	close(w.Errors) | ||||
| } | ||||
|  | ||||
| // newEvent returns an platform-independent Event based on kqueue Fflags. | ||||
| func newEvent(name string, mask uint32) Event { | ||||
| 	e := Event{Name: name} | ||||
| 	if mask&unix.NOTE_DELETE == unix.NOTE_DELETE { | ||||
| 		e.Op |= Remove | ||||
| 	} | ||||
| 	if mask&unix.NOTE_WRITE == unix.NOTE_WRITE { | ||||
| 		e.Op |= Write | ||||
| 	} | ||||
| 	if mask&unix.NOTE_RENAME == unix.NOTE_RENAME { | ||||
| 		e.Op |= Rename | ||||
| 	} | ||||
| 	if mask&unix.NOTE_ATTRIB == unix.NOTE_ATTRIB { | ||||
| 		e.Op |= Chmod | ||||
| 	} | ||||
| 	return e | ||||
| } | ||||
|  | ||||
| func newCreateEvent(name string) Event { | ||||
| 	return Event{Name: name, Op: Create} | ||||
| } | ||||
|  | ||||
| // watchDirectoryFiles to mimic inotify when adding a watch on a directory | ||||
| func (w *Watcher) watchDirectoryFiles(dirPath string) error { | ||||
| 	// Get all files | ||||
| 	files, err := ioutil.ReadDir(dirPath) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	for _, fileInfo := range files { | ||||
| 		filePath := filepath.Join(dirPath, fileInfo.Name()) | ||||
| 		filePath, err = w.internalWatch(filePath, fileInfo) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		w.mu.Lock() | ||||
| 		w.fileExists[filePath] = true | ||||
| 		w.mu.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 inotify 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 { | ||||
| 		select { | ||||
| 		case w.Errors <- err: | ||||
| 		case <-w.done: | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Search for new files | ||||
| 	for _, fileInfo := range files { | ||||
| 		filePath := filepath.Join(dirPath, fileInfo.Name()) | ||||
| 		err := w.sendFileCreatedEventIfNew(filePath, fileInfo) | ||||
|  | ||||
| 		if err != nil { | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // sendFileCreatedEvent sends a create event if the file isn't already being tracked. | ||||
| func (w *Watcher) sendFileCreatedEventIfNew(filePath string, fileInfo os.FileInfo) (err error) { | ||||
| 	w.mu.Lock() | ||||
| 	_, doesExist := w.fileExists[filePath] | ||||
| 	w.mu.Unlock() | ||||
| 	if !doesExist { | ||||
| 		// Send create event | ||||
| 		select { | ||||
| 		case w.Events <- newCreateEvent(filePath): | ||||
| 		case <-w.done: | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// like watchDirectoryFiles (but without doing another ReadDir) | ||||
| 	filePath, err = w.internalWatch(filePath, fileInfo) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	w.mu.Lock() | ||||
| 	w.fileExists[filePath] = true | ||||
| 	w.mu.Unlock() | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (w *Watcher) internalWatch(name string, fileInfo os.FileInfo) (string, error) { | ||||
| 	if fileInfo.IsDir() { | ||||
| 		// mimic Linux providing delete events for subdirectories | ||||
| 		// but preserve the flags used if currently watching subdirectory | ||||
| 		w.mu.Lock() | ||||
| 		flags := w.dirFlags[name] | ||||
| 		w.mu.Unlock() | ||||
|  | ||||
| 		flags |= unix.NOTE_DELETE | unix.NOTE_RENAME | ||||
| 		return w.addWatch(name, flags) | ||||
| 	} | ||||
|  | ||||
| 	// watch file to mimic Linux inotify | ||||
| 	return w.addWatch(name, noteAllEvents) | ||||
| } | ||||
|  | ||||
| // kqueue creates a new kernel event queue and returns a descriptor. | ||||
| func kqueue() (kq int, err error) { | ||||
| 	kq, err = unix.Kqueue() | ||||
| 	if kq == -1 { | ||||
| 		return kq, err | ||||
| 	} | ||||
| 	return kq, nil | ||||
| } | ||||
|  | ||||
| // register events with the queue | ||||
| func register(kq int, fds []int, flags int, fflags uint32) error { | ||||
| 	changes := make([]unix.Kevent_t, len(fds)) | ||||
|  | ||||
| 	for i, fd := range fds { | ||||
| 		// SetKevent converts int to the platform-specific types: | ||||
| 		unix.SetKevent(&changes[i], fd, unix.EVFILT_VNODE, flags) | ||||
| 		changes[i].Fflags = fflags | ||||
| 	} | ||||
|  | ||||
| 	// register the events | ||||
| 	success, err := unix.Kevent(kq, changes, nil, nil) | ||||
| 	if success == -1 { | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // read retrieves pending events, or waits until an event occurs. | ||||
| // A timeout of nil blocks indefinitely, while 0 polls the queue. | ||||
| func read(kq int, events []unix.Kevent_t, timeout *unix.Timespec) ([]unix.Kevent_t, error) { | ||||
| 	n, err := unix.Kevent(kq, nil, events, timeout) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return events[0:n], nil | ||||
| } | ||||
|  | ||||
| // durationToTimespec prepares a timeout value | ||||
| func durationToTimespec(d time.Duration) unix.Timespec { | ||||
| 	return unix.NsecToTimespec(d.Nanoseconds()) | ||||
| } | ||||
							
								
								
									
										11
									
								
								vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| // 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 "golang.org/x/sys/unix" | ||||
|  | ||||
| const openMode = unix.O_NONBLOCK | unix.O_RDONLY | unix.O_CLOEXEC | ||||
							
								
								
									
										12
									
								
								vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| // 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 "golang.org/x/sys/unix" | ||||
|  | ||||
| // note: this constant is not defined on BSD | ||||
| const openMode = unix.O_EVTONLY | unix.O_CLOEXEC | ||||
							
								
								
									
										561
									
								
								vendor/github.com/fsnotify/fsnotify/windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										561
									
								
								vendor/github.com/fsnotify/fsnotify/windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,561 @@ | ||||
| // 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" | ||||
| ) | ||||
|  | ||||
| // Watcher watches a set of files, delivering events to a channel. | ||||
| type Watcher struct { | ||||
| 	Events   chan Event | ||||
| 	Errors   chan error | ||||
| 	isClosed bool           // Set to true when Close() is first called | ||||
| 	mu       sync.Mutex     // Map access | ||||
| 	port     syscall.Handle // Handle to completion port | ||||
| 	watches  watchMap       // Map of watches (key: i-number) | ||||
| 	input    chan *input    // Inputs to the reader are sent on this channel | ||||
| 	quit     chan chan<- error | ||||
| } | ||||
|  | ||||
| // NewWatcher establishes a new watcher with the underlying OS and begins waiting for events. | ||||
| 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), | ||||
| 		input:   make(chan *input, 1), | ||||
| 		Events:  make(chan Event, 50), | ||||
| 		Errors:  make(chan error), | ||||
| 		quit:    make(chan chan<- error, 1), | ||||
| 	} | ||||
| 	go w.readEvents() | ||||
| 	return w, nil | ||||
| } | ||||
|  | ||||
| // Close removes all watches and closes the events channel. | ||||
| 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 | ||||
| } | ||||
|  | ||||
| // Add starts watching the named file or directory (non-recursively). | ||||
| func (w *Watcher) Add(name string) error { | ||||
| 	if w.isClosed { | ||||
| 		return errors.New("watcher already closed") | ||||
| 	} | ||||
| 	in := &input{ | ||||
| 		op:    opAddWatch, | ||||
| 		path:  filepath.Clean(name), | ||||
| 		flags: sysFSALLEVENTS, | ||||
| 		reply: make(chan error), | ||||
| 	} | ||||
| 	w.input <- in | ||||
| 	if err := w.wakeupReader(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return <-in.reply | ||||
| } | ||||
|  | ||||
| // Remove stops watching the the named file or directory (non-recursively). | ||||
| func (w *Watcher) Remove(name string) error { | ||||
| 	in := &input{ | ||||
| 		op:    opRemoveWatch, | ||||
| 		path:  filepath.Clean(name), | ||||
| 		reply: make(chan error), | ||||
| 	} | ||||
| 	w.input <- in | ||||
| 	if err := w.wakeupReader(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return <-in.reply | ||||
| } | ||||
|  | ||||
| const ( | ||||
| 	// Options for AddWatch | ||||
| 	sysFSONESHOT = 0x80000000 | ||||
| 	sysFSONLYDIR = 0x1000000 | ||||
|  | ||||
| 	// Events | ||||
| 	sysFSACCESS     = 0x1 | ||||
| 	sysFSALLEVENTS  = 0xfff | ||||
| 	sysFSATTRIB     = 0x4 | ||||
| 	sysFSCLOSE      = 0x18 | ||||
| 	sysFSCREATE     = 0x100 | ||||
| 	sysFSDELETE     = 0x200 | ||||
| 	sysFSDELETESELF = 0x400 | ||||
| 	sysFSMODIFY     = 0x2 | ||||
| 	sysFSMOVE       = 0xc0 | ||||
| 	sysFSMOVEDFROM  = 0x40 | ||||
| 	sysFSMOVEDTO    = 0x80 | ||||
| 	sysFSMOVESELF   = 0x800 | ||||
|  | ||||
| 	// Special events | ||||
| 	sysFSIGNORED   = 0x8000 | ||||
| 	sysFSQOVERFLOW = 0x4000 | ||||
| ) | ||||
|  | ||||
| func newEvent(name string, mask uint32) Event { | ||||
| 	e := Event{Name: name} | ||||
| 	if mask&sysFSCREATE == sysFSCREATE || mask&sysFSMOVEDTO == sysFSMOVEDTO { | ||||
| 		e.Op |= Create | ||||
| 	} | ||||
| 	if mask&sysFSDELETE == sysFSDELETE || mask&sysFSDELETESELF == sysFSDELETESELF { | ||||
| 		e.Op |= Remove | ||||
| 	} | ||||
| 	if mask&sysFSMODIFY == sysFSMODIFY { | ||||
| 		e.Op |= Write | ||||
| 	} | ||||
| 	if mask&sysFSMOVE == sysFSMOVE || mask&sysFSMOVESELF == sysFSMOVESELF || mask&sysFSMOVEDFROM == sysFSMOVEDFROM { | ||||
| 		e.Op |= Rename | ||||
| 	} | ||||
| 	if mask&sysFSATTRIB == sysFSATTRIB { | ||||
| 		e.Op |= Chmod | ||||
| 	} | ||||
| 	return e | ||||
| } | ||||
|  | ||||
| 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 | ||||
|  | ||||
| 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&sysFSONLYDIR != 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&sysFSIGNORED) | ||||
| 		watch.mask = 0 | ||||
| 	} else { | ||||
| 		name := filepath.Base(pathname) | ||||
| 		w.sendEvent(filepath.Join(watch.path, name), watch.names[name]&sysFSIGNORED) | ||||
| 		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(filepath.Join(watch.path, name), mask&sysFSIGNORED) | ||||
| 		} | ||||
| 		delete(watch.names, name) | ||||
| 	} | ||||
| 	if watch.mask != 0 { | ||||
| 		if watch.mask&provisional == 0 { | ||||
| 			w.sendEvent(watch.path, watch.mask&sysFSIGNORED) | ||||
| 		} | ||||
| 		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.Errors <- 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.Errors <- 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&sysFSDELETESELF) { | ||||
| 				if watch.mask&sysFSONESHOT != 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 Events 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.Events) | ||||
| 				close(w.Errors) | ||||
| 				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 syscall.ERROR_MORE_DATA: | ||||
| 			if watch == nil { | ||||
| 				w.Errors <- 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&sysFSDELETESELF) | ||||
| 			w.deleteWatch(watch) | ||||
| 			w.startRead(watch) | ||||
| 			continue | ||||
| 		case syscall.ERROR_OPERATION_ABORTED: | ||||
| 			// CancelIo was called on this handle | ||||
| 			continue | ||||
| 		default: | ||||
| 			w.Errors <- os.NewSyscallError("GetQueuedCompletionPort", e) | ||||
| 			continue | ||||
| 		case nil: | ||||
| 		} | ||||
|  | ||||
| 		var offset uint32 | ||||
| 		for { | ||||
| 			if n == 0 { | ||||
| 				w.Events <- newEvent("", sysFSQOVERFLOW) | ||||
| 				w.Errors <- 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 := filepath.Join(watch.path, name) | ||||
|  | ||||
| 			var mask uint64 | ||||
| 			switch raw.Action { | ||||
| 			case syscall.FILE_ACTION_REMOVED: | ||||
| 				mask = sysFSDELETESELF | ||||
| 			case syscall.FILE_ACTION_MODIFIED: | ||||
| 				mask = sysFSMODIFY | ||||
| 			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 = sysFSMOVESELF | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			sendNameEvent := func() { | ||||
| 				if w.sendEvent(fullname, watch.names[name]&mask) { | ||||
| 					if watch.names[name]&sysFSONESHOT != 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]&sysFSIGNORED) | ||||
| 				delete(watch.names, name) | ||||
| 			} | ||||
| 			if w.sendEvent(fullname, watch.mask&toFSnotifyFlags(raw.Action)) { | ||||
| 				if watch.mask&sysFSONESHOT != 0 { | ||||
| 					watch.mask = 0 | ||||
| 				} | ||||
| 			} | ||||
| 			if raw.Action == syscall.FILE_ACTION_RENAMED_NEW_NAME { | ||||
| 				fullname = filepath.Join(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.Errors <- errors.New("Windows system assumed buffer larger than it is, events have likely been missed.") | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if err := w.startRead(watch); err != nil { | ||||
| 			w.Errors <- err | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (w *Watcher) sendEvent(name string, mask uint64) bool { | ||||
| 	if mask == 0 { | ||||
| 		return false | ||||
| 	} | ||||
| 	event := newEvent(name, uint32(mask)) | ||||
| 	select { | ||||
| 	case ch := <-w.quit: | ||||
| 		w.quit <- ch | ||||
| 	case w.Events <- event: | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func toWindowsFlags(mask uint64) uint32 { | ||||
| 	var m uint32 | ||||
| 	if mask&sysFSACCESS != 0 { | ||||
| 		m |= syscall.FILE_NOTIFY_CHANGE_LAST_ACCESS | ||||
| 	} | ||||
| 	if mask&sysFSMODIFY != 0 { | ||||
| 		m |= syscall.FILE_NOTIFY_CHANGE_LAST_WRITE | ||||
| 	} | ||||
| 	if mask&sysFSATTRIB != 0 { | ||||
| 		m |= syscall.FILE_NOTIFY_CHANGE_ATTRIBUTES | ||||
| 	} | ||||
| 	if mask&(sysFSMOVE|sysFSCREATE|sysFSDELETE) != 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 sysFSCREATE | ||||
| 	case syscall.FILE_ACTION_REMOVED: | ||||
| 		return sysFSDELETE | ||||
| 	case syscall.FILE_ACTION_MODIFIED: | ||||
| 		return sysFSMODIFY | ||||
| 	case syscall.FILE_ACTION_RENAMED_OLD_NAME: | ||||
| 		return sysFSMOVEDFROM | ||||
| 	case syscall.FILE_ACTION_RENAMED_NEW_NAME: | ||||
| 		return sysFSMOVEDTO | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Mike Brown
					Mike Brown